"""The discoveries bundle (goal 24): the fleet's global, EDITABLE knowledge base of non-project facts — the /api/saddle/discovery insert + update callbacks, the /api/saddle/discoveries search, the human edit/delete actions on the Discoveries page, and the newest rows riding the assignment payload.""" from harness import * # noqa: F401,F403 — the shared wire helpers def run(ctx): auth, fake, fake_id, fake_token = ctx.auth, ctx.fake, ctx.fake_id, ctx.fake_token def db_rows(sql, params=()): conn = db() try: return conn.execute(sql, params).fetchall() finally: conn.close() print("== discoveries: the migration ==") # A fresh database: M0001..M0005 ran at startup, so the table # exists with the goal-24 shape. tables = [r[0] for r in db_rows( "select name from sqlite_master where type = 'table'")] check("discoveries" in tables, "M0005 creates the discoveries table on a fresh DB") columns = [r[1] for r in db_rows("pragma table_info(discoveries)")] check(columns == ["id", "body", "source_task_id", "created_at", "updated_at"], "the table carries the goal-24 columns (%s)" % columns) print("== discoveries: POST auth + validation ==") status, _ = saddle_api("POST", "/api/saddle/discovery", None, {"body": "x"}) check(status == 401, "POST rejects a missing token") status, _ = saddle_api("POST", "/api/saddle/discovery", "saddle-bogus", {"body": "x"}) check(status == 401, "POST rejects a wrong token") status, body = saddle_api("POST", "/api/saddle/discovery", fake_token, {"body": " "}) check(status == 400 and body.get("ok") is False, "POST rejects an empty body") print("== discoveries: inserts and ordering ==") status, body = saddle_api("POST", "/api/saddle/discovery", fake_token, {"task_id": 21, "body": "valac chokes on `size` for Invercargill's Vector; use `length`."}) check(status == 200 and body.get("ok") is True and int(body.get("discovery_id", 0)) > 0, "POST inserts and returns the discovery id") first_id = int(body["discovery_id"]) status, body = saddle_api("POST", "/api/saddle/discovery", fake_token, {"body": "meson setup needs a fresh build dir after editing meson.build custom_targets."}) check(status == 200 and int(body.get("discovery_id", 0)) > first_id, "a second insert gets a LATER id") second_id = int(body["discovery_id"]) rows = db_rows("select id, body, source_task_id, created_at, updated_at" " from discoveries order by id") check([r[0] for r in rows] == [first_id, second_id], "rows land in insert order") check(rows[0][2] == 21 and rows[1][2] == 0, "rows keep the optional source task (21 / 0)") check(all(r[3] is not None and r[4] is not None for r in rows), "created_at and updated_at are stamped") print("== discoveries: GET search ==") status, _ = saddle_api("GET", "/api/saddle/discoveries?q=valac", None) check(status == 401, "GET rejects a missing token") status, body = saddle_api("GET", "/api/saddle/discoveries?q=VALAC", fake_token) check(status == 200 and body.get("ok") is True, "GET answers a search") hits = body.get("discoveries") or [] check(len(hits) == 1 and hits[0]["id"] == first_id, "the search is a case-insensitive substring match over the body") check((hits[0].get("created") or "").endswith("Z") and (hits[0].get("updated") or "").endswith("Z"), "rows carry ISO-8601 created/updated stamps") status, body = saddle_api("GET", "/api/saddle/discoveries", fake_token) check(status == 200 and len(body.get("discoveries") or []) == 2 and body["discoveries"][0]["id"] == second_id, "an empty q lists the newest 20 (newest first)") print("== discoveries: update refines in place ==") status, _ = saddle_api("POST", "/api/saddle/discovery/update", None, {"id": first_id, "body": "x"}) check(status == 401, "update rejects a missing token") status, body = saddle_api("POST", "/api/saddle/discovery/update", fake_token, {"id": 999999, "body": "x"}) check(status == 400 and body.get("ok") is False, "update rejects an unknown id with an error JSON") status, body = saddle_api("POST", "/api/saddle/discovery/update", fake_token, {"id": first_id, "body": "valac chokes on `size` for Invercargill's Vector; use `length` (also `count`, a uint)."}) check(status == 200 and body.get("ok") is True, "update answers ok") rows = db_rows("select id, body, source_task_id, created_at, updated_at" " from discoveries order by id") check(len(rows) == 2 and rows[0][0] == first_id, "update INSERTS NOTHING — the row is refined in place") check("also `count`" in rows[0][1], "the body was replaced") check(rows[0][2] == 21, "update keeps the original source task") check(rows[0][4] >= rows[0][3], "updated_at bumped at or after created_at") print("== discoveries: the page and the human actions ==") state = slot_public(auth, "/discoveries", "discoveries") listed = state.get("discoveries") or [] check([r["id"] for r in listed] == [str(second_id), str(first_id)], "the page lists rows newest first") check("also `count`" in listed[1]["body"], "the page shows the refined body") check(listed[1].get("updated_display", "") != "" and listed[0].get("updated_display", "") != "", "refined rows show an updated stamp") check(all(r.get("update") is not None and r.get("delete") is not None for r in listed), "every row authors the edit and delete actions") # The human edit path: the same update-in-place semantics. result = act(auth, listed[1]["update"], {"body": "valac: Invercargill Vector exposes `length` (uint) and `count()`, never `size`."}) check(any(d.get("type") == "notify" and d.get("kind") == "success" for d in result), "the human edit notifies success") rows = db_rows("select body from discoveries where id = %d" % first_id) check("never `size`" in rows[0][0], "the human edit replaced the body in place") # The human delete path. result = act(auth, listed[0]["delete"], {}) check(any(d.get("type") == "notify" and d.get("kind") == "success" for d in result), "the human delete notifies success") rows = db_rows("select id from discoveries order by id") check([r[0] for r in rows] == [first_id], "the human delete removed the row") print("== discoveries: the page search filter ==") # The browser filters client-side (the script above), but the # entrypoint still honours a server-side q: a direct entrypoint # hit passes the filter as a sibling query param next to uri= # (the same shape the task page's status filter documents). status, body = http( "GET", "/_statum/entrypoint?uri=" + urllib.parse.quote("/discoveries") + "&q=" + urllib.parse.quote("never `size`"), headers={"X-Statum-Slot": "key=%s; as_at=%s" % (auth["key"], auth["as_at"])}) check(status == 200, "a direct entrypoint hit with q= answers") filtered = None for directive in json.loads(body): if directive.get("type") == "set": content = json.loads(directive["snapshot_frame"]["content"]) if content["slot"]["type"] == "discoveries": filtered = content["public"] check(filtered is not None and filtered.get("query") == "never `size`", "the entrypoint seeds the search box from q") check(len(filtered.get("discoveries") or []) == 1 and "never `size`" in (filtered["discoveries"][0].get("body", "")), "the q filter narrows the rows server-side") state = slot_public(auth, "/discoveries", "discoveries") check(state.get("query", "") == "" and len(state.get("discoveries") or []) == 1, "a plain hit stays unfiltered") print("== discoveries: ride the assignment payload ==") payload_count = len(fake.start_payloads) create_queued(auth, { "project_id": "1", "title": "Discovery dispatch", "priority": "2", }) check(wait_for(lambda: len(fake.start_payloads) > payload_count or False, timeout=30, what="discovery /start push") is not None, "dispatcher pushes the discovery task") payload = fake.start_payloads[-1] check("discoveries" in payload, "the assignment payload carries a discoveries member") shipped = payload.get("discoveries") check(isinstance(shipped, list) and len(shipped) == 1 and "never `size`" in shipped[0].get("body", ""), "the payload ships the newest rows as {body} objects") # The cap: 12 giant rows → at most 10 entries / 8000 chars. conn = db() for i in range(12): conn.execute( "insert into discoveries (body, source_task_id, created_at, updated_at)" " values (?, 0, strftime('%s','now'), strftime('%s','now'))", ("cap%02d " % i + "z" * 1500,)) conn.commit() conn.close() payload_count = len(fake.start_payloads) create_queued(auth, { "project_id": "1", "title": "Discovery cap dispatch", "priority": "2", }) check(wait_for(lambda: len(fake.start_payloads) > payload_count or False, timeout=30, what="cap /start push") is not None, "dispatcher pushes the cap task") shipped = fake.start_payloads[-1].get("discoveries") check(isinstance(shipped, list) and len(shipped) <= 10, "the payload caps the discoveries at 10 entries (%d)" % len(shipped)) check(sum(len(r.get("body", "")) for r in shipped) <= 8000, "the payload caps the discoveries at 8000 chars total (%d)" % sum(len(r.get("body", "")) for r in shipped)) check(shipped[0].get("body", "").startswith("cap11"), "the newest rows ship first") print("== discoveries: the dashboard slice ==") home = slot_public(auth, "/", "dashboard") latest = home.get("discoveries") or [] check(len(latest) == 5 and latest[0].get("body", "").startswith("cap11"), "the dashboard shows the five newest discoveries newest first")