Hugoifier
test: replace all stub tests with real unit tests (87 tests, 0 failures) Closes #9
Commit
6272677ca26ac7f987bae388c97d39a0b304730bd5acfc1272be5fc1d6afe5c5
Parent
75aa4ac47cd6fba…
13 files changed
+4
+59
-18
+18
-19
+113
-18
+22
+188
-16
+15
-16
+38
-41
+69
-18
+18
-19
+37
+60
+41
-16
+
tests/conftest.py
~
tests/test_analyze.py
~
tests/test_cloudflare.py
~
tests/test_complete.py
+
tests/test_config.py
~
tests/test_decapify.py
~
tests/test_deploy.py
~
tests/test_generate_decap_config.py
~
tests/test_hugoify.py
~
tests/test_parser.py
+
tests/test_theme_finder.py
+
tests/test_theme_patcher.py
~
tests/test_translate.py
+4
| --- a/tests/conftest.py | ||
| +++ b/tests/conftest.py | ||
| @@ -0,0 +1,4 @@ | ||
| 1 | +"""Add src/ to sys.path so tests can import from utils.* direcs | |
| 2 | + | |
| 3 | +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) | |
| 4 | +, 'src' |
| --- a/tests/conftest.py | |
| +++ b/tests/conftest.py | |
| @@ -0,0 +1,4 @@ | |
| --- a/tests/conftest.py | |
| +++ b/tests/conftest.py | |
| @@ -0,0 +1,4 @@ | |
| 1 | """Add src/ to sys.path so tests can import from utils.* direcs |
| 2 | |
| 3 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) |
| 4 | , 'src' |
+59
-18
| --- tests/test_analyze.py | ||
| +++ tests/test_analyze.py | ||
| @@ -1,19 +1,60 @@ | ||
| 1 | +"""Tests for utils.analyze.""" | |
| 2 | +import os | |
| 3 | +import tempfile | |
| 1 | 4 | import unittest |
| 2 | -from src.utils.analyze import analyze | |
| 3 | - | |
| 4 | -class TestAnalyze(unittest.TestCase): | |
| 5 | - | |
| 6 | - def setUp(self): | |
| 7 | - # Set up any necessary test data | |
| 8 | - pass | |
| 9 | - | |
| 10 | - def tearDown(self): | |
| 11 | - # Clean up after tests | |
| 12 | - pass | |
| 13 | - | |
| 14 | - def test_analyze(self): | |
| 15 | - # Placeholder for analyze function test | |
| 16 | - pass | |
| 17 | - | |
| 18 | -if __name__ == '__main__': | |
| 19 | - unittest.main() | |
| 5 | + | |
| 6 | +from utils.analyze import _analyze_hugo_theme | |
| 7 | + | |
| 8 | + | |
| 9 | +class TestAnalyzeHugoTheme(unittest.TestCase): | |
| 10 | + def _make_theme_info(self, tmp, layouts=None, example_site=None): | |
| 11 | + theme_dir = os.path.join(tmp, "test-theme") | |
| 12 | + layouts_dir = os.path.join(theme_dir, "layouts", "_default") | |
| 13 | + os.makedirs(layouts_dir) | |
| 14 | + for name in (layouts or ["baseof.html", "single.html"]): | |
| 15 | + open(os.path.join(layouts_dir, name), "w").close() | |
| 16 | + return { | |
| 17 | + "theme_dir": theme_dir, | |
| 18 | + "theme_name": "test-theme", | |
| 19 | + "example_site": example_site, | |
| 20 | + "is_hugo_theme": True, | |
| 21 | + } | |
| 22 | + | |
| 23 | + def test_reports_theme_name(self): | |
| 24 | + with tempfile.TemporaryDirectory() as tmp: | |
| 25 | + info = self._make_theme_info(tmp) | |
| 26 | + result = _analyze_hugo_theme(info) | |
| 27 | + self.assertIn("test-theme", result) | |
| 28 | + | |
| 29 | + def test_lists_layout_files(self): | |
| 30 | + with tempfile.TemporaryDirectory() as tmp: | |
| 31 | + info = self._make_theme_info(tmp, layouts=["baseof.html", "single.html"]) | |
| 32 | + result = _analyze_hugo_theme(info) | |
| 33 | + self.assertIn("baseof.html", result) | |
| 34 | + self.assertIn("single.html", result) | |
| 35 | + | |
| 36 | + def test_reports_no_example_site(self): | |
| 37 | + with tempfile.TemporaryDirectory() as tmp: | |
| 38 | + info = self._make_theme_info(tmp, example_site=None) | |
| 39 | + result = _analyze_hugo_theme(info) | |
| 40 | + self.assertIn("none", result.lower()) | |
| 41 | + | |
| 42 | + def test_reports_content_types_from_example_site(self): | |
| 43 | + with tempfile.TemporaryDirectory() as tmp: | |
| 44 | + info = self._make_theme_info(tmp) | |
| 45 | + example = os.path.join(tmp, "exampleSite") | |
| 46 | + content = os.path.join(example, "content", "blog") | |
| 47 | + os.makedirs(content) | |
| 48 | + info["example_site"] = example | |
| 49 | + result = _analyze_hugo_theme(info) | |
| 50 | + self.assertIn("blog", result) | |
| 51 | + | |
| 52 | + def test_suggests_complete_command(self): | |
| 53 | + with tempfile.TemporaryDirectory() as tmp: | |
| 54 | + info = self._make_theme_info(tmp) | |
| 55 | + result = _analyze_hugo_theme(info) | |
| 56 | + self.assertIn("complete", result) | |
| 57 | + | |
| 58 | + | |
| 59 | +if __name__ == "__main__": | |
| 60 | + unittest.main() | |
| 20 | 61 |
| --- tests/test_analyze.py | |
| +++ tests/test_analyze.py | |
| @@ -1,19 +1,60 @@ | |
| 1 | import unittest |
| 2 | from src.utils.analyze import analyze |
| 3 | |
| 4 | class TestAnalyze(unittest.TestCase): |
| 5 | |
| 6 | def setUp(self): |
| 7 | # Set up any necessary test data |
| 8 | pass |
| 9 | |
| 10 | def tearDown(self): |
| 11 | # Clean up after tests |
| 12 | pass |
| 13 | |
| 14 | def test_analyze(self): |
| 15 | # Placeholder for analyze function test |
| 16 | pass |
| 17 | |
| 18 | if __name__ == '__main__': |
| 19 | unittest.main() |
| 20 |
| --- tests/test_analyze.py | |
| +++ tests/test_analyze.py | |
| @@ -1,19 +1,60 @@ | |
| 1 | """Tests for utils.analyze.""" |
| 2 | import os |
| 3 | import tempfile |
| 4 | import unittest |
| 5 | |
| 6 | from utils.analyze import _analyze_hugo_theme |
| 7 | |
| 8 | |
| 9 | class TestAnalyzeHugoTheme(unittest.TestCase): |
| 10 | def _make_theme_info(self, tmp, layouts=None, example_site=None): |
| 11 | theme_dir = os.path.join(tmp, "test-theme") |
| 12 | layouts_dir = os.path.join(theme_dir, "layouts", "_default") |
| 13 | os.makedirs(layouts_dir) |
| 14 | for name in (layouts or ["baseof.html", "single.html"]): |
| 15 | open(os.path.join(layouts_dir, name), "w").close() |
| 16 | return { |
| 17 | "theme_dir": theme_dir, |
| 18 | "theme_name": "test-theme", |
| 19 | "example_site": example_site, |
| 20 | "is_hugo_theme": True, |
| 21 | } |
| 22 | |
| 23 | def test_reports_theme_name(self): |
| 24 | with tempfile.TemporaryDirectory() as tmp: |
| 25 | info = self._make_theme_info(tmp) |
| 26 | result = _analyze_hugo_theme(info) |
| 27 | self.assertIn("test-theme", result) |
| 28 | |
| 29 | def test_lists_layout_files(self): |
| 30 | with tempfile.TemporaryDirectory() as tmp: |
| 31 | info = self._make_theme_info(tmp, layouts=["baseof.html", "single.html"]) |
| 32 | result = _analyze_hugo_theme(info) |
| 33 | self.assertIn("baseof.html", result) |
| 34 | self.assertIn("single.html", result) |
| 35 | |
| 36 | def test_reports_no_example_site(self): |
| 37 | with tempfile.TemporaryDirectory() as tmp: |
| 38 | info = self._make_theme_info(tmp, example_site=None) |
| 39 | result = _analyze_hugo_theme(info) |
| 40 | self.assertIn("none", result.lower()) |
| 41 | |
| 42 | def test_reports_content_types_from_example_site(self): |
| 43 | with tempfile.TemporaryDirectory() as tmp: |
| 44 | info = self._make_theme_info(tmp) |
| 45 | example = os.path.join(tmp, "exampleSite") |
| 46 | content = os.path.join(example, "content", "blog") |
| 47 | os.makedirs(content) |
| 48 | info["example_site"] = example |
| 49 | result = _analyze_hugo_theme(info) |
| 50 | self.assertIn("blog", result) |
| 51 | |
| 52 | def test_suggests_complete_command(self): |
| 53 | with tempfile.TemporaryDirectory() as tmp: |
| 54 | info = self._make_theme_info(tmp) |
| 55 | result = _analyze_hugo_theme(info) |
| 56 | self.assertIn("complete", result) |
| 57 | |
| 58 | |
| 59 | if __name__ == "__main__": |
| 60 | unittest.main() |
| 61 |
+18
-19
| --- tests/test_cloudflare.py | ||
| +++ tests/test_cloudflare.py | ||
| @@ -1,19 +1,18 @@ | ||
| 1 | -import unittest | |
| 2 | -from src.utils.cloudflare import configure_cloudflare | |
| 3 | - | |
| 4 | -class TestCloudflare(unittest.TestCase): | |
| 5 | - | |
| 6 | - def setUp(self): | |
| 7 | - # Set up any necessary test data | |
| 8 | - pass | |
| 9 | - | |
| 10 | - def tearDown(self): | |
| 11 | - # Clean up after tests | |
| 12 | - pass | |
| 13 | - | |
| 14 | - def test_configure_cloudflare(self): | |
| 15 | - # Placeholder for configure_cloudflare function test | |
| 16 | - pass | |
| 17 | - | |
| 18 | -if __name__ == '__main__': | |
| 19 | - unittest.main() | |
| 1 | +"""Tests for utils.cloudflare.""" | |
| 2 | +import unittest | |
| 3 | + | |
| 4 | +from utils.cloudflare import configure_cloudflare | |
| 5 | + | |
| 6 | + | |
| 7 | +class TestConfigureCloudflare(unittest.TestCase): | |
| 8 | + def test_returns_complete_message(self): | |
| 9 | + result = configure_cloudflare("/some/path", "example.com") | |
| 10 | + self.assertIn("complete", result.lower()) | |
| 11 | + | |
| 12 | + def test_accepts_path_and_zone(self): | |
| 13 | + result = configure_cloudflare("/tmp/site", "zone-id-123") | |
| 14 | + self.assertIsInstance(result, str) | |
| 15 | + | |
| 16 | + | |
| 17 | +if __name__ == "__main__": | |
| 18 | + unittest.main() | |
| 20 | 19 |
| --- tests/test_cloudflare.py | |
| +++ tests/test_cloudflare.py | |
| @@ -1,19 +1,18 @@ | |
| 1 | import unittest |
| 2 | from src.utils.cloudflare import configure_cloudflare |
| 3 | |
| 4 | class TestCloudflare(unittest.TestCase): |
| 5 | |
| 6 | def setUp(self): |
| 7 | # Set up any necessary test data |
| 8 | pass |
| 9 | |
| 10 | def tearDown(self): |
| 11 | # Clean up after tests |
| 12 | pass |
| 13 | |
| 14 | def test_configure_cloudflare(self): |
| 15 | # Placeholder for configure_cloudflare function test |
| 16 | pass |
| 17 | |
| 18 | if __name__ == '__main__': |
| 19 | unittest.main() |
| 20 |
| --- tests/test_cloudflare.py | |
| +++ tests/test_cloudflare.py | |
| @@ -1,19 +1,18 @@ | |
| 1 | """Tests for utils.cloudflare.""" |
| 2 | import unittest |
| 3 | |
| 4 | from utils.cloudflare import configure_cloudflare |
| 5 | |
| 6 | |
| 7 | class TestConfigureCloudflare(unittest.TestCase): |
| 8 | def test_returns_complete_message(self): |
| 9 | result = configure_cloudflare("/some/path", "example.com") |
| 10 | self.assertIn("complete", result.lower()) |
| 11 | |
| 12 | def test_accepts_path_and_zone(self): |
| 13 | result = configure_cloudflare("/tmp/site", "zone-id-123") |
| 14 | self.assertIsInstance(result, str) |
| 15 | |
| 16 | |
| 17 | if __name__ == "__main__": |
| 18 | unittest.main() |
| 19 |
+113
-18
| --- tests/test_complete.py | ||
| +++ tests/test_complete.py | ||
| @@ -1,19 +1,114 @@ | ||
| 1 | +"""Tests for utils.complete helpers.""" | |
| 2 | +import os | |
| 3 | +import tempfile | |
| 1 | 4 | import unittest |
| 2 | -from src.utils.complete import complete | |
| 3 | - | |
| 4 | -class TestComplete(unittest.TestCase): | |
| 5 | - | |
| 6 | - def setUp(self): | |
| 7 | - # Set up any necessary test data | |
| 8 | - pass | |
| 9 | - | |
| 10 | - def tearDown(self): | |
| 11 | - # Clean up after tests | |
| 12 | - pass | |
| 13 | - | |
| 14 | - def test_complete(self): | |
| 15 | - # Placeholder for complete function test | |
| 16 | - pass | |
| 17 | - | |
| 18 | -if __name__ == '__main__': | |
| 19 | - unittest.main() | |
| 5 | + | |
| 6 | +from utils.complete import _pick_main_html, _copy_dir, _find_config, _write_minimal_hugo_toml | |
| 7 | + | |
| 8 | + | |
| 9 | +class TestPickMainHtml(unittest.TestCase): | |
| 10 | + def test_prefers_index_html(self): | |
| 11 | + files = ["/path/about.html", "/path/index.html", "/path/contact.html"] | |
| 12 | + self.assertEqual(_pick_main_html(files), "/path/index.html") | |
| 13 | + | |
| 14 | + def test_prefers_home_html(self): | |
| 15 | + files = ["/path/about.html", "/path/home.html"] | |
| 16 | + self.assertEqual(_pick_main_html(files), "/path/home.html") | |
| 17 | + | |
| 18 | + def test_falls_back_to_first(self): | |
| 19 | + files = ["/path/about.html", "/path/services.html"] | |
| 20 | + self.assertEqual(_pick_main_html(files), "/path/about.html") | |
| 21 | + | |
| 22 | + | |
| 23 | +class TestCopyDir(unittest.TestCase): | |
| 24 | + def test_copies_files(self): | |
| 25 | + with tempfile.TemporaryDirectory() as src, tempfile.TemporaryDirectory() as dst: | |
| 26 | + open(os.path.join(src, "file.txt"), "w").close() | |
| 27 | + _copy_dir(src, dst) | |
| 28 | + self.assertTrue(os.path.exists(os.path.join(dst, "file.txt"))) | |
| 29 | + | |
| 30 | + def test_excludes_specified_names(self): | |
| 31 | + with tempfile.TemporaryDirectory() as src, tempfile.TemporaryDirectory() as dst: | |
| 32 | + open(os.path.join(src, "keep.txt"), "w").close() | |
| 33 | + open(os.path.join(src, "skip.txt"), "w").close() | |
| 34 | + _copy_dir(src, dst, exclude={"skip.txt"}) | |
| 35 | + self.assertTrue(os.path.exists(os.path.join(dst, "keep.txt"))) | |
| 36 | + self.assertFalse(os.path.exists(os.path.join(dst, "skip.txt"))) | |
| 37 | + | |
| 38 | + def test_excludes_dotfiles_starting_with_dot_underscore(self): | |
| 39 | + with tempfile.TemporaryDirectory() as src, tempfile.TemporaryDirectory() as dst: | |
| 40 | + open(os.path.join(src, "._junk"), "w").close() | |
| 41 | + open(os.path.join(src, "real.txt"), "w").close() | |
| 42 | + _copy_dir(src, dst) | |
| 43 | + self.assertFalse(os.path.exists(os.path.join(dst, "._junk"))) | |
| 44 | + self.assertTrue(os.path.exists(os.path.join(dst, "real.txt"))) | |
| 45 | + | |
| 46 | + def test_nonexistent_src_is_safe(self): | |
| 47 | + with tempfile.TemporaryDirectory() as dst: | |
| 48 | + _copy_dir("/nonexistent/src", dst) # should not raise | |
| 49 | + | |
| 50 | + def test_recurses_into_subdirs(self): | |
| 51 | + with tempfile.TemporaryDirectory() as src, tempfile.TemporaryDirectory() as dst: | |
| 52 | + nested = os.path.join(src, "sub") | |
| 53 | + os.makedirs(nested) | |
| 54 | + open(os.path.join(nested, "nested.txt"), "w").close() | |
| 55 | + _copy_dir(src, dst) | |
| 56 | + self.assertTrue(os.path.exists(os.path.join(dst, "sub", "nested.txt"))) | |
| 57 | + | |
| 58 | + | |
| 59 | +class TestFindConfig(unittest.TestCase): | |
| 60 | + def test_finds_hugo_toml(self): | |
| 61 | + with tempfile.TemporaryDirectory() as tmp: | |
| 62 | + path = os.path.join(tmp, "hugo.toml") | |
| 63 | + open(path, "w").close() | |
| 64 | + self.assertEqual(_find_config(tmp), path) | |
| 65 | + | |
| 66 | + def test_finds_config_toml(self): | |
| 67 | + with tempfile.TemporaryDirectory() as tmp: | |
| 68 | + path = os.path.join(tmp, "config.toml") | |
| 69 | + open(path, "w").close() | |
| 70 | + self.assertEqual(_find_config(tmp), path) | |
| 71 | + | |
| 72 | + def test_prefers_hugo_toml_over_config_toml(self): | |
| 73 | + with tempfile.TemporaryDirectory() as tmp: | |
| 74 | + hugo = os.path.join(tmp, "hugo.toml") | |
| 75 | + config = os.path.join(tmp, "config.toml") | |
| 76 | + open(hugo, "w").close() | |
| 77 | + open(config, "w").close() | |
| 78 | + self.assertEqual(_find_config(tmp), hugo) | |
| 79 | + | |
| 80 | + def test_finds_nested_config(self): | |
| 81 | + with tempfile.TemporaryDirectory() as tmp: | |
| 82 | + nested = os.path.join(tmp, "config", "_default") | |
| 83 | + os.makedirs(nested) | |
| 84 | + path = os.path.join(nested, "config.toml") | |
| 85 | + open(path, "w").close() | |
| 86 | + self.assertEqual(_find_config(tmp), path) | |
| 87 | + | |
| 88 | + def test_returns_none_when_missing(self): | |
| 89 | + with tempfile.TemporaryDirectory() as tmp: | |
| 90 | + self.assertIsNone(_find_config(tmp)) | |
| 91 | + | |
| 92 | + | |
| 93 | +class TestWriteMinimalHugoToml(unittest.TestCase): | |
| 94 | + def test_writes_valid_toml(self): | |
| 95 | + with tempfile.TemporaryDirectory() as tmp: | |
| 96 | + _write_minimal_hugo_toml(tmp, "my-theme") | |
| 97 | + path = os.path.join(tmp, "hugo.toml") | |
| 98 | + self.assertTrue(os.path.exists(path)) | |
| 99 | + with open(path) as f: | |
| 100 | + content = f.read() | |
| 101 | + self.assertIn('theme = "my-theme"', content) | |
| 102 | + self.assertIn("My Theme", content) | |
| 103 | + | |
| 104 | + def test_sanitizes_quotes_in_theme_name(self): | |
| 105 | + with tempfile.TemporaryDirectory() as tmp: | |
| 106 | + _write_minimal_hugo_toml(tmp, 'evil"theme') | |
| 107 | + with open(os.path.join(tmp, "hugo.toml")) as f: | |
| 108 | + content = f.read() | |
| 109 | + # Should not contain unescaped quote that breaks TOML | |
| 110 | + self.assertNotIn('theme = "evil"theme"', content) | |
| 111 | + | |
| 112 | + | |
| 113 | +if __name__ == "__main__": | |
| 114 | + unittest.main() | |
| 20 | 115 | |
| 21 | 116 | ADDED tests/test_config.py |
| --- tests/test_complete.py | |
| +++ tests/test_complete.py | |
| @@ -1,19 +1,114 @@ | |
| 1 | import unittest |
| 2 | from src.utils.complete import complete |
| 3 | |
| 4 | class TestComplete(unittest.TestCase): |
| 5 | |
| 6 | def setUp(self): |
| 7 | # Set up any necessary test data |
| 8 | pass |
| 9 | |
| 10 | def tearDown(self): |
| 11 | # Clean up after tests |
| 12 | pass |
| 13 | |
| 14 | def test_complete(self): |
| 15 | # Placeholder for complete function test |
| 16 | pass |
| 17 | |
| 18 | if __name__ == '__main__': |
| 19 | unittest.main() |
| 20 | |
| 21 | DDED tests/test_config.py |
| --- tests/test_complete.py | |
| +++ tests/test_complete.py | |
| @@ -1,19 +1,114 @@ | |
| 1 | """Tests for utils.complete helpers.""" |
| 2 | import os |
| 3 | import tempfile |
| 4 | import unittest |
| 5 | |
| 6 | from utils.complete import _pick_main_html, _copy_dir, _find_config, _write_minimal_hugo_toml |
| 7 | |
| 8 | |
| 9 | class TestPickMainHtml(unittest.TestCase): |
| 10 | def test_prefers_index_html(self): |
| 11 | files = ["/path/about.html", "/path/index.html", "/path/contact.html"] |
| 12 | self.assertEqual(_pick_main_html(files), "/path/index.html") |
| 13 | |
| 14 | def test_prefers_home_html(self): |
| 15 | files = ["/path/about.html", "/path/home.html"] |
| 16 | self.assertEqual(_pick_main_html(files), "/path/home.html") |
| 17 | |
| 18 | def test_falls_back_to_first(self): |
| 19 | files = ["/path/about.html", "/path/services.html"] |
| 20 | self.assertEqual(_pick_main_html(files), "/path/about.html") |
| 21 | |
| 22 | |
| 23 | class TestCopyDir(unittest.TestCase): |
| 24 | def test_copies_files(self): |
| 25 | with tempfile.TemporaryDirectory() as src, tempfile.TemporaryDirectory() as dst: |
| 26 | open(os.path.join(src, "file.txt"), "w").close() |
| 27 | _copy_dir(src, dst) |
| 28 | self.assertTrue(os.path.exists(os.path.join(dst, "file.txt"))) |
| 29 | |
| 30 | def test_excludes_specified_names(self): |
| 31 | with tempfile.TemporaryDirectory() as src, tempfile.TemporaryDirectory() as dst: |
| 32 | open(os.path.join(src, "keep.txt"), "w").close() |
| 33 | open(os.path.join(src, "skip.txt"), "w").close() |
| 34 | _copy_dir(src, dst, exclude={"skip.txt"}) |
| 35 | self.assertTrue(os.path.exists(os.path.join(dst, "keep.txt"))) |
| 36 | self.assertFalse(os.path.exists(os.path.join(dst, "skip.txt"))) |
| 37 | |
| 38 | def test_excludes_dotfiles_starting_with_dot_underscore(self): |
| 39 | with tempfile.TemporaryDirectory() as src, tempfile.TemporaryDirectory() as dst: |
| 40 | open(os.path.join(src, "._junk"), "w").close() |
| 41 | open(os.path.join(src, "real.txt"), "w").close() |
| 42 | _copy_dir(src, dst) |
| 43 | self.assertFalse(os.path.exists(os.path.join(dst, "._junk"))) |
| 44 | self.assertTrue(os.path.exists(os.path.join(dst, "real.txt"))) |
| 45 | |
| 46 | def test_nonexistent_src_is_safe(self): |
| 47 | with tempfile.TemporaryDirectory() as dst: |
| 48 | _copy_dir("/nonexistent/src", dst) # should not raise |
| 49 | |
| 50 | def test_recurses_into_subdirs(self): |
| 51 | with tempfile.TemporaryDirectory() as src, tempfile.TemporaryDirectory() as dst: |
| 52 | nested = os.path.join(src, "sub") |
| 53 | os.makedirs(nested) |
| 54 | open(os.path.join(nested, "nested.txt"), "w").close() |
| 55 | _copy_dir(src, dst) |
| 56 | self.assertTrue(os.path.exists(os.path.join(dst, "sub", "nested.txt"))) |
| 57 | |
| 58 | |
| 59 | class TestFindConfig(unittest.TestCase): |
| 60 | def test_finds_hugo_toml(self): |
| 61 | with tempfile.TemporaryDirectory() as tmp: |
| 62 | path = os.path.join(tmp, "hugo.toml") |
| 63 | open(path, "w").close() |
| 64 | self.assertEqual(_find_config(tmp), path) |
| 65 | |
| 66 | def test_finds_config_toml(self): |
| 67 | with tempfile.TemporaryDirectory() as tmp: |
| 68 | path = os.path.join(tmp, "config.toml") |
| 69 | open(path, "w").close() |
| 70 | self.assertEqual(_find_config(tmp), path) |
| 71 | |
| 72 | def test_prefers_hugo_toml_over_config_toml(self): |
| 73 | with tempfile.TemporaryDirectory() as tmp: |
| 74 | hugo = os.path.join(tmp, "hugo.toml") |
| 75 | config = os.path.join(tmp, "config.toml") |
| 76 | open(hugo, "w").close() |
| 77 | open(config, "w").close() |
| 78 | self.assertEqual(_find_config(tmp), hugo) |
| 79 | |
| 80 | def test_finds_nested_config(self): |
| 81 | with tempfile.TemporaryDirectory() as tmp: |
| 82 | nested = os.path.join(tmp, "config", "_default") |
| 83 | os.makedirs(nested) |
| 84 | path = os.path.join(nested, "config.toml") |
| 85 | open(path, "w").close() |
| 86 | self.assertEqual(_find_config(tmp), path) |
| 87 | |
| 88 | def test_returns_none_when_missing(self): |
| 89 | with tempfile.TemporaryDirectory() as tmp: |
| 90 | self.assertIsNone(_find_config(tmp)) |
| 91 | |
| 92 | |
| 93 | class TestWriteMinimalHugoToml(unittest.TestCase): |
| 94 | def test_writes_valid_toml(self): |
| 95 | with tempfile.TemporaryDirectory() as tmp: |
| 96 | _write_minimal_hugo_toml(tmp, "my-theme") |
| 97 | path = os.path.join(tmp, "hugo.toml") |
| 98 | self.assertTrue(os.path.exists(path)) |
| 99 | with open(path) as f: |
| 100 | content = f.read() |
| 101 | self.assertIn('theme = "my-theme"', content) |
| 102 | self.assertIn("My Theme", content) |
| 103 | |
| 104 | def test_sanitizes_quotes_in_theme_name(self): |
| 105 | with tempfile.TemporaryDirectory() as tmp: |
| 106 | _write_minimal_hugo_toml(tmp, 'evil"theme') |
| 107 | with open(os.path.join(tmp, "hugo.toml")) as f: |
| 108 | content = f.read() |
| 109 | # Should not contain unescaped quote that breaks TOML |
| 110 | self.assertNotIn('theme = "evil"theme"', content) |
| 111 | |
| 112 | |
| 113 | if __name__ == "__main__": |
| 114 | unittest.main() |
| 115 | |
| 116 | DDED tests/test_config.py |
+22
| --- a/tests/test_config.py | ||
| +++ b/tests/test_config.py | ||
| @@ -0,0 +1,22 @@ | ||
| 1 | +"""Tests for confsys | |
| 2 | +import unittest | |
| 3 | +from, MagicMock | |
| 4 | + | |
| 5 | + | |
| 6 | +class TestCallAiRouting(unittest.TestCase): | |
| 7 | + def _get_config(self): | |
| 8 | + # Reload config to pick up env var change importimport hugoifier.confanthropic', return_value="response") as mock_fn: | |
| 9 | + config.BACKEND = 'anthropic' | |
| 10 | + result = config.call_ai("hello") | |
| 11 | + mock_fn.assert_called_once() | |
| 12 | + self.assertEqual(result, "response") | |
| 13 | + | |
| 14 | + def test_openai_backend_calls_openai(self): | |
| 15 | + import import hugoifier.config as config | |
| 16 | + with patch.object(config, '_call_openai', return_value="response") as mock_fn: | |
| 17 | + config.BACKEND = 'openai' | |
| 18 | + result = config.call_ai("hello") | |
| 19 | + mock_fn.assert_called_once() | |
| 20 | + self.assertEqual(result, "response") | |
| 21 | + | |
| 22 | + def test_google_backend_calls_goimport hugoifier.confgoogle', return_value="response") as m |
| --- a/tests/test_config.py | |
| +++ b/tests/test_config.py | |
| @@ -0,0 +1,22 @@ | |
| --- a/tests/test_config.py | |
| +++ b/tests/test_config.py | |
| @@ -0,0 +1,22 @@ | |
| 1 | """Tests for confsys |
| 2 | import unittest |
| 3 | from, MagicMock |
| 4 | |
| 5 | |
| 6 | class TestCallAiRouting(unittest.TestCase): |
| 7 | def _get_config(self): |
| 8 | # Reload config to pick up env var change importimport hugoifier.confanthropic', return_value="response") as mock_fn: |
| 9 | config.BACKEND = 'anthropic' |
| 10 | result = config.call_ai("hello") |
| 11 | mock_fn.assert_called_once() |
| 12 | self.assertEqual(result, "response") |
| 13 | |
| 14 | def test_openai_backend_calls_openai(self): |
| 15 | import import hugoifier.config as config |
| 16 | with patch.object(config, '_call_openai', return_value="response") as mock_fn: |
| 17 | config.BACKEND = 'openai' |
| 18 | result = config.call_ai("hello") |
| 19 | mock_fn.assert_called_once() |
| 20 | self.assertEqual(result, "response") |
| 21 | |
| 22 | def test_google_backend_calls_goimport hugoifier.confgoogle', return_value="response") as m |
+188
-16
| --- tests/test_decapify.py | ||
| +++ tests/test_decapify.py | ||
| @@ -1,19 +1,191 @@ | ||
| 1 | +"""Tests for utils.decapify.""" | |
| 2 | +import os | |
| 3 | +import tempfile | |
| 1 | 4 | import unittest |
| 2 | -from src.utils.decapify import decapify | |
| 5 | + | |
| 6 | +from utils.decapify import ( | |
| 7 | + _sanitize_color, | |
| 8 | + _widget_for_value, | |
| 9 | + _parse_frontmatter, | |
| 10 | + _infer_fields_for_folder, | |
| 11 | + _infer_fields_for_file, | |
| 12 | + _build_collections, | |
| 13 | + decapify, | |
| 14 | +) | |
| 15 | + | |
| 16 | + | |
| 17 | +class TestSanitizeColor(unittest.TestCase): | |
| 18 | + def test_valid_hex6(self): | |
| 19 | + self.assertEqual(_sanitize_color("#2e3748"), "#2e3748") | |
| 20 | + | |
| 21 | + def test_valid_hex3(self): | |
| 22 | + self.assertEqual(_sanitize_color("#fff"), "#fff") | |
| 23 | + | |
| 24 | + def test_uppercase(self): | |
| 25 | + self.assertEqual(_sanitize_color("#AABBCC"), "#AABBCC") | |
| 26 | + | |
| 27 | + def test_invalid_falls_back(self): | |
| 28 | + self.assertEqual(_sanitize_color("red"), "#2e3748") | |
| 29 | + self.assertEqual(_sanitize_color("javascript:alert(1)"), "#2e3748") | |
| 30 | + self.assertEqual(_sanitize_color("#gggggg"), "#2e3748") | |
| 31 | + | |
| 32 | + | |
| 33 | +class TestWidgetForValue(unittest.TestCase): | |
| 34 | + def test_bool(self): | |
| 35 | + self.assertEqual(_widget_for_value(True), "boolean") | |
| 36 | + self.assertEqual(_widget_for_value(False), "boolean") | |
| 37 | + | |
| 38 | + def test_number(self): | |
| 39 | + self.assertEqual(_widget_for_value(42), "number") | |
| 40 | + self.assertEqual(_widget_for_value(3.14), "number") | |
| 41 | + | |
| 42 | + def test_list(self): | |
| 43 | + self.assertEqual(_widget_for_value([]), "list") | |
| 44 | + self.assertEqual(_widget_for_value(["a", "b"]), "list") | |
| 45 | + | |
| 46 | + def test_string_default(self): | |
| 47 | + self.assertEqual(_widget_for_value("hello"), "string") | |
| 48 | + self.assertEqual(_widget_for_value(None), "string") | |
| 49 | + | |
| 50 | + | |
| 51 | +class TestParseFrontmatter(unittest.TestCase): | |
| 52 | + def test_parses_yaml_frontmatter(self): | |
| 53 | + with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f: | |
| 54 | + f.write("---\ntitle: Hello\ndate: 2024-01-01\ndraft: false\n---\n\nBody text.\n") | |
| 55 | + path = f.name | |
| 56 | + try: | |
| 57 | + result = _parse_frontmatter(path) | |
| 58 | + self.assertEqual(result["title"], "Hello") | |
| 59 | + self.assertEqual(result["draft"], False) | |
| 60 | + finally: | |
| 61 | + os.unlink(path) | |
| 62 | + | |
| 63 | + def test_empty_on_missing_frontmatter(self): | |
| 64 | + with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f: | |
| 65 | + f.write("No frontmatter here.\n") | |
| 66 | + path = f.name | |
| 67 | + try: | |
| 68 | + self.assertEqual(_parse_frontmatter(path), {}) | |
| 69 | + finally: | |
| 70 | + os.unlink(path) | |
| 71 | + | |
| 72 | + def test_empty_on_missing_file(self): | |
| 73 | + self.assertEqual(_parse_frontmatter("/nonexistent/path.md"), {}) | |
| 74 | + | |
| 75 | + | |
| 76 | +class TestInferFields(unittest.TestCase): | |
| 77 | + def _make_md(self, dirpath, name, frontmatter): | |
| 78 | + path = os.path.join(dirpath, name) | |
| 79 | + with open(path, "w") as f: | |
| 80 | + f.write("---\n") | |
| 81 | + for k, v in frontmatter.items(): | |
| 82 | + f.write(f"{k}: {v!r}\n") | |
| 83 | + f.write("---\n\nBody.\n") | |
| 84 | + return path | |
| 85 | + | |
| 86 | + def test_folder_fields_include_known_keys(self): | |
| 87 | + with tempfile.TemporaryDirectory() as tmp: | |
| 88 | + self._make_md(tmp, "post1.md", {"title": "Hello", "date": "2024-01-01", "tags": ["a"]}) | |
| 89 | + fields = _infer_fields_for_folder(tmp, ["post1.md"]) | |
| 90 | + names = [f["name"] for f in fields] | |
| 91 | + self.assertIn("title", names) | |
| 92 | + self.assertIn("date", names) | |
| 93 | + self.assertIn("tags", names) | |
| 94 | + self.assertIn("body", names) | |
| 95 | + | |
| 96 | + def test_folder_fields_always_end_with_body(self): | |
| 97 | + with tempfile.TemporaryDirectory() as tmp: | |
| 98 | + self._make_md(tmp, "post.md", {"title": "Test"}) | |
| 99 | + fields = _infer_fields_for_folder(tmp, ["post.md"]) | |
| 100 | + self.assertEqual(fields[-1]["name"], "body") | |
| 101 | + self.assertEqual(fields[-1]["widget"], "markdown") | |
| 102 | + | |
| 103 | + def test_file_fields_include_all_frontmatter_keys(self): | |
| 104 | + with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f: | |
| 105 | + f.write("---\ntitle: About\nsubtitle: Us\n---\n") | |
| 106 | + path = f.name | |
| 107 | + try: | |
| 108 | + fields = _infer_fields_for_file(path) | |
| 109 | + names = [fi["name"] for fi in fields] | |
| 110 | + self.assertIn("title", names) | |
| 111 | + self.assertIn("subtitle", names) | |
| 112 | + self.assertIn("body", names) | |
| 113 | + finally: | |
| 114 | + os.unlink(path) | |
| 115 | + | |
| 116 | + | |
| 117 | +class TestBuildCollections(unittest.TestCase): | |
| 118 | + def test_folder_collection_from_md_files(self): | |
| 119 | + with tempfile.TemporaryDirectory() as tmp: | |
| 120 | + blog = os.path.join(tmp, "blog") | |
| 121 | + os.makedirs(blog) | |
| 122 | + for i in range(3): | |
| 123 | + with open(os.path.join(blog, f"post{i}.md"), "w") as f: | |
| 124 | + f.write("---\ntitle: Post\n---\n") | |
| 125 | + collections = _build_collections(tmp) | |
| 126 | + self.assertEqual(len(collections), 1) | |
| 127 | + self.assertEqual(collections[0]["name"], "blog") | |
| 128 | + self.assertIn("folder", collections[0]) | |
| 129 | + | |
| 130 | + def test_file_collection_from_index_only(self): | |
| 131 | + with tempfile.TemporaryDirectory() as tmp: | |
| 132 | + about = os.path.join(tmp, "about") | |
| 133 | + os.makedirs(about) | |
| 134 | + with open(os.path.join(about, "_index.md"), "w") as f: | |
| 135 | + f.write("---\ntitle: About\n---\n") | |
| 136 | + collections = _build_collections(tmp) | |
| 137 | + self.assertEqual(len(collections), 1) | |
| 138 | + self.assertIn("files", collections[0]) | |
| 139 | + | |
| 140 | + def test_recursive_md_discovery(self): | |
| 141 | + with tempfile.TemporaryDirectory() as tmp: | |
| 142 | + deep = os.path.join(tmp, "blog", "2024") | |
| 143 | + os.makedirs(deep) | |
| 144 | + with open(os.path.join(deep, "post.md"), "w") as f: | |
| 145 | + f.write("---\ntitle: Deep Post\n---\n") | |
| 146 | + collections = _build_collections(tmp) | |
| 147 | + self.assertEqual(len(collections), 1) | |
| 148 | + self.assertEqual(collections[0]["name"], "blog") | |
| 149 | + | |
| 150 | + def test_default_collection_when_empty(self): | |
| 151 | + with tempfile.TemporaryDirectory() as tmp: | |
| 152 | + collections = _build_collections(tmp) | |
| 153 | + self.assertEqual(len(collections), 1) | |
| 154 | + self.assertEqual(collections[0]["name"], "pages") | |
| 155 | + | |
| 156 | + def test_nonexistent_dir_returns_default(self): | |
| 157 | + collections = _build_collections("/nonexistent/content") | |
| 158 | + self.assertEqual(len(collections), 1) | |
| 159 | + self.assertEqual(collections[0]["name"], "pages") | |
| 160 | + | |
| 3 | 161 | |
| 4 | 162 | class TestDecapify(unittest.TestCase): |
| 5 | - | |
| 6 | - def setUp(self): | |
| 7 | - # Set up any necessary test data | |
| 8 | - pass | |
| 9 | - | |
| 10 | - def tearDown(self): | |
| 11 | - # Clean up after tests | |
| 12 | - pass | |
| 13 | - | |
| 14 | - def test_decapify(self): | |
| 15 | - # Placeholder for decapify function test | |
| 16 | - pass | |
| 17 | - | |
| 18 | -if __name__ == '__main__': | |
| 19 | - unittest.main() | |
| 163 | + def test_creates_admin_files(self): | |
| 164 | + with tempfile.TemporaryDirectory() as tmp: | |
| 165 | + content = os.path.join(tmp, "content") | |
| 166 | + os.makedirs(content) | |
| 167 | + with open(os.path.join(content, "_index.md"), "w") as f: | |
| 168 | + f.write("---\ntitle: Home\n---\n") | |
| 169 | + result = decapify(tmp) | |
| 170 | + self.assertIn("complete", result.lower()) | |
| 171 | + self.assertTrue(os.path.exists(os.path.join(tmp, "static", "admin", "index.html"))) | |
| 172 | + self.assertTrue(os.path.exists(os.path.join(tmp, "static", "admin", "config.yml"))) | |
| 173 | + | |
| 174 | + def test_admin_index_escapes_name(self): | |
| 175 | + with tempfile.TemporaryDirectory() as tmp: | |
| 176 | + decapify(tmp, cms_name='<script>alert("xss")</script>') | |
| 177 | + with open(os.path.join(tmp, "static", "admin", "index.html")) as f: | |
| 178 | + html = f.read() | |
| 179 | + self.assertNotIn("<script>alert", html) | |
| 180 | + self.assertIn("<script>", html) | |
| 181 | + | |
| 182 | + def test_invalid_color_falls_back(self): | |
| 183 | + with tempfile.TemporaryDirectory() as tmp: | |
| 184 | + decapify(tmp, cms_color="not-a-color") | |
| 185 | + with open(os.path.join(tmp, "static", "admin", "index.html")) as f: | |
| 186 | + html = f.read() | |
| 187 | + self.assertIn("#2e3748", html) | |
| 188 | + | |
| 189 | + | |
| 190 | +if __name__ == "__main__": | |
| 191 | + unittest.main() | |
| 20 | 192 |
| --- tests/test_decapify.py | |
| +++ tests/test_decapify.py | |
| @@ -1,19 +1,191 @@ | |
| 1 | import unittest |
| 2 | from src.utils.decapify import decapify |
| 3 | |
| 4 | class TestDecapify(unittest.TestCase): |
| 5 | |
| 6 | def setUp(self): |
| 7 | # Set up any necessary test data |
| 8 | pass |
| 9 | |
| 10 | def tearDown(self): |
| 11 | # Clean up after tests |
| 12 | pass |
| 13 | |
| 14 | def test_decapify(self): |
| 15 | # Placeholder for decapify function test |
| 16 | pass |
| 17 | |
| 18 | if __name__ == '__main__': |
| 19 | unittest.main() |
| 20 |
| --- tests/test_decapify.py | |
| +++ tests/test_decapify.py | |
| @@ -1,19 +1,191 @@ | |
| 1 | """Tests for utils.decapify.""" |
| 2 | import os |
| 3 | import tempfile |
| 4 | import unittest |
| 5 | |
| 6 | from utils.decapify import ( |
| 7 | _sanitize_color, |
| 8 | _widget_for_value, |
| 9 | _parse_frontmatter, |
| 10 | _infer_fields_for_folder, |
| 11 | _infer_fields_for_file, |
| 12 | _build_collections, |
| 13 | decapify, |
| 14 | ) |
| 15 | |
| 16 | |
| 17 | class TestSanitizeColor(unittest.TestCase): |
| 18 | def test_valid_hex6(self): |
| 19 | self.assertEqual(_sanitize_color("#2e3748"), "#2e3748") |
| 20 | |
| 21 | def test_valid_hex3(self): |
| 22 | self.assertEqual(_sanitize_color("#fff"), "#fff") |
| 23 | |
| 24 | def test_uppercase(self): |
| 25 | self.assertEqual(_sanitize_color("#AABBCC"), "#AABBCC") |
| 26 | |
| 27 | def test_invalid_falls_back(self): |
| 28 | self.assertEqual(_sanitize_color("red"), "#2e3748") |
| 29 | self.assertEqual(_sanitize_color("javascript:alert(1)"), "#2e3748") |
| 30 | self.assertEqual(_sanitize_color("#gggggg"), "#2e3748") |
| 31 | |
| 32 | |
| 33 | class TestWidgetForValue(unittest.TestCase): |
| 34 | def test_bool(self): |
| 35 | self.assertEqual(_widget_for_value(True), "boolean") |
| 36 | self.assertEqual(_widget_for_value(False), "boolean") |
| 37 | |
| 38 | def test_number(self): |
| 39 | self.assertEqual(_widget_for_value(42), "number") |
| 40 | self.assertEqual(_widget_for_value(3.14), "number") |
| 41 | |
| 42 | def test_list(self): |
| 43 | self.assertEqual(_widget_for_value([]), "list") |
| 44 | self.assertEqual(_widget_for_value(["a", "b"]), "list") |
| 45 | |
| 46 | def test_string_default(self): |
| 47 | self.assertEqual(_widget_for_value("hello"), "string") |
| 48 | self.assertEqual(_widget_for_value(None), "string") |
| 49 | |
| 50 | |
| 51 | class TestParseFrontmatter(unittest.TestCase): |
| 52 | def test_parses_yaml_frontmatter(self): |
| 53 | with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f: |
| 54 | f.write("---\ntitle: Hello\ndate: 2024-01-01\ndraft: false\n---\n\nBody text.\n") |
| 55 | path = f.name |
| 56 | try: |
| 57 | result = _parse_frontmatter(path) |
| 58 | self.assertEqual(result["title"], "Hello") |
| 59 | self.assertEqual(result["draft"], False) |
| 60 | finally: |
| 61 | os.unlink(path) |
| 62 | |
| 63 | def test_empty_on_missing_frontmatter(self): |
| 64 | with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f: |
| 65 | f.write("No frontmatter here.\n") |
| 66 | path = f.name |
| 67 | try: |
| 68 | self.assertEqual(_parse_frontmatter(path), {}) |
| 69 | finally: |
| 70 | os.unlink(path) |
| 71 | |
| 72 | def test_empty_on_missing_file(self): |
| 73 | self.assertEqual(_parse_frontmatter("/nonexistent/path.md"), {}) |
| 74 | |
| 75 | |
| 76 | class TestInferFields(unittest.TestCase): |
| 77 | def _make_md(self, dirpath, name, frontmatter): |
| 78 | path = os.path.join(dirpath, name) |
| 79 | with open(path, "w") as f: |
| 80 | f.write("---\n") |
| 81 | for k, v in frontmatter.items(): |
| 82 | f.write(f"{k}: {v!r}\n") |
| 83 | f.write("---\n\nBody.\n") |
| 84 | return path |
| 85 | |
| 86 | def test_folder_fields_include_known_keys(self): |
| 87 | with tempfile.TemporaryDirectory() as tmp: |
| 88 | self._make_md(tmp, "post1.md", {"title": "Hello", "date": "2024-01-01", "tags": ["a"]}) |
| 89 | fields = _infer_fields_for_folder(tmp, ["post1.md"]) |
| 90 | names = [f["name"] for f in fields] |
| 91 | self.assertIn("title", names) |
| 92 | self.assertIn("date", names) |
| 93 | self.assertIn("tags", names) |
| 94 | self.assertIn("body", names) |
| 95 | |
| 96 | def test_folder_fields_always_end_with_body(self): |
| 97 | with tempfile.TemporaryDirectory() as tmp: |
| 98 | self._make_md(tmp, "post.md", {"title": "Test"}) |
| 99 | fields = _infer_fields_for_folder(tmp, ["post.md"]) |
| 100 | self.assertEqual(fields[-1]["name"], "body") |
| 101 | self.assertEqual(fields[-1]["widget"], "markdown") |
| 102 | |
| 103 | def test_file_fields_include_all_frontmatter_keys(self): |
| 104 | with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f: |
| 105 | f.write("---\ntitle: About\nsubtitle: Us\n---\n") |
| 106 | path = f.name |
| 107 | try: |
| 108 | fields = _infer_fields_for_file(path) |
| 109 | names = [fi["name"] for fi in fields] |
| 110 | self.assertIn("title", names) |
| 111 | self.assertIn("subtitle", names) |
| 112 | self.assertIn("body", names) |
| 113 | finally: |
| 114 | os.unlink(path) |
| 115 | |
| 116 | |
| 117 | class TestBuildCollections(unittest.TestCase): |
| 118 | def test_folder_collection_from_md_files(self): |
| 119 | with tempfile.TemporaryDirectory() as tmp: |
| 120 | blog = os.path.join(tmp, "blog") |
| 121 | os.makedirs(blog) |
| 122 | for i in range(3): |
| 123 | with open(os.path.join(blog, f"post{i}.md"), "w") as f: |
| 124 | f.write("---\ntitle: Post\n---\n") |
| 125 | collections = _build_collections(tmp) |
| 126 | self.assertEqual(len(collections), 1) |
| 127 | self.assertEqual(collections[0]["name"], "blog") |
| 128 | self.assertIn("folder", collections[0]) |
| 129 | |
| 130 | def test_file_collection_from_index_only(self): |
| 131 | with tempfile.TemporaryDirectory() as tmp: |
| 132 | about = os.path.join(tmp, "about") |
| 133 | os.makedirs(about) |
| 134 | with open(os.path.join(about, "_index.md"), "w") as f: |
| 135 | f.write("---\ntitle: About\n---\n") |
| 136 | collections = _build_collections(tmp) |
| 137 | self.assertEqual(len(collections), 1) |
| 138 | self.assertIn("files", collections[0]) |
| 139 | |
| 140 | def test_recursive_md_discovery(self): |
| 141 | with tempfile.TemporaryDirectory() as tmp: |
| 142 | deep = os.path.join(tmp, "blog", "2024") |
| 143 | os.makedirs(deep) |
| 144 | with open(os.path.join(deep, "post.md"), "w") as f: |
| 145 | f.write("---\ntitle: Deep Post\n---\n") |
| 146 | collections = _build_collections(tmp) |
| 147 | self.assertEqual(len(collections), 1) |
| 148 | self.assertEqual(collections[0]["name"], "blog") |
| 149 | |
| 150 | def test_default_collection_when_empty(self): |
| 151 | with tempfile.TemporaryDirectory() as tmp: |
| 152 | collections = _build_collections(tmp) |
| 153 | self.assertEqual(len(collections), 1) |
| 154 | self.assertEqual(collections[0]["name"], "pages") |
| 155 | |
| 156 | def test_nonexistent_dir_returns_default(self): |
| 157 | collections = _build_collections("/nonexistent/content") |
| 158 | self.assertEqual(len(collections), 1) |
| 159 | self.assertEqual(collections[0]["name"], "pages") |
| 160 | |
| 161 | |
| 162 | class TestDecapify(unittest.TestCase): |
| 163 | def test_creates_admin_files(self): |
| 164 | with tempfile.TemporaryDirectory() as tmp: |
| 165 | content = os.path.join(tmp, "content") |
| 166 | os.makedirs(content) |
| 167 | with open(os.path.join(content, "_index.md"), "w") as f: |
| 168 | f.write("---\ntitle: Home\n---\n") |
| 169 | result = decapify(tmp) |
| 170 | self.assertIn("complete", result.lower()) |
| 171 | self.assertTrue(os.path.exists(os.path.join(tmp, "static", "admin", "index.html"))) |
| 172 | self.assertTrue(os.path.exists(os.path.join(tmp, "static", "admin", "config.yml"))) |
| 173 | |
| 174 | def test_admin_index_escapes_name(self): |
| 175 | with tempfile.TemporaryDirectory() as tmp: |
| 176 | decapify(tmp, cms_name='<script>alert("xss")</script>') |
| 177 | with open(os.path.join(tmp, "static", "admin", "index.html")) as f: |
| 178 | html = f.read() |
| 179 | self.assertNotIn("<script>alert", html) |
| 180 | self.assertIn("<script>", html) |
| 181 | |
| 182 | def test_invalid_color_falls_back(self): |
| 183 | with tempfile.TemporaryDirectory() as tmp: |
| 184 | decapify(tmp, cms_color="not-a-color") |
| 185 | with open(os.path.join(tmp, "static", "admin", "index.html")) as f: |
| 186 | html = f.read() |
| 187 | self.assertIn("#2e3748", html) |
| 188 | |
| 189 | |
| 190 | if __name__ == "__main__": |
| 191 | unittest.main() |
| 192 |
+15
-16
| --- tests/test_deploy.py | ||
| +++ tests/test_deploy.py | ||
| @@ -1,19 +1,18 @@ | ||
| 1 | +"""Tests for utils.deploy.""" | |
| 1 | 2 | import unittest |
| 2 | -from src.utils.deploy import deploy | |
| 3 | + | |
| 4 | +from utils.deploy import deploy | |
| 5 | + | |
| 3 | 6 | |
| 4 | 7 | class TestDeploy(unittest.TestCase): |
| 5 | - | |
| 6 | - def setUp(self): | |
| 7 | - # Set up any necessary test data | |
| 8 | - pass | |
| 9 | - | |
| 10 | - def tearDown(self): | |
| 11 | - # Clean up after tests | |
| 12 | - pass | |
| 13 | - | |
| 14 | - def test_deploy(self): | |
| 15 | - # Placeholder for deploy function test | |
| 16 | - pass | |
| 17 | - | |
| 18 | -if __name__ == '__main__': | |
| 19 | - unittest.main() | |
| 8 | + def test_returns_complete_message(self): | |
| 9 | + result = deploy("/some/path", "example.com") | |
| 10 | + self.assertIn("complete", result.lower()) | |
| 11 | + | |
| 12 | + def test_accepts_path_and_zone(self): | |
| 13 | + result = deploy("/tmp/site", "zone-id-123") | |
| 14 | + self.assertIsInstance(result, str) | |
| 15 | + | |
| 16 | + | |
| 17 | +if __name__ == "__main__": | |
| 18 | + unittest.main() | |
| 20 | 19 |
| --- tests/test_deploy.py | |
| +++ tests/test_deploy.py | |
| @@ -1,19 +1,18 @@ | |
| 1 | import unittest |
| 2 | from src.utils.deploy import deploy |
| 3 | |
| 4 | class TestDeploy(unittest.TestCase): |
| 5 | |
| 6 | def setUp(self): |
| 7 | # Set up any necessary test data |
| 8 | pass |
| 9 | |
| 10 | def tearDown(self): |
| 11 | # Clean up after tests |
| 12 | pass |
| 13 | |
| 14 | def test_deploy(self): |
| 15 | # Placeholder for deploy function test |
| 16 | pass |
| 17 | |
| 18 | if __name__ == '__main__': |
| 19 | unittest.main() |
| 20 |
| --- tests/test_deploy.py | |
| +++ tests/test_deploy.py | |
| @@ -1,19 +1,18 @@ | |
| 1 | """Tests for utils.deploy.""" |
| 2 | import unittest |
| 3 | |
| 4 | from utils.deploy import deploy |
| 5 | |
| 6 | |
| 7 | class TestDeploy(unittest.TestCase): |
| 8 | def test_returns_complete_message(self): |
| 9 | result = deploy("/some/path", "example.com") |
| 10 | self.assertIn("complete", result.lower()) |
| 11 | |
| 12 | def test_accepts_path_and_zone(self): |
| 13 | result = deploy("/tmp/site", "zone-id-123") |
| 14 | self.assertIsInstance(result, str) |
| 15 | |
| 16 | |
| 17 | if __name__ == "__main__": |
| 18 | unittest.main() |
| 19 |
+38
-41
| --- tests/test_generate_decap_config.py | ||
| +++ tests/test_generate_decap_config.py | ||
| @@ -1,44 +1,41 @@ | ||
| 1 | -import unittest | |
| 2 | -from src.utils.generate_decap_config import generate_decap_config, analyze_theme, optimize_for_decap, create_config_yaml | |
| 1 | +"""Tests for utils.generate_decap_config (thin wrapper around decapify).""" | |
| 3 | 2 | import os |
| 3 | +import tempfile | |
| 4 | +import unittest | |
| 5 | + | |
| 6 | +from utils.generate_decap_config import generate_decap_config | |
| 7 | + | |
| 4 | 8 | |
| 5 | 9 | class TestGenerateDecapConfig(unittest.TestCase): |
| 6 | - | |
| 7 | - def setUp(self): | |
| 8 | - # Set up a temporary directory for testing | |
| 9 | - self.test_dir = 'test_theme' | |
| 10 | - os.makedirs(self.test_dir, exist_ok=True) | |
| 11 | - with open(os.path.join(self.test_dir, 'index.html'), 'w') as f: | |
| 12 | - f.write('<html><nav>Menu</nav><div class="hero">Welcome</div><footer>Footer</footer></html>') | |
| 13 | - | |
| 14 | - def tearDown(self): | |
| 15 | - # Clean up the temporary directory after tests | |
| 16 | - for root, dirs, files in os.walk(self.test_dir, topdown=False): | |
| 17 | - for name in files: | |
| 18 | - os.remove(os.path.join(root, name)) | |
| 19 | - for name in dirs: | |
| 20 | - os.rmdir(os.path.join(root, name)) | |
| 21 | - os.rmdir(self.test_dir) | |
| 22 | - | |
| 23 | - def test_analyze_theme(self): | |
| 24 | - elements = analyze_theme(self.test_dir) | |
| 25 | - self.assertIn('index.html', elements['navigation']) | |
| 26 | - self.assertIn('index.html', elements['hero']) | |
| 27 | - self.assertIn('index.html', elements['footer']) | |
| 28 | - | |
| 29 | - def test_optimize_for_decap(self): | |
| 30 | - optimize_for_decap(self.test_dir) | |
| 31 | - self.assertTrue(os.path.exists(os.path.join(self.test_dir, 'data', 'navigation.yaml'))) | |
| 32 | - | |
| 33 | - def test_create_config_yaml(self): | |
| 34 | - config = create_config_yaml(self.test_dir) | |
| 35 | - self.assertIn('backend', config) | |
| 36 | - self.assertIn('collections', config) | |
| 37 | - | |
| 38 | - def test_generate_decap_config(self): | |
| 39 | - result = generate_decap_config(self.test_dir) | |
| 40 | - self.assertEqual(result, "Decap CMS config generation complete") | |
| 41 | - self.assertTrue(os.path.exists(os.path.join(self.test_dir, 'config.yml'))) | |
| 42 | - | |
| 43 | -if __name__ == '__main__': | |
| 44 | - unittest.main() | |
| 10 | + def test_creates_admin_directory(self): | |
| 11 | + with tempfile.TemporaryDirectory() as tmp: | |
| 12 | + generate_decap_config(tmp) | |
| 13 | + self.assertTrue(os.path.isdir(os.path.join(tmp, "static", "admin"))) | |
| 14 | + | |
| 15 | + def test_creates_index_html(self): | |
| 16 | + with tempfile.TemporaryDirectory() as tmp: | |
| 17 | + generate_decap_config(tmp) | |
| 18 | + self.assertTrue(os.path.exists(os.path.join(tmp, "static", "admin", "index.html"))) | |
| 19 | + | |
| 20 | + def test_creates_config_yml(self): | |
| 21 | + with tempfile.TemporaryDirectory() as tmp: | |
| 22 | + generate_decap_config(tmp) | |
| 23 | + self.assertTrue(os.path.exists(os.path.join(tmp, "static", "admin", "config.yml"))) | |
| 24 | + | |
| 25 | + def test_returns_status_string(self): | |
| 26 | + with tempfile.TemporaryDirectory() as tmp: | |
| 27 | + result = generate_decap_config(tmp) | |
| 28 | + self.assertIsInstance(result, str) | |
| 29 | + self.assertIn("complete", result.lower()) | |
| 30 | + | |
| 31 | + def test_config_yml_has_backend(self): | |
| 32 | + with tempfile.TemporaryDirectory() as tmp: | |
| 33 | + generate_decap_config(tmp) | |
| 34 | + with open(os.path.join(tmp, "static", "admin", "config.yml")) as f: | |
| 35 | + content = f.read() | |
| 36 | + self.assertIn("backend", content) | |
| 37 | + self.assertIn("git-gateway", content) | |
| 38 | + | |
| 39 | + | |
| 40 | +if __name__ == "__main__": | |
| 41 | + unittest.main() | |
| 45 | 42 |
| --- tests/test_generate_decap_config.py | |
| +++ tests/test_generate_decap_config.py | |
| @@ -1,44 +1,41 @@ | |
| 1 | import unittest |
| 2 | from src.utils.generate_decap_config import generate_decap_config, analyze_theme, optimize_for_decap, create_config_yaml |
| 3 | import os |
| 4 | |
| 5 | class TestGenerateDecapConfig(unittest.TestCase): |
| 6 | |
| 7 | def setUp(self): |
| 8 | # Set up a temporary directory for testing |
| 9 | self.test_dir = 'test_theme' |
| 10 | os.makedirs(self.test_dir, exist_ok=True) |
| 11 | with open(os.path.join(self.test_dir, 'index.html'), 'w') as f: |
| 12 | f.write('<html><nav>Menu</nav><div class="hero">Welcome</div><footer>Footer</footer></html>') |
| 13 | |
| 14 | def tearDown(self): |
| 15 | # Clean up the temporary directory after tests |
| 16 | for root, dirs, files in os.walk(self.test_dir, topdown=False): |
| 17 | for name in files: |
| 18 | os.remove(os.path.join(root, name)) |
| 19 | for name in dirs: |
| 20 | os.rmdir(os.path.join(root, name)) |
| 21 | os.rmdir(self.test_dir) |
| 22 | |
| 23 | def test_analyze_theme(self): |
| 24 | elements = analyze_theme(self.test_dir) |
| 25 | self.assertIn('index.html', elements['navigation']) |
| 26 | self.assertIn('index.html', elements['hero']) |
| 27 | self.assertIn('index.html', elements['footer']) |
| 28 | |
| 29 | def test_optimize_for_decap(self): |
| 30 | optimize_for_decap(self.test_dir) |
| 31 | self.assertTrue(os.path.exists(os.path.join(self.test_dir, 'data', 'navigation.yaml'))) |
| 32 | |
| 33 | def test_create_config_yaml(self): |
| 34 | config = create_config_yaml(self.test_dir) |
| 35 | self.assertIn('backend', config) |
| 36 | self.assertIn('collections', config) |
| 37 | |
| 38 | def test_generate_decap_config(self): |
| 39 | result = generate_decap_config(self.test_dir) |
| 40 | self.assertEqual(result, "Decap CMS config generation complete") |
| 41 | self.assertTrue(os.path.exists(os.path.join(self.test_dir, 'config.yml'))) |
| 42 | |
| 43 | if __name__ == '__main__': |
| 44 | unittest.main() |
| 45 |
| --- tests/test_generate_decap_config.py | |
| +++ tests/test_generate_decap_config.py | |
| @@ -1,44 +1,41 @@ | |
| 1 | """Tests for utils.generate_decap_config (thin wrapper around decapify).""" |
| 2 | import os |
| 3 | import tempfile |
| 4 | import unittest |
| 5 | |
| 6 | from utils.generate_decap_config import generate_decap_config |
| 7 | |
| 8 | |
| 9 | class TestGenerateDecapConfig(unittest.TestCase): |
| 10 | def test_creates_admin_directory(self): |
| 11 | with tempfile.TemporaryDirectory() as tmp: |
| 12 | generate_decap_config(tmp) |
| 13 | self.assertTrue(os.path.isdir(os.path.join(tmp, "static", "admin"))) |
| 14 | |
| 15 | def test_creates_index_html(self): |
| 16 | with tempfile.TemporaryDirectory() as tmp: |
| 17 | generate_decap_config(tmp) |
| 18 | self.assertTrue(os.path.exists(os.path.join(tmp, "static", "admin", "index.html"))) |
| 19 | |
| 20 | def test_creates_config_yml(self): |
| 21 | with tempfile.TemporaryDirectory() as tmp: |
| 22 | generate_decap_config(tmp) |
| 23 | self.assertTrue(os.path.exists(os.path.join(tmp, "static", "admin", "config.yml"))) |
| 24 | |
| 25 | def test_returns_status_string(self): |
| 26 | with tempfile.TemporaryDirectory() as tmp: |
| 27 | result = generate_decap_config(tmp) |
| 28 | self.assertIsInstance(result, str) |
| 29 | self.assertIn("complete", result.lower()) |
| 30 | |
| 31 | def test_config_yml_has_backend(self): |
| 32 | with tempfile.TemporaryDirectory() as tmp: |
| 33 | generate_decap_config(tmp) |
| 34 | with open(os.path.join(tmp, "static", "admin", "config.yml")) as f: |
| 35 | content = f.read() |
| 36 | self.assertIn("backend", content) |
| 37 | self.assertIn("git-gateway", content) |
| 38 | |
| 39 | |
| 40 | if __name__ == "__main__": |
| 41 | unittest.main() |
| 42 |
+69
-18
| --- tests/test_hugoify.py | ||
| +++ tests/test_hugoify.py | ||
| @@ -1,19 +1,70 @@ | ||
| 1 | +"""Tests for utils.hugoify.""" | |
| 2 | +import os | |
| 3 | +import tempfile | |
| 1 | 4 | import unittest |
| 2 | -from src.utils.hugoify import hugoify | |
| 3 | - | |
| 4 | -class TestHugoify(unittest.TestCase): | |
| 5 | - | |
| 6 | - def setUp(self): | |
| 7 | - # Set up any necessary test data | |
| 8 | - pass | |
| 9 | - | |
| 10 | - def tearDown(self): | |
| 11 | - # Clean up after tests | |
| 12 | - pass | |
| 13 | - | |
| 14 | - def test_hugoify(self): | |
| 15 | - # Placeholder for hugoify function test | |
| 16 | - pass | |
| 17 | - | |
| 18 | -if __name__ == '__main__': | |
| 19 | - unittest.main() | |
| 5 | + | |
| 6 | +from utils.hugoify import _parse_layout_json, _fallback_baseof, hugoify_dir | |
| 7 | + | |
| 8 | + | |
| 9 | +class TestParseLayoutJson(unittest.TestCase): | |
| 10 | + def test_parses_valid_json(self): | |
| 11 | + response = '{"_default/baseof.html": "<!doctype html>", "index.html": "{{ define \\"main\\" }}{{ end }}"}' | |
| 12 | + result = _parse_layout_json(response) | |
| 13 | + self.assertIn("_default/baseof.html", result) | |
| 14 | + self.assertEqual(result["_default/baseof.html"], "<!doctype html>") | |
| 15 | + | |
| 16 | + def test_extracts_json_from_prose(self): | |
| 17 | + response = 'Here is the converted theme:\n{"_default/baseof.html": "<html></html>"}\nDone.' | |
| 18 | + result = _parse_layout_json(response) | |
| 19 | + self.assertIn("_default/baseof.html", result) | |
| 20 | + | |
| 21 | + def test_falls_back_on_invalid_json(self): | |
| 22 | + result = _parse_layout_json("This is not JSON at all.") | |
| 23 | + self.assertIn("_default/baseof.html", result) | |
| 24 | + self.assertIn("partials/header.html", result) | |
| 25 | + self.assertIn("partials/footer.html", result) | |
| 26 | + self.assertIn("index.html", result) | |
| 27 | + | |
| 28 | + def test_fallback_contains_valid_hugo_syntax(self): | |
| 29 | + result = _parse_layout_json("not json") | |
| 30 | + baseof = result["_default/baseof.html"] | |
| 31 | + self.assertIn("block", baseof) | |
| 32 | + self.assertIn("partial", baseof) | |
| 33 | + | |
| 34 | + | |
| 35 | +class TestFallbackBaseof(unittest.TestCase): | |
| 36 | + def test_contains_required_hugo_blocks(self): | |
| 37 | + result = _fallback_baseof() | |
| 38 | + self.assertIn('block "main"', result) | |
| 39 | + self.assertIn('partial "header.html"', result) | |
| 40 | + self.assertIn('partial "footer.html"', result) | |
| 41 | + self.assertIn("<!DOCTYPE html>", result) | |
| 42 | + | |
| 43 | + def test_contains_language_code(self): | |
| 44 | + result = _fallback_baseof() | |
| 45 | + self.assertIn(".Site.LanguageCode", result) | |
| 46 | + | |
| 47 | + | |
| 48 | +class TestHugoifyDir(unittest.TestCase): | |
| 49 | + def test_valid_theme_passes(self): | |
| 50 | + with tempfile.TemporaryDirectory() as tmp: | |
| 51 | + baseof = os.path.join(tmp, "layouts", "_default", "baseof.html") | |
| 52 | + os.makedirs(os.path.dirname(baseof)) | |
| 53 | + open(baseof, "w").close() | |
| 54 | + result = hugoify_dir(tmp) | |
| 55 | + self.assertIn("Valid", result) | |
| 56 | + | |
| 57 | + def test_missing_layouts_fails(self): | |
| 58 | + with tempfile.TemporaryDirectory() as tmp: | |
| 59 | + result = hugoify_dir(tmp) | |
| 60 | + self.assertIn("failed", result.lower()) | |
| 61 | + | |
| 62 | + def test_missing_baseof_reports_issue(self): | |
| 63 | + with tempfile.TemporaryDirectory() as tmp: | |
| 64 | + os.makedirs(os.path.join(tmp, "layouts", "_default")) | |
| 65 | + result = hugoify_dir(tmp) | |
| 66 | + self.assertIn("baseof.html", result) | |
| 67 | + | |
| 68 | + | |
| 69 | +if __name__ == "__main__": | |
| 70 | + unittest.main() | |
| 20 | 71 |
| --- tests/test_hugoify.py | |
| +++ tests/test_hugoify.py | |
| @@ -1,19 +1,70 @@ | |
| 1 | import unittest |
| 2 | from src.utils.hugoify import hugoify |
| 3 | |
| 4 | class TestHugoify(unittest.TestCase): |
| 5 | |
| 6 | def setUp(self): |
| 7 | # Set up any necessary test data |
| 8 | pass |
| 9 | |
| 10 | def tearDown(self): |
| 11 | # Clean up after tests |
| 12 | pass |
| 13 | |
| 14 | def test_hugoify(self): |
| 15 | # Placeholder for hugoify function test |
| 16 | pass |
| 17 | |
| 18 | if __name__ == '__main__': |
| 19 | unittest.main() |
| 20 |
| --- tests/test_hugoify.py | |
| +++ tests/test_hugoify.py | |
| @@ -1,19 +1,70 @@ | |
| 1 | """Tests for utils.hugoify.""" |
| 2 | import os |
| 3 | import tempfile |
| 4 | import unittest |
| 5 | |
| 6 | from utils.hugoify import _parse_layout_json, _fallback_baseof, hugoify_dir |
| 7 | |
| 8 | |
| 9 | class TestParseLayoutJson(unittest.TestCase): |
| 10 | def test_parses_valid_json(self): |
| 11 | response = '{"_default/baseof.html": "<!doctype html>", "index.html": "{{ define \\"main\\" }}{{ end }}"}' |
| 12 | result = _parse_layout_json(response) |
| 13 | self.assertIn("_default/baseof.html", result) |
| 14 | self.assertEqual(result["_default/baseof.html"], "<!doctype html>") |
| 15 | |
| 16 | def test_extracts_json_from_prose(self): |
| 17 | response = 'Here is the converted theme:\n{"_default/baseof.html": "<html></html>"}\nDone.' |
| 18 | result = _parse_layout_json(response) |
| 19 | self.assertIn("_default/baseof.html", result) |
| 20 | |
| 21 | def test_falls_back_on_invalid_json(self): |
| 22 | result = _parse_layout_json("This is not JSON at all.") |
| 23 | self.assertIn("_default/baseof.html", result) |
| 24 | self.assertIn("partials/header.html", result) |
| 25 | self.assertIn("partials/footer.html", result) |
| 26 | self.assertIn("index.html", result) |
| 27 | |
| 28 | def test_fallback_contains_valid_hugo_syntax(self): |
| 29 | result = _parse_layout_json("not json") |
| 30 | baseof = result["_default/baseof.html"] |
| 31 | self.assertIn("block", baseof) |
| 32 | self.assertIn("partial", baseof) |
| 33 | |
| 34 | |
| 35 | class TestFallbackBaseof(unittest.TestCase): |
| 36 | def test_contains_required_hugo_blocks(self): |
| 37 | result = _fallback_baseof() |
| 38 | self.assertIn('block "main"', result) |
| 39 | self.assertIn('partial "header.html"', result) |
| 40 | self.assertIn('partial "footer.html"', result) |
| 41 | self.assertIn("<!DOCTYPE html>", result) |
| 42 | |
| 43 | def test_contains_language_code(self): |
| 44 | result = _fallback_baseof() |
| 45 | self.assertIn(".Site.LanguageCode", result) |
| 46 | |
| 47 | |
| 48 | class TestHugoifyDir(unittest.TestCase): |
| 49 | def test_valid_theme_passes(self): |
| 50 | with tempfile.TemporaryDirectory() as tmp: |
| 51 | baseof = os.path.join(tmp, "layouts", "_default", "baseof.html") |
| 52 | os.makedirs(os.path.dirname(baseof)) |
| 53 | open(baseof, "w").close() |
| 54 | result = hugoify_dir(tmp) |
| 55 | self.assertIn("Valid", result) |
| 56 | |
| 57 | def test_missing_layouts_fails(self): |
| 58 | with tempfile.TemporaryDirectory() as tmp: |
| 59 | result = hugoify_dir(tmp) |
| 60 | self.assertIn("failed", result.lower()) |
| 61 | |
| 62 | def test_missing_baseof_reports_issue(self): |
| 63 | with tempfile.TemporaryDirectory() as tmp: |
| 64 | os.makedirs(os.path.join(tmp, "layouts", "_default")) |
| 65 | result = hugoify_dir(tmp) |
| 66 | self.assertIn("baseof.html", result) |
| 67 | |
| 68 | |
| 69 | if __name__ == "__main__": |
| 70 | unittest.main() |
| 71 |
+18
-19
| --- tests/test_parser.py | ||
| +++ tests/test_parser.py | ||
| @@ -1,19 +1,18 @@ | ||
| 1 | -import unittest | |
| 2 | -from src.utils.parser import parse | |
| 3 | - | |
| 4 | -class TestParser(unittest.TestCase): | |
| 5 | - | |
| 6 | - def setUp(self): | |
| 7 | - # Set up any necessary test data | |
| 8 | - pass | |
| 9 | - | |
| 10 | - def tearDown(self): | |
| 11 | - # Clean up after tests | |
| 12 | - pass | |
| 13 | - | |
| 14 | - def test_parse(self): | |
| 15 | - # Placeholder for parse function test | |
| 16 | - pass | |
| 17 | - | |
| 18 | -if __name__ == '__main__': | |
| 19 | - unittest.main() | |
| 1 | +"""Tests for utils.parser.""" | |
| 2 | +import unittest | |
| 3 | + | |
| 4 | +from utils.parser import parse | |
| 5 | + | |
| 6 | + | |
| 7 | +class TestParse(unittest.TestCase): | |
| 8 | + def test_returns_complete_message(self): | |
| 9 | + result = parse("/some/path") | |
| 10 | + self.assertIn("complete", result.lower()) | |
| 11 | + | |
| 12 | + def test_accepts_path(self): | |
| 13 | + result = parse("/tmp/site") | |
| 14 | + self.assertIsInstance(result, str) | |
| 15 | + | |
| 16 | + | |
| 17 | +if __name__ == "__main__": | |
| 18 | + unittest.main() | |
| 20 | 19 | |
| 21 | 20 | ADDED tests/test_theme_finder.py |
| 22 | 21 | ADDED tests/test_theme_patcher.py |
| --- tests/test_parser.py | |
| +++ tests/test_parser.py | |
| @@ -1,19 +1,18 @@ | |
| 1 | import unittest |
| 2 | from src.utils.parser import parse |
| 3 | |
| 4 | class TestParser(unittest.TestCase): |
| 5 | |
| 6 | def setUp(self): |
| 7 | # Set up any necessary test data |
| 8 | pass |
| 9 | |
| 10 | def tearDown(self): |
| 11 | # Clean up after tests |
| 12 | pass |
| 13 | |
| 14 | def test_parse(self): |
| 15 | # Placeholder for parse function test |
| 16 | pass |
| 17 | |
| 18 | if __name__ == '__main__': |
| 19 | unittest.main() |
| 20 | |
| 21 | DDED tests/test_theme_finder.py |
| 22 | DDED tests/test_theme_patcher.py |
| --- tests/test_parser.py | |
| +++ tests/test_parser.py | |
| @@ -1,19 +1,18 @@ | |
| 1 | """Tests for utils.parser.""" |
| 2 | import unittest |
| 3 | |
| 4 | from utils.parser import parse |
| 5 | |
| 6 | |
| 7 | class TestParse(unittest.TestCase): |
| 8 | def test_returns_complete_message(self): |
| 9 | result = parse("/some/path") |
| 10 | self.assertIn("complete", result.lower()) |
| 11 | |
| 12 | def test_accepts_path(self): |
| 13 | result = parse("/tmp/site") |
| 14 | self.assertIsInstance(result, str) |
| 15 | |
| 16 | |
| 17 | if __name__ == "__main__": |
| 18 | unittest.main() |
| 19 | |
| 20 | DDED tests/test_theme_finder.py |
| 21 | DDED tests/test_theme_patcher.py |
| --- a/tests/test_theme_finder.py | ||
| +++ b/tests/test_theme_finder.py | ||
| @@ -0,0 +1,37 @@ | ||
| 1 | +"""Tests for utils.theme_finder.""" | |
| 2 | +import os | |
| 3 | +import tempfile | |
| 4 | +import unittest | |
| 5 | + | |
| 6 | +from utils.theme_finder import find_hugo_theme, find_raw_html_files | |
| 7 | + | |
| 8 | + | |
| 9 | +def _make_hugo_theme(base_dir, theme_name="test-theme"): | |
| 10 | + """Create a minimal Hugo theme structure.""" | |
| 11 | + layouts = os.path.join(base_dir, theme_name, "layouts", "_default") | |
| 12 | + os.makedirs(layouts) | |
| 13 | + open(os.path.join(layouts, "baseof.html"), "w").close() | |
| 14 | + return os.path.join(base_dir, theme_name) | |
| 15 | + | |
| 16 | + | |
| 17 | +class TestFindHugoTheme(unittest.TestCase): | |
| 18 | + def test_finds_theme_with_layouts(self): | |
| 19 | + with tempfile.TemporaryDirectory() as tmp: | |
| 20 | + theme_path = _make_hugo_theme(tmp) | |
| 21 | + result = find_hugo_theme(theme_path) | |
| 22 | + self.assertIsNotNone(result) | |
| 23 | + self.assertTrue(result["is_hugo_theme"]) | |
| 24 | + self.assertEqual(result["theme_name"], "test-theme") | |
| 25 | + | |
| 26 | + def test_finds_example_site(self): | |
| 27 | + with tempfile.TemporaryDirectory() as tmp: | |
| 28 | + theme_path = _make_hugo_theme(tmp) | |
| 29 | + example = os.path.join(theme_path, "exampleSite") | |
| 30 | + os.makedirs(example) | |
| 31 | + result = find_hugo_theme(theme_path) | |
| 32 | + self.assertEqual(result["example_site"], example) | |
| 33 | + | |
| 34 | + def test_returns_none_for_non_hugo_dir(self): | |
| 35 | + with tempfile.TemporaryDirectory() as tmp: | |
| 36 | + os.makedirs(os.path.join(tmp, "css")) | |
| 37 | + |
| --- a/tests/test_theme_finder.py | |
| +++ b/tests/test_theme_finder.py | |
| @@ -0,0 +1,37 @@ | |
| --- a/tests/test_theme_finder.py | |
| +++ b/tests/test_theme_finder.py | |
| @@ -0,0 +1,37 @@ | |
| 1 | """Tests for utils.theme_finder.""" |
| 2 | import os |
| 3 | import tempfile |
| 4 | import unittest |
| 5 | |
| 6 | from utils.theme_finder import find_hugo_theme, find_raw_html_files |
| 7 | |
| 8 | |
| 9 | def _make_hugo_theme(base_dir, theme_name="test-theme"): |
| 10 | """Create a minimal Hugo theme structure.""" |
| 11 | layouts = os.path.join(base_dir, theme_name, "layouts", "_default") |
| 12 | os.makedirs(layouts) |
| 13 | open(os.path.join(layouts, "baseof.html"), "w").close() |
| 14 | return os.path.join(base_dir, theme_name) |
| 15 | |
| 16 | |
| 17 | class TestFindHugoTheme(unittest.TestCase): |
| 18 | def test_finds_theme_with_layouts(self): |
| 19 | with tempfile.TemporaryDirectory() as tmp: |
| 20 | theme_path = _make_hugo_theme(tmp) |
| 21 | result = find_hugo_theme(theme_path) |
| 22 | self.assertIsNotNone(result) |
| 23 | self.assertTrue(result["is_hugo_theme"]) |
| 24 | self.assertEqual(result["theme_name"], "test-theme") |
| 25 | |
| 26 | def test_finds_example_site(self): |
| 27 | with tempfile.TemporaryDirectory() as tmp: |
| 28 | theme_path = _make_hugo_theme(tmp) |
| 29 | example = os.path.join(theme_path, "exampleSite") |
| 30 | os.makedirs(example) |
| 31 | result = find_hugo_theme(theme_path) |
| 32 | self.assertEqual(result["example_site"], example) |
| 33 | |
| 34 | def test_returns_none_for_non_hugo_dir(self): |
| 35 | with tempfile.TemporaryDirectory() as tmp: |
| 36 | os.makedirs(os.path.join(tmp, "css")) |
| 37 |
| --- a/tests/test_theme_patcher.py | ||
| +++ b/tests/test_theme_patcher.py | ||
| @@ -0,0 +1,60 @@ | ||
| 1 | +"""Tests for utils.theme_patcher.""" | |
| 2 | +import os | |
| 3 | +import tempfile | |
| 4 | +import unittest | |
| 5 | + | |
| 6 | +from utils.theme_patcher import patch_theme, patch_config | |
| 7 | + | |
| 8 | + | |
| 9 | +class TestPatchtch_theme | |
| 10 | + | |
| 11 | + | |
| 12 | +class TestPatchTheme(unittest.TestCase): | |
| 13 | + def _write_layout(self, layouts_dir, name, content): | |
| 14 | + path = os.path.join(layouts_dir, name) | |
| 15 | + with open(path, "w") as f: | |
| 16 | + f.write(content) | |
| 17 | + return path | |
| 18 | + | |
| 19 | + def test_patches_disqus_shortname(self): | |
| 20 | + with tempfile.TemporaryDirectory() as tmp: | |
| 21 | + layouts = os.path.join(tmp, "layouts") | |
| 22 | + os.makedirs(layouts) | |
| 23 | + self._write_layout(layouts, "single.html", "{{ .Site.DisqusShortname }}") | |
| 24 | + patch_theme(tmp) | |
| 25 | + with open(os.path.join(layouts, "single.html")) as f: | |
| 26 | + result = f.read() | |
| 27 | + self.assertIn(".Site.Config.Services.Disqus.Shortname", result) | |
| 28 | + self.assertNotIn(".Site.DisqusShortname", result) | |
| 29 | + | |
| 30 | + def test_patches_google_analytics(self): | |
| 31 | + with tempfile.TemporaryDirectory() as tmp: | |
| 32 | + layouts = os.path.join(tmp, "layouts") | |
| 33 | + os.makedirs(layouts) | |
| 34 | + self._write_layout(layouts, "head.html", "{{ .Site.GoogleAnalytics }}") | |
| 35 | + patch_theme(tmp) | |
| 36 | + with open(os.path.join(layouts, "head.html")) as f: | |
| 37 | + result = f.read() | |
| 38 | + self.assertIn(".Site.Config.Services.GoogleAnalytics.ID", result) | |
| 39 | + | |
| 40 | + def test_no_change_when_already_patched(self): | |
| 41 | + with tempfile.TemporaryDirectory() as tmp: | |
| 42 | + layouts = os.path.join(tmp, "layouts") | |
| 43 | + os.makedirs(layouts) | |
| 44 | + content = "{{ .Site.Config.Services.Disqus.Shortname }}" | |
| 45 | + self._write_layout(layouts, "single.html", content) | |
| 46 | + patch_theme(tmp) | |
| 47 | + with open(os.path.join(layouts, "single.html")) as f: | |
| 48 | + result = f.read() | |
| 49 | + self.assertEqual(result, content) | |
| 50 | + | |
| 51 | + def test_no_layouts_dir_is_safe(self): | |
| 52 | + with tempfile.TemporaryDirectory() as tmp: | |
| 53 | + # Should not raise even if layouts/ doesn't exist | |
| 54 | + patch_theme(tmp) | |
| 55 | + | |
| 56 | + | |
| 57 | +class TestPatchConfig(unittest.TestCase): | |
| 58 | + def _write_config(self, path, content): | |
| 59 | + with open(path, "w") as f: | |
| 60 | + |
| --- a/tests/test_theme_patcher.py | |
| +++ b/tests/test_theme_patcher.py | |
| @@ -0,0 +1,60 @@ | |
| --- a/tests/test_theme_patcher.py | |
| +++ b/tests/test_theme_patcher.py | |
| @@ -0,0 +1,60 @@ | |
| 1 | """Tests for utils.theme_patcher.""" |
| 2 | import os |
| 3 | import tempfile |
| 4 | import unittest |
| 5 | |
| 6 | from utils.theme_patcher import patch_theme, patch_config |
| 7 | |
| 8 | |
| 9 | class TestPatchtch_theme |
| 10 | |
| 11 | |
| 12 | class TestPatchTheme(unittest.TestCase): |
| 13 | def _write_layout(self, layouts_dir, name, content): |
| 14 | path = os.path.join(layouts_dir, name) |
| 15 | with open(path, "w") as f: |
| 16 | f.write(content) |
| 17 | return path |
| 18 | |
| 19 | def test_patches_disqus_shortname(self): |
| 20 | with tempfile.TemporaryDirectory() as tmp: |
| 21 | layouts = os.path.join(tmp, "layouts") |
| 22 | os.makedirs(layouts) |
| 23 | self._write_layout(layouts, "single.html", "{{ .Site.DisqusShortname }}") |
| 24 | patch_theme(tmp) |
| 25 | with open(os.path.join(layouts, "single.html")) as f: |
| 26 | result = f.read() |
| 27 | self.assertIn(".Site.Config.Services.Disqus.Shortname", result) |
| 28 | self.assertNotIn(".Site.DisqusShortname", result) |
| 29 | |
| 30 | def test_patches_google_analytics(self): |
| 31 | with tempfile.TemporaryDirectory() as tmp: |
| 32 | layouts = os.path.join(tmp, "layouts") |
| 33 | os.makedirs(layouts) |
| 34 | self._write_layout(layouts, "head.html", "{{ .Site.GoogleAnalytics }}") |
| 35 | patch_theme(tmp) |
| 36 | with open(os.path.join(layouts, "head.html")) as f: |
| 37 | result = f.read() |
| 38 | self.assertIn(".Site.Config.Services.GoogleAnalytics.ID", result) |
| 39 | |
| 40 | def test_no_change_when_already_patched(self): |
| 41 | with tempfile.TemporaryDirectory() as tmp: |
| 42 | layouts = os.path.join(tmp, "layouts") |
| 43 | os.makedirs(layouts) |
| 44 | content = "{{ .Site.Config.Services.Disqus.Shortname }}" |
| 45 | self._write_layout(layouts, "single.html", content) |
| 46 | patch_theme(tmp) |
| 47 | with open(os.path.join(layouts, "single.html")) as f: |
| 48 | result = f.read() |
| 49 | self.assertEqual(result, content) |
| 50 | |
| 51 | def test_no_layouts_dir_is_safe(self): |
| 52 | with tempfile.TemporaryDirectory() as tmp: |
| 53 | # Should not raise even if layouts/ doesn't exist |
| 54 | patch_theme(tmp) |
| 55 | |
| 56 | |
| 57 | class TestPatchConfig(unittest.TestCase): |
| 58 | def _write_config(self, path, content): |
| 59 | with open(path, "w") as f: |
| 60 |
+41
-16
| --- tests/test_translate.py | ||
| +++ tests/test_translate.py | ||
| @@ -1,19 +1,44 @@ | ||
| 1 | +"""Tests for utils.translate.""" | |
| 2 | +import os | |
| 3 | +import tempfile | |
| 1 | 4 | import unittest |
| 2 | -from src.utils.translate import translate | |
| 5 | +from unittest.mock import patch | |
| 6 | + | |
| 3 | 7 | |
| 4 | 8 | class TestTranslate(unittest.TestCase): |
| 5 | - | |
| 6 | - def setUp(self): | |
| 7 | - # Set up any necessary test data | |
| 8 | - pass | |
| 9 | - | |
| 10 | - def tearDown(self): | |
| 11 | - # Clean up after tests | |
| 12 | - pass | |
| 13 | - | |
| 14 | - def test_translate(self): | |
| 15 | - # Placeholder for translate function test | |
| 16 | - pass | |
| 17 | - | |
| 18 | -if __name__ == '__main__': | |
| 19 | - unittest.main() | |
| 9 | + def test_translates_file_content(self): | |
| 10 | + with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False) as f: | |
| 11 | + f.write("<p>Hello world</p>") | |
| 12 | + path = f.name | |
| 13 | + try: | |
| 14 | + from utils.translate import translate | |
| 15 | + with patch("utils.translate.call_ai", return_value="<p>Hola mundo</p>") as mock_ai: | |
| 16 | + result = translate(path, target_language="Spanish") | |
| 17 | + self.assertEqual(result, "<p>Hola mundo</p>") | |
| 18 | + call_args = mock_ai.call_args[0][0] | |
| 19 | + self.assertIn("Spanish", call_args) | |
| 20 | + self.assertIn("Hello world", call_args) | |
| 21 | + finally: | |
| 22 | + os.unlink(path) | |
| 23 | + | |
| 24 | + def test_uses_target_language_param(self): | |
| 25 | + with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False) as f: | |
| 26 | + f.write("<p>Bonjour</p>") | |
| 27 | + path = f.name | |
| 28 | + try: | |
| 29 | + from utils.translate import translate | |
| 30 | + with patch("utils.translate.call_ai", return_value="<p>Hallo</p>") as mock_ai: | |
| 31 | + translate(path, target_language="German") | |
| 32 | + call_args = mock_ai.call_args[0][0] | |
| 33 | + self.assertIn("German", call_args) | |
| 34 | + finally: | |
| 35 | + os.unlink(path) | |
| 36 | + | |
| 37 | + def test_returns_error_on_missing_file(self): | |
| 38 | + from utils.translate import translate | |
| 39 | + result = translate("/nonexistent/file.html") | |
| 40 | + self.assertIn("failed", result.lower()) | |
| 41 | + | |
| 42 | + | |
| 43 | +if __name__ == "__main__": | |
| 44 | + unittest.main() | |
| 20 | 45 |
| --- tests/test_translate.py | |
| +++ tests/test_translate.py | |
| @@ -1,19 +1,44 @@ | |
| 1 | import unittest |
| 2 | from src.utils.translate import translate |
| 3 | |
| 4 | class TestTranslate(unittest.TestCase): |
| 5 | |
| 6 | def setUp(self): |
| 7 | # Set up any necessary test data |
| 8 | pass |
| 9 | |
| 10 | def tearDown(self): |
| 11 | # Clean up after tests |
| 12 | pass |
| 13 | |
| 14 | def test_translate(self): |
| 15 | # Placeholder for translate function test |
| 16 | pass |
| 17 | |
| 18 | if __name__ == '__main__': |
| 19 | unittest.main() |
| 20 |
| --- tests/test_translate.py | |
| +++ tests/test_translate.py | |
| @@ -1,19 +1,44 @@ | |
| 1 | """Tests for utils.translate.""" |
| 2 | import os |
| 3 | import tempfile |
| 4 | import unittest |
| 5 | from unittest.mock import patch |
| 6 | |
| 7 | |
| 8 | class TestTranslate(unittest.TestCase): |
| 9 | def test_translates_file_content(self): |
| 10 | with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False) as f: |
| 11 | f.write("<p>Hello world</p>") |
| 12 | path = f.name |
| 13 | try: |
| 14 | from utils.translate import translate |
| 15 | with patch("utils.translate.call_ai", return_value="<p>Hola mundo</p>") as mock_ai: |
| 16 | result = translate(path, target_language="Spanish") |
| 17 | self.assertEqual(result, "<p>Hola mundo</p>") |
| 18 | call_args = mock_ai.call_args[0][0] |
| 19 | self.assertIn("Spanish", call_args) |
| 20 | self.assertIn("Hello world", call_args) |
| 21 | finally: |
| 22 | os.unlink(path) |
| 23 | |
| 24 | def test_uses_target_language_param(self): |
| 25 | with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False) as f: |
| 26 | f.write("<p>Bonjour</p>") |
| 27 | path = f.name |
| 28 | try: |
| 29 | from utils.translate import translate |
| 30 | with patch("utils.translate.call_ai", return_value="<p>Hallo</p>") as mock_ai: |
| 31 | translate(path, target_language="German") |
| 32 | call_args = mock_ai.call_args[0][0] |
| 33 | self.assertIn("German", call_args) |
| 34 | finally: |
| 35 | os.unlink(path) |
| 36 | |
| 37 | def test_returns_error_on_missing_file(self): |
| 38 | from utils.translate import translate |
| 39 | result = translate("/nonexistent/file.html") |
| 40 | self.assertIn("failed", result.lower()) |
| 41 | |
| 42 | |
| 43 | if __name__ == "__main__": |
| 44 | unittest.main() |
| 45 |