"""Tests for the build module.""" import json import os import shutil import sys import textwrap from pathlib import Path import pytest sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from build import ( build, count_entries, extract_github_repo, extract_preview, group_categories, load_stars, parse_readme, render_content_html, slugify, sort_entries, ) # --------------------------------------------------------------------------- # slugify # --------------------------------------------------------------------------- class TestSlugify: def test_simple(self): assert slugify("Admin Panels") == "admin-panels" def test_uppercase_acronym(self): assert slugify("RESTful API") == "restful-api" def test_all_caps(self): assert slugify("CMS") == "cms" def test_hyphenated_input(self): assert slugify("Command-line Tools") == "command-line-tools" def test_special_chars(self): assert slugify("Editor Plugins and IDEs") == "editor-plugins-and-ides" def test_single_word(self): assert slugify("Audio") == "audio" def test_extra_spaces(self): assert slugify(" Date and Time ") == "date-and-time" # --------------------------------------------------------------------------- # count_entries # --------------------------------------------------------------------------- class TestCountEntries: def test_counts_dash_entries(self): assert count_entries("- [a](url) - Desc.\n- [b](url) - Desc.") == 2 def test_counts_star_entries(self): assert count_entries("* [a](url) - Desc.") == 1 def test_ignores_non_entries(self): assert count_entries("Some text\n- [a](url) - Desc.\nMore text") == 1 def test_counts_indented_entries(self): assert count_entries(" - [a](url) - Desc.") == 1 def test_empty_content(self): assert count_entries("") == 0 # --------------------------------------------------------------------------- # extract_preview # --------------------------------------------------------------------------- class TestExtractPreview: def test_basic(self): content = "* [alpha](url) - A.\n* [beta](url) - B.\n* [gamma](url) - C." assert extract_preview(content) == "alpha, beta, gamma" def test_max_four(self): content = "\n".join(f"* [lib{i}](url) - Desc." for i in range(10)) assert extract_preview(content) == "lib0, lib1, lib2, lib3" def test_empty(self): assert extract_preview("") == "" def test_skips_subcategory_labels(self): content = "* Synchronous\n* [django](url) - Framework.\n* [flask](url) - Micro." assert extract_preview(content) == "django, flask" # --------------------------------------------------------------------------- # render_content_html # --------------------------------------------------------------------------- class TestRenderContentHtml: def test_basic_entry(self): content = "* [django](https://example.com) - A web framework." html = render_content_html(content) assert 'href="https://example.com"' in html assert "django" in html assert "A web framework." in html assert 'class="entry"' in html def test_subcategory_label(self): content = "* Synchronous\n* [django](https://x.com) - Framework." html = render_content_html(content) assert 'class="subcat"' in html assert "Synchronous" in html def test_sub_entry(self): content = "* [django](https://x.com) - Framework.\n * [awesome-django](https://y.com)" html = render_content_html(content) assert 'class="entry-sub"' in html assert "awesome-django" in html def test_link_only_entry(self): content = "* [tool](https://x.com)" html = render_content_html(content) assert 'href="https://x.com"' in html assert "tool" in html # --------------------------------------------------------------------------- # parse_readme # --------------------------------------------------------------------------- MINIMAL_README = textwrap.dedent("""\ # Awesome Python Some intro text. --- ## Alpha _Libraries for alpha stuff._ - [lib-a](https://example.com/a) - Does A. - [lib-b](https://example.com/b) - Does B. ## Beta _Tools for beta._ - [lib-c](https://example.com/c) - Does C. # Resources Where to discover resources. ## Newsletters - [News One](https://example.com/n1) - [News Two](https://example.com/n2) ## Podcasts - [Pod One](https://example.com/p1) # Contributing Please contribute! """) class TestParseReadme: def test_category_count(self): cats, resources = parse_readme(MINIMAL_README) assert len(cats) == 2 def test_resource_count(self): cats, resources = parse_readme(MINIMAL_README) assert len(resources) == 2 def test_category_names(self): cats, _ = parse_readme(MINIMAL_README) assert cats[0]["name"] == "Alpha" assert cats[1]["name"] == "Beta" def test_category_slugs(self): cats, _ = parse_readme(MINIMAL_README) assert cats[0]["slug"] == "alpha" assert cats[1]["slug"] == "beta" def test_category_description(self): cats, _ = parse_readme(MINIMAL_README) assert cats[0]["description"] == "Libraries for alpha stuff." assert cats[1]["description"] == "Tools for beta." def test_category_content_has_entries(self): cats, _ = parse_readme(MINIMAL_README) assert "lib-a" in cats[0]["content"] assert "lib-b" in cats[0]["content"] def test_resources_names(self): _, resources = parse_readme(MINIMAL_README) assert resources[0]["name"] == "Newsletters" assert resources[1]["name"] == "Podcasts" def test_resources_content(self): _, resources = parse_readme(MINIMAL_README) assert "News One" in resources[0]["content"] assert "Pod One" in resources[1]["content"] def test_contributing_skipped(self): cats, resources = parse_readme(MINIMAL_README) all_names = [c["name"] for c in cats] + [r["name"] for r in resources] assert "Contributing" not in all_names def test_no_separator(self): cats, resources = parse_readme("# Just a heading\n\nSome text.\n") assert cats == [] assert resources == [] def test_no_description(self): readme = textwrap.dedent("""\ # Title --- ## NullDesc - [item](https://x.com) - Thing. # Resources ## Tips - [tip](https://x.com) # Contributing Done. """) cats, resources = parse_readme(readme) assert cats[0]["description"] == "" assert "item" in cats[0]["content"] # --------------------------------------------------------------------------- # parse_readme on real README # --------------------------------------------------------------------------- class TestParseRealReadme: @pytest.fixture(autouse=True) def load_readme(self): readme_path = os.path.join(os.path.dirname(__file__), "..", "..", "README.md") with open(readme_path, encoding="utf-8") as f: self.readme_text = f.read() self.cats, self.resources = parse_readme(self.readme_text) def test_at_least_83_categories(self): assert len(self.cats) >= 83 def test_resources_has_newsletters_and_podcasts(self): names = [r["name"] for r in self.resources] assert "Newsletters" in names assert "Podcasts" in names def test_contributing_not_in_results(self): all_names = [c["name"] for c in self.cats] + [ r["name"] for r in self.resources ] assert "Contributing" not in all_names def test_first_category_is_admin_panels(self): assert self.cats[0]["name"] == "Admin Panels" assert self.cats[0]["slug"] == "admin-panels" def test_last_category_is_wsgi_servers(self): assert self.cats[-1]["name"] == "WSGI Servers" assert self.cats[-1]["slug"] == "wsgi-servers" def test_restful_api_slug(self): slugs = [c["slug"] for c in self.cats] assert "restful-api" in slugs def test_descriptions_extracted(self): admin = self.cats[0] assert admin["description"] == "Libraries for administrative interfaces." # --------------------------------------------------------------------------- # group_categories # --------------------------------------------------------------------------- class TestGroupCategories: def test_groups_known_categories(self): cats = [ {"name": "Web Frameworks", "slug": "web-frameworks"}, {"name": "Testing", "slug": "testing"}, ] groups = group_categories(cats, []) group_names = [g["name"] for g in groups] assert "Web & API" in group_names assert "Development Tools" in group_names def test_ungrouped_go_to_other(self): cats = [{"name": "Unknown Category", "slug": "unknown-category"}] groups = group_categories(cats, []) group_names = [g["name"] for g in groups] assert "Other" in group_names def test_resources_grouped(self): resources = [{"name": "Newsletters", "slug": "newsletters"}] groups = group_categories([], resources) group_names = [g["name"] for g in groups] assert "Resources" in group_names # --------------------------------------------------------------------------- # render_markdown (kept for compatibility) # --------------------------------------------------------------------------- class TestRenderMarkdown: def test_renders_link_list(self): from build import render_markdown html = render_markdown("- [lib](https://example.com) - Does stuff.") assert "
Hello world
" in html # --------------------------------------------------------------------------- # build (integration) # --------------------------------------------------------------------------- class TestBuild: def _make_repo(self, tmp_path, readme): (tmp_path / "README.md").write_text(readme, encoding="utf-8") tpl_dir = tmp_path / "website" / "templates" tpl_dir.mkdir(parents=True) (tpl_dir / "base.html").write_text( "