| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- """The stacks bundle (goal 8 task 2): the goal link pre-fill from the
- primary project's project_links, and the stacks feature — a named
- collection of reference projects applied one-click to a project's or a
- goal's link set (stacks/stack_projects tables, /stacks pages, the apply
- actions with exact-duplicate skipping)."""
- 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 create_goal(fields):
- act(auth, slot_public(auth, "/goals", "goals")["create"], fields)
- rows = [g for g in slot_public(auth, "/goals", "goals")["goals"]
- if g["title"] == fields["title"]]
- return int(rows[0]["id"])
- def stack_state(stack_id):
- return slot_public(auth, "/stacks/%d" % stack_id, "stack")
- def link_rows(goal_id):
- conn = db()
- try:
- return conn.execute(
- "select gl.project_id, gl.relationship from goal_links gl"
- " where gl.goal_id = ? order by gl.id", (goal_id,)).fetchall()
- finally:
- conn.close()
- def project_link_rows(parent_id):
- conn = db()
- try:
- return conn.execute(
- "select linked_project_id, relationship from project_links"
- " where parent_project_id = ? order by id", (parent_id,)).fetchall()
- finally:
- conn.close()
- print("== goal 8 task 2 A: goal links pre-fill from the project ==")
- # Two satellites around the demo project: one co-developed, one
- # reference. The goal created on demo must inherit both.
- conn = db()
- conn.execute("insert into projects (name, git_url, default_branch, created_at)"
- " values ('stacks-codev', 'https://example.com/codev.git', 'main',"
- " strftime('%s','now'))")
- conn.execute("insert into projects (name, git_url, default_branch, created_at)"
- " values ('stacks-ref', 'https://example.com/ref.git', 'main',"
- " strftime('%s','now'))")
- conn.commit()
- codev_id = int(conn.execute(
- "select id from projects where name = 'stacks-codev'").fetchone()[0])
- ref_id = int(conn.execute(
- "select id from projects where name = 'stacks-ref'").fetchone()[0])
- # A third project linked to the project but AFTER goal creation —
- # it must NOT propagate (the pre-fill is a one-time snapshot).
- conn.execute("insert into projects (name, git_url, default_branch, created_at)"
- " values ('stacks-late', 'https://example.com/late.git', 'main',"
- " strftime('%s','now'))")
- conn.commit()
- late_id = int(conn.execute(
- "select id from projects where name = 'stacks-late'").fetchone()[0])
- conn.execute("insert into project_links (parent_project_id, linked_project_id,"
- " relationship) values (1, ?, 'co-developed')", (codev_id,))
- conn.execute("insert into project_links (parent_project_id, linked_project_id,"
- " relationship) values (1, ?, 'reference')", (ref_id,))
- conn.commit()
- conn.close()
- goal = create_goal({"project_id": "1", "title": "Prefill probe",
- "brief": "Check the link pre-fill.",
- "branch_name": "goal/prefill", "priority": "3"})
- check(set(link_rows(goal)) == {(codev_id, "co-developed"), (ref_id, "reference")},
- "a goal created on a linked project pre-fills goal_links from"
- " project_links (same targets, same relationships)")
- state = goal_state(goal)
- check(sorted(link["name"] for link in state["links"]) == ["stacks-codev", "stacks-ref"],
- "the goal page lists the pre-filled links")
- check(state["links"][0].get("remove") is not None,
- "the pre-filled links are prunable like manual ones")
- # The late link must not propagate into the existing goal.
- conn = db()
- conn.execute("insert into project_links (parent_project_id, linked_project_id,"
- " relationship) values (1, ?, 'reference')", (late_id,))
- conn.commit()
- conn.close()
- check(all(row[0] != late_id for row in link_rows(goal)),
- "a project link added AFTER creation never propagates (snapshot)")
- # A NEW goal created after the late link lands DOES carry it.
- goal2 = create_goal({"project_id": "1", "title": "Prefill probe 2",
- "brief": "Snapshot at creation time.",
- "branch_name": "goal/prefill2", "priority": "3"})
- check(set(link_rows(goal2)) == {(codev_id, "co-developed"),
- (ref_id, "reference"),
- (late_id, "reference")},
- "a goal created after a new project link snapshots that link too")
- # Self-links in the project graph are never copied (a project link
- # to the goal's own primary is redundant — it is implicit).
- conn = db()
- conn.execute("insert into projects (name, git_url, default_branch, created_at)"
- " values ('stacks-self', 'https://example.com/self.git', 'main',"
- " strftime('%s','now'))")
- conn.commit()
- conn.close()
- goal3 = create_goal({"project_id": "1", "title": "No self links",
- "brief": "The primary is implicit.",
- "branch_name": "goal/prefill3", "priority": "3"})
- check(all(row[0] != 1 for row in link_rows(goal3)),
- "the primary project is never duplicated as an explicit link")
- print("== the pre-filled links ride the assignment payload ==")
- # Removing one pre-filled link must remove it from the payload —
- # goal_links stays the single source of truth after the snapshot.
- state = goal_state(goal)
- codev_link = [link for link in state["links"] if link["name"] == "stacks-codev"][0]
- act(auth, codev_link["remove"], {"link_id": str(codev_link["id"])})
- check(set(link_rows(goal)) == {(ref_id, "reference")},
- "removing a pre-filled goal link removes the row")
- fake.goal_script = [("suspend",)]
- act(auth, goal_state(goal)["start"], {})
- check(wait_for(lambda: next(
- (True for row in db().execute(
- "select id from tasks where goal_id = ? and kind = 'goal_run'",
- (goal,)).fetchall()), False) or False,
- timeout=30, what="goal run task creation") is not None,
- "start creates the goal-run task")
- conn = db()
- run_task = int(conn.execute(
- "select id from tasks where goal_id = ? and kind = 'goal_run'",
- (goal,)).fetchone()[0])
- conn.close()
- def assigned_payload():
- for payload in reversed(fake.goal_assignments):
- if (payload.get("task") or {}).get("id") == run_task:
- return payload
- return None
- check(wait_for(lambda: assigned_payload() is not None or False,
- timeout=60, what="goal run assignment") is not None,
- "the fake saddle captures the goal-run assignment")
- payload = assigned_payload()
- codev_names = [p["name"] for p in payload["task"]["co_developed"]]
- ref_names = [p["name"] for p in payload["task"]["reference"]]
- check("demo" in codev_names and "stacks-codev" not in codev_names,
- "the assignment payload's co-developed carries the primary and the"
- " surviving pre-filled link (the removed one is gone)")
- check(ref_names == ["stacks-ref"],
- "the assignment payload's reference set comes from the goal's links")
- print("== stacks: CRUD ==")
- stacks_state = slot_public(auth, "/stacks", "stacks")
- check(stacks_state.get("create") is not None and stacks_state["stacks"] == [],
- "the stacks page starts empty with a create action")
- act(auth, stacks_state["create"],
- {"name": "web-stack-refs", "summary": "The usual reference collection"})
- stacks = slot_public(auth, "/stacks", "stacks")["stacks"]
- check(len(stacks) == 1 and stacks[0]["name"] == "web-stack-refs"
- and int(stacks[0]["member_count"]) == 0,
- "a stack lands in the list with its (empty) member count")
- stack_id = int(stacks[0]["id"])
- detail = stack_state(stack_id)
- check(detail["exists"] and detail["name"] == "web-stack-refs"
- and detail["members"] == [] and detail.get("update") is not None
- and detail.get("add_member") is not None and detail.get("delete") is not None,
- "the stack page exposes edit, add-member and delete actions")
- check(any(option["name"] == "demo" for option in detail["options"]),
- "every project is offerable as a stack member")
- # Duplicate name refused.
- dup = act(auth, slot_public(auth, "/stacks", "stacks")["create"],
- {"name": "web-stack-refs"})
- check(any(d.get("type") == "notify" and d.get("kind") == "error"
- for d in dup),
- "a duplicate stack name refuses")
- # Rename + summary clear.
- act(auth, detail["update"], {"name": "web-stack-refs-2", "summary": ""})
- detail = stack_state(stack_id)
- check(detail["name"] == "web-stack-refs-2" and detail["summary"] == "",
- "a stack renames and its summary clears")
- print("== stacks: members ==")
- detail = stack_state(stack_id)
- act(auth, detail["add_member"],
- {"project_id": str(codev_id), "relationship": "co-developed"})
- act(auth, stack_state(stack_id)["add_member"],
- {"project_id": str(ref_id), "relationship": "reference"})
- detail = stack_state(stack_id)
- member_names = [(m["name"], m["relationship"]) for m in detail["members"]]
- check(member_names == [("stacks-codev", "co-developed"),
- ("stacks-ref", "reference")],
- "members land with their relationships")
- dup = act(auth, stack_state(stack_id)["add_member"],
- {"project_id": str(ref_id), "relationship": "reference"})
- check(any(d.get("type") == "notify" and d.get("kind") == "error"
- and "already" in (d.get("message") or "") for d in dup),
- "adding the same project twice refuses")
- bad = act(auth, stack_state(stack_id)["add_member"],
- {"project_id": str(late_id), "relationship": "sponsor"})
- check(any(d.get("type") == "notify" and d.get("kind") == "error"
- for d in bad),
- "an unknown relationship refuses")
- detail = stack_state(stack_id)
- check(len(detail["members"]) == 2,
- "the refused additions left the stack untouched")
- print("== stacks: apply to a project ==")
- conn = db()
- conn.execute("insert into projects (name, git_url, default_branch, created_at)"
- " values ('stacks-target', 'https://example.com/target.git', 'main',"
- " strftime('%s','now'))")
- conn.commit()
- conn.close()
- target_id = int(db().execute(
- "select id from projects where name = 'stacks-target'"
- ).fetchone()[0])
- project_state = slot_public(auth, "/projects/%d" % target_id, "project")
- check(project_state.get("apply_stack") is not None
- and any(opt["name"] == "web-stack-refs-2" for opt in project_state["stack_options"]),
- "the project page offers Apply stack with the stacks as options")
- result = act(auth, project_state["apply_stack"], {"stack_id": str(stack_id)})
- check("2 link" in (action_notify(result) or ""),
- "applying reports how many links landed")
- check(set(project_link_rows(target_id)) == {(codev_id, "co-developed"),
- (ref_id, "reference")},
- "the stack's members became project_links rows")
- # Re-apply: exact duplicates skipped, nothing duplicated.
- result = act(auth, slot_public(auth, "/projects/%d" % target_id,
- "project")["apply_stack"],
- {"stack_id": str(stack_id)})
- check("nothing new" in (action_notify(result) or ""),
- "re-applying skips exact duplicates")
- check(set(project_link_rows(target_id)) == {(codev_id, "co-developed"),
- (ref_id, "reference")},
- "no duplicate project_links rows appear")
- # A pre-existing DIFFERENT relationship on the same target is not a
- # duplicate: applying adds the stack's own relationship alongside.
- conn = db()
- conn.execute("insert into project_links (parent_project_id, linked_project_id,"
- " relationship) values (?, ?, 'co-developed')",
- (target_id, ref_id))
- conn.commit()
- conn.close()
- act(auth, slot_public(auth, "/projects/%d" % target_id, "project")["apply_stack"],
- {"stack_id": str(stack_id)})
- check((ref_id, "reference") in project_link_rows(target_id)
- and project_link_rows(target_id).count((ref_id, "co-developed")) == 1,
- "a different relationship on the same target coexists (exact pairs skip)")
- print("== stacks: apply to a goal ==")
- # A goal on a LINK-FREE host project — the pre-fill seeds nothing,
- # so the stack's members are exactly what applying must land.
- conn = db()
- conn.execute("insert into projects (name, git_url, default_branch, created_at)"
- " values ('stacks-goalhost', 'https://example.com/goalhost.git',"
- " 'main', strftime('%s','now'))")
- conn.commit()
- conn.close()
- host_id = int(db().execute(
- "select id from projects where name = 'stacks-goalhost'").fetchone()[0])
- goal4 = create_goal({"project_id": str(host_id), "title": "Stack target",
- "brief": "Apply a stack here.",
- "branch_name": "goal/stacktarget", "priority": "3"})
- check(link_rows(goal4) == [],
- "a goal on a link-free project pre-fills nothing")
- gstate = goal_state(goal4)
- check(gstate.get("apply_stack") is not None
- and any(opt["name"] == "web-stack-refs-2" for opt in gstate["stack_options"]),
- "the goal page offers Apply stack with the stacks as options")
- result = act(auth, gstate["apply_stack"], {"stack_id": str(stack_id)})
- check("2 link" in (action_notify(result) or ""),
- "applying to the goal reports the links landed")
- check(set(link_rows(goal4)) == {(codev_id, "co-developed"),
- (ref_id, "reference")},
- "the stack's members became goal_links rows")
- # Re-apply: idempotent.
- result = act(auth, goal_state(goal4)["apply_stack"], {"stack_id": str(stack_id)})
- check("nothing new" in (action_notify(result) or ""),
- "re-applying to the goal skips exact duplicates")
- check(set(link_rows(goal4)) == {(codev_id, "co-developed"),
- (ref_id, "reference")},
- "no duplicate goal_links rows appear")
- # Removing a stack member then re-applying adds only the newcomer.
- detail = stack_state(stack_id)
- ref_view = [m for m in detail["members"]
- if int(m["project_id"]) == ref_id][0]
- act(auth, ref_view["remove"], {})
- check(len(stack_state(stack_id)["members"]) == 1,
- "removing a member shrinks the stack")
- act(auth, goal_state(goal4)["apply_stack"], {"stack_id": str(stack_id)})
- check(set(link_rows(goal4)) == {(codev_id, "co-developed"),
- (ref_id, "reference")},
- "removing a stack member never unlinks what was already applied")
- print("== stacks: delete ==")
- conn = db()
- act(auth, slot_public(auth, "/stacks/%d" % stack_id, "stack")["delete"], {})
- conn.close()
- check(slot_public(auth, "/stacks", "stacks")["stacks"] == [],
- "deleting a stack empties the list")
- conn = db()
- leftover = conn.execute("select count(*) from stack_projects").fetchone()[0]
- conn.close()
- check(leftover == 0, "the stack's member rows die with it")
- check(set(link_rows(goal4)) == {(codev_id, "co-developed"),
- (ref_id, "reference")},
- "deleting a stack keeps every link it already applied")
|