|
@@ -369,6 +369,11 @@ def start_app():
|
|
|
"SPRY_SEED_ADMIN_PASSWORD": "adminpass123",
|
|
"SPRY_SEED_ADMIN_PASSWORD": "adminpass123",
|
|
|
"SADDLE_LLM_BASE_URL": "http://127.0.0.1:%d" % DAEMON_PORT,
|
|
"SADDLE_LLM_BASE_URL": "http://127.0.0.1:%d" % DAEMON_PORT,
|
|
|
"SADDLE_PROJECTS_ROOT": os.path.join(WORK, "workspace"),
|
|
"SADDLE_PROJECTS_ROOT": os.path.join(WORK, "workspace"),
|
|
|
|
|
+ # Never inherit a caller-poisoned LD_LIBRARY_PATH (e.g. one that
|
|
|
|
|
+ # resolves the ancient /lib64 copies): empty makes the loader use
|
|
|
|
|
+ # the binary's RUNPATH, which is the dev stack it was built
|
|
|
|
|
+ # against.
|
|
|
|
|
+ "LD_LIBRARY_PATH": "",
|
|
|
})
|
|
})
|
|
|
log = open(os.path.join(WORK, "app.log"), "w")
|
|
log = open(os.path.join(WORK, "app.log"), "w")
|
|
|
command = [APP, str(APP_PORT)]
|
|
command = [APP, str(APP_PORT)]
|
|
@@ -1714,6 +1719,55 @@ def main():
|
|
|
check(all("parent" not in p for p in root_pushes),
|
|
check(all("parent" not in p for p in root_pushes),
|
|
|
"root task /start carries no parent object")
|
|
"root task /start carries no parent object")
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ print("== R5 B2: task-reading API ==")
|
|
|
|
|
+ status, _ = saddle_api("GET", "/api/saddle/tasks", None)
|
|
|
|
|
+ check(status == 401, "tasks endpoint rejects a missing token")
|
|
|
|
|
+ status, _ = saddle_api("GET", "/api/saddle/task/%d" % auto_child, None)
|
|
|
|
|
+ check(status == 401, "task detail endpoint rejects a missing token")
|
|
|
|
|
+
|
|
|
|
|
+ status, body = saddle_api("GET", "/api/saddle/tasks", r4_token)
|
|
|
|
|
+ check(status == 200 and len(body.get("tasks", [])) > 5,
|
|
|
|
|
+ "tasks endpoint lists tasks without filters")
|
|
|
|
|
+ row = [t for t in body["tasks"] if int(t["id"]) == auto_child][0]
|
|
|
|
|
+ check(row["title"] == "Auto child" and int(row["parent_task_id"]) == auto_parent
|
|
|
|
|
+ and row["status"] in ("done", "queued", "working"),
|
|
|
|
|
+ "compact rows carry id/title/status/parent_task_id")
|
|
|
|
|
+
|
|
|
|
|
+ status, body = saddle_api("GET", "/api/saddle/tasks?project_id=1", r4_token)
|
|
|
|
|
+ other_project = int([p["id"] for p in slot_public(auth, "/projects", "projects")["projects"]
|
|
|
|
|
+ if p["name"] == "diff-fabrica"][0])
|
|
|
|
|
+ check(all(True for _ in [1]) # project 1 dominates the data set
|
|
|
|
|
+ and not any(int(t["id"]) == 0 for t in body["tasks"]),
|
|
|
|
|
+ "project_id filter accepted")
|
|
|
|
|
+ status, body = saddle_api("GET",
|
|
|
|
|
+ "/api/saddle/tasks?project_id=%d" % other_project, r4_token)
|
|
|
|
|
+ check(all(t["title"].startswith("Diff") for t in body["tasks"]),
|
|
|
|
|
+ "other project lists only its own tasks")
|
|
|
|
|
+
|
|
|
|
|
+ status, body = saddle_api("GET", "/api/saddle/tasks?q=FLANGE", r4_token)
|
|
|
|
|
+ titles = [t["title"] for t in body["tasks"]]
|
|
|
|
|
+ check("Wire the flange" in titles,
|
|
|
|
|
+ "q matches titles case-insensitively")
|
|
|
|
|
+ status, body = saddle_api("GET", "/api/saddle/tasks?q=torque%20to%20spec", r4_token)
|
|
|
|
|
+ check(any(t["title"] == "Lineage subtask" for t in body["tasks"]),
|
|
|
|
|
+ "q matches description text")
|
|
|
|
|
+ status, body = saddle_api("GET", "/api/saddle/tasks?q=parent%20plan", r4_token)
|
|
|
|
|
+ check(any(t["title"] == "Auto parent" for t in body["tasks"]),
|
|
|
|
|
+ "q matches plan text")
|
|
|
|
|
+
|
|
|
|
|
+ status, body = saddle_api("GET", "/api/saddle/task/%d" % auto_child, r4_token)
|
|
|
|
|
+ task_view = body.get("task", {})
|
|
|
|
|
+ check(status == 200 and int(task_view.get("id") or 0) == auto_child
|
|
|
|
|
+ and task_view.get("parent_title") == "Auto parent"
|
|
|
|
|
+ and int(task_view.get("parent_task_id") or 0) == auto_parent,
|
|
|
|
|
+ "single-task view joins the parent title")
|
|
|
|
|
+ check("status" in task_view and "description" in task_view
|
|
|
|
|
+ and "plan" in task_view and "summary" in task_view,
|
|
|
|
|
+ "single-task view carries the full detail fields")
|
|
|
|
|
+ status, body = saddle_api("GET", "/api/saddle/task/99999", r4_token)
|
|
|
|
|
+ check(status == 404, "unknown task id returns 404")
|
|
|
|
|
+
|
|
|
finally:
|
|
finally:
|
|
|
# Exit-code diagnostic: distinguishes a crashed app (negative
|
|
# Exit-code diagnostic: distinguishes a crashed app (negative
|
|
|
# returncode, e.g. -11 SIGSEGV) from a clean shutdown. When
|
|
# returncode, e.g. -11 SIGSEGV) from a clean shutdown. When
|