build-small-hackathon/hackathon-advisor
16
1from __future__ import annotations2 3from types import SimpleNamespace4 5import pytest6 7from scripts import crawl_hf_spaces8 9 10def test_readme_frontmatter_extracts_app_file() -> None:11 frontmatter = crawl_hf_spaces.readme_frontmatter(12 """---13title: Tiny Demo14app_file: "src/app.py" # main entrypoint15tags:16 - gradio17---18# Tiny Demo19"""20 )21 22 assert frontmatter["app_file"] == "src/app.py"23 24 25def test_validate_app_file_rejects_untrusted_paths() -> None:26 with pytest.raises(RuntimeError, match="invalid app_file path"):27 crawl_hf_spaces.validate_app_file("../app.py", space_id="build-small-hackathon/demo")28 29 30def test_project_from_space_downloads_frontmatter_app_file(monkeypatch) -> None:31 downloads = {32 ("build-small-hackathon/demo", "README.md"): "---\napp_file: app.py\n---\n# Demo\nREADME body evidence.\n",33 ("build-small-hackathon/demo", "app.py"): "import gradio as gr\ngr.Textbox(label='Idea')\n",34 }35 36 def fake_download(repo_id: str, filename: str) -> str:37 return downloads[(repo_id, filename)]38 39 monkeypatch.setattr(crawl_hf_spaces, "download_repo_text", fake_download)40 space = SimpleNamespace(41 id="build-small-hackathon/demo",42 card_data={"title": "Demo", "short_description": "Advisor demo", "sdk": "gradio"},43 siblings=[44 SimpleNamespace(rfilename="README.md"),45 SimpleNamespace(rfilename="app.py"),46 ],47 tags=["gradio", "region:us"],48 models=[],49 datasets=[],50 likes=3,51 created_at=None,52 last_modified=None,53 host="https://example.test",54 private=False,55 )56 57 project = crawl_hf_spaces.project_from_space(space)58 59 assert project["app_file"] == "app.py"60 assert project["readme_body"] == "# Demo\nREADME body evidence."61 assert project["app_file_source"] == "import gradio as gr\ngr.Textbox(label='Idea')\n"62 assert "gr.Textbox" in project["app_file_embedding_text"]63 assert "Idea" in project["app_file_embedding_text"]64 assert project["tags"] == ["gradio"]65 66 67def test_project_from_space_tolerates_stale_frontmatter_app_file(monkeypatch) -> None:68 downloads = {69 ("build-small-hackathon/demo", "README.md"): "---\napp_file: app.py\n---\n",70 }71 72 def fake_download(repo_id: str, filename: str) -> str:73 if (repo_id, filename) in downloads:74 return downloads[(repo_id, filename)]75 raise crawl_hf_spaces.EntryNotFoundError("missing file")76 77 monkeypatch.setattr(crawl_hf_spaces, "download_repo_text", fake_download)78 space = SimpleNamespace(79 id="build-small-hackathon/demo",80 card_data={"title": "Demo", "short_description": "Advisor demo", "sdk": "gradio"},81 siblings=[82 SimpleNamespace(rfilename="README.md"),83 SimpleNamespace(rfilename="app.py"),84 ],85 tags=["gradio"],86 models=[],87 datasets=[],88 likes=3,89 created_at=None,90 last_modified=None,91 host="https://example.test",92 private=False,93 )94 95 project = crawl_hf_spaces.project_from_space(space)96 97 assert project["app_file"] == "app.py"98 assert project["readme_body"] == ""99 assert project["app_file_source"] == ""100 assert project["app_file_embedding_text"] == ""101 102 103def test_download_repo_text_uses_bounded_raw_request(monkeypatch) -> None:104 calls: list[tuple[str, tuple[int, int]]] = []105 106 class Response:107 status_code = 200108 encoding = ""109 text = "hello"110 111 def raise_for_status(self) -> None:112 return None113 114 def fake_get(url: str, *, timeout: tuple[int, int]):115 calls.append((url, timeout))116 return Response()117 118 monkeypatch.setattr(crawl_hf_spaces.requests, "get", fake_get)119 120 text = crawl_hf_spaces.download_repo_text("build-small-hackathon/demo space", "src/app file.py")121 122 assert text == "hello"123 assert calls == [124 (125 "https://huggingface.co/spaces/build-small-hackathon/demo%20space/resolve/main/src/app%20file.py",126 crawl_hf_spaces.DOWNLOAD_TIMEOUT_SECONDS,127 )128 ]129 130 131def test_crawl_workers_rejects_non_integer_env(monkeypatch) -> None:132 monkeypatch.setenv("ADVISOR_CRAWL_WORKERS", "many")133 134 with pytest.raises(RuntimeError, match="ADVISOR_CRAWL_WORKERS"):135 crawl_hf_spaces.crawl_workers()136 