test(slice3): integration tests for faceted list endpoint (§22.4a PUC-3)
This commit is contained in:
@@ -0,0 +1,134 @@
|
|||||||
|
"""§22.4a SLICE-3 integration — faceted list endpoint (PUC-3, §6.4).
|
||||||
|
|
||||||
|
Through the real API: schema-declared facets, filter params (OR within / AND
|
||||||
|
across), drill-down counts, malformed toggle, unknown-field 400, and meta_json
|
||||||
|
persistence. Reuses the fake-Gitea harness from test_metadata_cache.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from app import cache, db, gitea as gitea_mod
|
||||||
|
from app.config import load_config
|
||||||
|
|
||||||
|
from test_propose_vertical import ( # noqa: F401 (fixtures)
|
||||||
|
app_with_fake_gitea,
|
||||||
|
tmp_env,
|
||||||
|
)
|
||||||
|
|
||||||
|
# The fake-Gitea harness seeds a single project whose id is the literal
|
||||||
|
# 'default' (see test_s1_collection_grain_vertical), served at
|
||||||
|
# /api/projects/default/rfcs.
|
||||||
|
PID = "default"
|
||||||
|
|
||||||
|
|
||||||
|
def _refresh():
|
||||||
|
cfg = load_config()
|
||||||
|
asyncio.run(cache.refresh_meta_repo(cfg, gitea_mod.Gitea(cfg)))
|
||||||
|
|
||||||
|
|
||||||
|
def _set_default_fields_schema(schema):
|
||||||
|
db.conn().execute(
|
||||||
|
"UPDATE collections SET config_json = ? WHERE id = 'default'",
|
||||||
|
(json.dumps({"fields": schema}),))
|
||||||
|
|
||||||
|
|
||||||
|
def _seed(fake, slug, *, state="active", **front):
|
||||||
|
fm = {"slug": slug, "title": slug.title(), "state": state, **front}
|
||||||
|
body = yaml.safe_dump(fm, sort_keys=False).strip()
|
||||||
|
fake.files[("wiggleverse", "meta", "main", f"rfcs/{slug}.md")] = {
|
||||||
|
"content": f"---\n{body}\n---\n\nBody.\n", "sha": slug}
|
||||||
|
|
||||||
|
|
||||||
|
def test_facets_and_counts_returned(app_with_fake_gitea):
|
||||||
|
app, fake = app_with_fake_gitea
|
||||||
|
with TestClient(app) as client:
|
||||||
|
_set_default_fields_schema({
|
||||||
|
"priority": {"type": "enum", "values": ["P0", "P1", "P2"]},
|
||||||
|
"tags": {"type": "tags"},
|
||||||
|
})
|
||||||
|
_seed(fake, "a", priority="P0", tags=["checkout"])
|
||||||
|
_seed(fake, "b", priority="P0", tags=["cart"])
|
||||||
|
_seed(fake, "c", priority="P1", tags=["checkout", "cart"])
|
||||||
|
_refresh()
|
||||||
|
|
||||||
|
res = client.get(f"/api/projects/{PID}/rfcs")
|
||||||
|
assert res.status_code == 200
|
||||||
|
body = res.json()
|
||||||
|
assert {i["slug"] for i in body["items"]} == {"a", "b", "c"}
|
||||||
|
assert body["facets"]["priority"] == {"P0": 2, "P1": 1}
|
||||||
|
assert body["facets"]["tags"] == {"checkout": 2, "cart": 2}
|
||||||
|
assert body["facets"]["state"] == {"active": 3}
|
||||||
|
|
||||||
|
|
||||||
|
def test_filter_params_compose(app_with_fake_gitea):
|
||||||
|
app, fake = app_with_fake_gitea
|
||||||
|
with TestClient(app) as client:
|
||||||
|
_set_default_fields_schema({
|
||||||
|
"priority": {"type": "enum", "values": ["P0", "P1"]},
|
||||||
|
"tags": {"type": "tags"},
|
||||||
|
})
|
||||||
|
_seed(fake, "a", priority="P0", tags=["checkout"])
|
||||||
|
_seed(fake, "b", priority="P0", tags=["cart"])
|
||||||
|
_seed(fake, "c", priority="P1", tags=["checkout"])
|
||||||
|
_refresh()
|
||||||
|
|
||||||
|
res = client.get(
|
||||||
|
f"/api/projects/{PID}/rfcs",
|
||||||
|
params={"priority": "P0", "tags": "checkout"})
|
||||||
|
assert {i["slug"] for i in res.json()["items"]} == {"a"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_unknown_filter_field_400(app_with_fake_gitea):
|
||||||
|
app, fake = app_with_fake_gitea
|
||||||
|
with TestClient(app) as client:
|
||||||
|
_set_default_fields_schema(
|
||||||
|
{"priority": {"type": "enum", "values": ["P0"]}})
|
||||||
|
_seed(fake, "a", priority="P0")
|
||||||
|
_refresh()
|
||||||
|
|
||||||
|
res = client.get(f"/api/projects/{PID}/rfcs",
|
||||||
|
params={"nonsense": "x"})
|
||||||
|
assert res.status_code == 400
|
||||||
|
|
||||||
|
|
||||||
|
def test_malformed_toggle(app_with_fake_gitea):
|
||||||
|
app, fake = app_with_fake_gitea
|
||||||
|
with TestClient(app) as client:
|
||||||
|
_set_default_fields_schema(
|
||||||
|
{"priority": {"type": "enum", "values": ["P0", "P1"]}})
|
||||||
|
_seed(fake, "good", priority="P0")
|
||||||
|
_seed(fake, "bad", priority="P9") # not in values → malformed (INV-3)
|
||||||
|
_refresh()
|
||||||
|
|
||||||
|
res = client.get(f"/api/projects/{PID}/rfcs",
|
||||||
|
params={"malformed": "true"})
|
||||||
|
slugs = {i["slug"] for i in res.json()["items"]}
|
||||||
|
assert slugs == {"bad"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_schema_no_facets(app_with_fake_gitea):
|
||||||
|
# INV-5: the default document collection (no fields) returns empty facets.
|
||||||
|
app, fake = app_with_fake_gitea
|
||||||
|
with TestClient(app) as client:
|
||||||
|
_seed(fake, "plain", tags=["whatever"])
|
||||||
|
_refresh()
|
||||||
|
body = client.get(f"/api/projects/{PID}/rfcs").json()
|
||||||
|
assert body["facets"] == {}
|
||||||
|
|
||||||
|
|
||||||
|
def test_meta_json_persisted_at_ingest(app_with_fake_gitea):
|
||||||
|
app, fake = app_with_fake_gitea
|
||||||
|
with TestClient(app):
|
||||||
|
_seed(fake, "withmeta", priority="P0", tags=["x"])
|
||||||
|
_refresh()
|
||||||
|
row = db.conn().execute(
|
||||||
|
"SELECT meta_json FROM cached_rfcs WHERE slug = 'withmeta'"
|
||||||
|
).fetchone()
|
||||||
|
meta = json.loads(row["meta_json"])
|
||||||
|
assert meta["priority"] == "P0"
|
||||||
|
assert meta["tags"] == ["x"]
|
||||||
Reference in New Issue
Block a user