from __future__ import annotations import sys from pathlib import Path import unittest from unittest.mock import patch from urllib.error import HTTPError sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) from project_sources import ( # noqa: E402 ProjectSourceError, RepositoryNotPublicError, append_fields, collect_metadata, fetch_json, load_registry, metadata_digest, normalize_tag_value, parse_record, split_list, taxonomy_label_items, validate_image, validate_url, ) class ProjectSourceTests(unittest.TestCase): def test_registry_feature_order_is_optional_and_numeric(self) -> None: site_root = Path(__file__).resolve().parents[1] registry = {source["project_id"]: source for source in load_registry(site_root)} self.assertEqual(registry["thinkstorm"]["featured_order"], 0) self.assertNotIn("featured_order", registry["thinkloom"]) def test_registry_urls_must_be_public_https_without_credentials(self) -> None: self.assertEqual( validate_url("https://git.example.test/owner/repo.git", "repository"), "https://git.example.test/owner/repo.git", ) for value in ( "http://git.example.test/owner/repo.git", "https://token@git.example.test/owner/repo.git", "https://127.0.0.1/owner/repo.git", ): with self.subTest(value=value), self.assertRaises(ProjectSourceError): validate_url(value, "repository") def test_remote_comma_lists_become_lektor_multiline_values(self) -> None: fields = split_list("Tauri, React, Rust\nSQLite") rendered = append_fields(b"_model: project\n", {"technology_tags": fields}) self.assertIn(b"technology_tags:\n\nTauri\nReact\nRust\nSQLite\n", rendered) def test_metadata_digest_ignores_local_mirror_path(self) -> None: first = metadata_digest({"commit": "a" * 40, "mirror_path": "/one"}) second = metadata_digest({"commit": "a" * 40, "mirror_path": "/two"}) self.assertEqual(first, second) def test_image_extension_must_match_magic_bytes(self) -> None: with self.assertRaises(ProjectSourceError): validate_image("logo.png", b"not a png") def test_raw_html_is_rejected_from_remote_markdown(self) -> None: record = b"_model: devlog-entry\n---\nbody: \n" with self.assertRaises(ProjectSourceError): parse_record(record, ".labyricorn/devlog/example/contents.lr") def test_private_or_hidden_repository_response_is_fatal(self) -> None: error = HTTPError("https://git.example.test/api/repo", 404, "Not Found", {}, None) with patch("project_sources.urlopen", side_effect=error): with self.assertRaises(RepositoryNotPublicError): fetch_json("https://git.example.test/api/repo", require_public=True) def test_github_anonymous_rate_limit_is_not_reported_as_private(self) -> None: error = HTTPError( "https://api.github.com/repos/example/project", 403, "Forbidden", {"X-RateLimit-Remaining": "0"}, None, ) with patch("project_sources.urlopen", side_effect=error): with self.assertRaisesRegex(ProjectSourceError, "rate limit"): fetch_json( "https://api.github.com/repos/example/project", require_public=True, ) def test_unapproved_labels_remain_visible_without_becoming_tags(self) -> None: items = taxonomy_label_items( ["TypeScript", "Unlisted Tool"], ["typescript"] ) self.assertEqual(items, ["typescript\tTypeScript", "-\tUnlisted Tool"]) def test_devlog_topic_versions_get_stable_tag_slugs(self) -> None: self.assertEqual(normalize_tag_value("v0.4.0"), "v0-4-0") self.assertEqual(normalize_tag_value("OpenAI Build Week"), "openai-build-week") def test_github_public_metadata_is_normalized_for_project_templates(self) -> None: source = { "project_id": "example", "repository": "https://github.com/example/project.git", "web_url": "https://github.com/example/project", "api_url": "https://api.github.com/repos/example/project", "branch": "main", } responses = ( { "private": False, "default_branch": "main", "open_issues_count": 7, "stargazers_count": 42, "forks_count": 5, }, {"Python": 100, "JavaScript": 25}, {"tag_name": "v1.0.0", "html_url": "https://github.com/example/project/releases/tag/v1.0.0"}, ) with ( patch("project_sources.commit_metadata", return_value={"commit": "a" * 40}), patch("project_sources.detect_license", return_value="MIT"), patch("project_sources.fetch_json", side_effect=responses), ): metadata, current = collect_metadata(source, Path("unused.git"), "a" * 40, None) self.assertTrue(current) self.assertEqual(metadata["stars"], 42) self.assertEqual(metadata["forks"], 5) self.assertEqual(metadata["languages"], ["Python", "JavaScript"]) self.assertEqual( metadata["readme_url"], "https://github.com/example/project/blob/main/README.md", ) if __name__ == "__main__": unittest.main()