"""Tests for the enhanced /search/aliases/ endpoint (search-driven library).""" import pytest from django.test import Client from django.urls import reverse from links.models import Link, Tag @pytest.fixture def db_links(db): """A small, deterministic link library for search tests.""" l1 = Link.objects.create( alias="github", original_url="https://github.com", link_type="LINK", click_count=5, description="Code hosting", ) l2 = Link.objects.create( alias="gitlab", original_url="https://gitlab.com", link_type="LINK", click_count=2, description="Another code host", ) l3 = Link.objects.create( alias="docs", original_url="https://docs.example.com/{page,default=index}", link_type="LINK", click_count=0, description="Template link", ) l4 = Link.objects.create( alias="notify", original_url="", link_type="ACTION", action_config={"action_type": "discord_send"}, click_count=0, ) tag = Tag.objects.create(name="dev") l1.tags.add(tag) return [l1, l2, l3, l4] @pytest.mark.django_db def test_search_aliases_matches_alias_and_returns_enriched_fields(client, db_links): resp = client.get(reverse("search_aliases"), {"q": "git"}) assert resp.status_code == 200 data = resp.json() aliases = {item["alias"] for item in data} assert aliases == {"github", "gitlab"} github = next(i for i in data if i["alias"] == "github") # Enriched management fields are present and correct assert github["id"] == db_links[0].id assert github["link_type"] == "LINK" assert github["detail_url"] == f"/link/{db_links[0].id}/" assert github["edit_url"] == f"/link/{db_links[0].id}/edit/" assert github["click_count"] == 5 assert github["description"] == "Code hosting" assert github["original_url"] == "https://github.com" # Backwards-compatible fields (hero autocomplete relies on these) assert github["url"].endswith("/github/") @pytest.mark.django_db def test_search_aliases_matches_original_url(client, db_links): resp = client.get(reverse("search_aliases"), {"q": "gitlab.com"}) assert resp.status_code == 200 assert [i["alias"] for i in resp.json()] == ["gitlab"] @pytest.mark.django_db def test_search_aliases_matches_description(client, db_links): resp = client.get(reverse("search_aliases"), {"q": "template link"}) assert resp.status_code == 200 assert [i["alias"] for i in resp.json()] == ["docs"] @pytest.mark.django_db def test_search_aliases_matches_tag_name(client, db_links): resp = client.get(reverse("search_aliases"), {"q": "dev"}) assert resp.status_code == 200 assert [i["alias"] for i in resp.json()] == ["github"] @pytest.mark.django_db def test_search_aliases_priority_exact_first(client, db_links): resp = client.get(reverse("search_aliases"), {"q": "github"}) assert resp.status_code == 200 assert resp.json()[0]["alias"] == "github" @pytest.mark.django_db def test_search_aliases_empty_query_returns_recent(client, db_links): # Touch l2 so it becomes the most recently updated (auto_now needs the # field explicitly listed in update_fields to refresh). db_links[1].click_count = 3 db_links[1].save(update_fields=["click_count", "updated_at"]) resp = client.get(reverse("search_aliases")) assert resp.status_code == 200 data = resp.json() assert len(data) >= 4 assert data[0]["alias"] == "gitlab" # most recently updated first @pytest.mark.django_db def test_search_aliases_action_type_surfaces(client, db_links): resp = client.get(reverse("search_aliases"), {"q": "notify"}) assert resp.status_code == 200 item = resp.json()[0] assert item["link_type"] == "ACTION" assert item["detail_url"] == f"/link/{db_links[3].id}/" @pytest.mark.django_db def test_search_aliases_no_results(client, db_links): resp = client.get(reverse("search_aliases"), {"q": "zzzznothing"}) assert resp.status_code == 200 assert resp.json() == [] # ---------------- /search/palette/ (⌘K command palette) ---------------- @pytest.mark.django_db def test_palette_groups_links_pages_posts(client, db_links): from links.models import Page, Post Page.objects.create(url="https://github.com/about", title="About GitHub", summary="dev") Post.objects.create(title="Weekly digest", summary="dev roundup", content="body") resp = client.get(reverse("search_palette"), {"q": "github"}) assert resp.status_code == 200 data = resp.json() # links group matches alias/url; pages group matches title/url assert any(i["alias"] == "github" for i in data["links"]) assert any(i["title"] == "About GitHub" for i in data["pages"]) # posts group does not match "github" assert data["posts"] == [] @pytest.mark.django_db def test_palette_matches_posts(client, db_links): from links.models import Post Post.objects.create(title="Weekly digest", summary="dev roundup", content="body") resp = client.get(reverse("search_palette"), {"q": "digest"}) data = resp.json() assert any(i["title"] == "Weekly digest" for i in data["posts"]) assert data["posts"][0]["edit_url"].startswith("/ui/posts/") @pytest.mark.django_db def test_palette_link_entry_has_management_fields(client, db_links): resp = client.get(reverse("search_palette"), {"q": "notify"}) data = resp.json() item = next(i for i in data["links"] if i["alias"] == "notify") assert item["link_type"] == "ACTION" assert item["detail_url"] == f"/link/{db_links[3].id}/" assert item["edit_url"] == f"/link/{db_links[3].id}/edit/" @pytest.mark.django_db def test_palette_empty_query_returns_recent_links(client, db_links): resp = client.get(reverse("search_palette")) data = resp.json() assert len(data["links"]) >= 4 assert data["pages"] == [] assert data["posts"] == [] @pytest.mark.django_db def test_palette_no_results(client, db_links): resp = client.get(reverse("search_palette"), {"q": "zzzznothing"}) data = resp.json() assert data == {"links": [], "pages": [], "posts": []}