test(cli): journey for group-script inheritance, CoW, isolation + apply binding

Phase 4-lite C5. Two `pic` journeys:
  * `group_script_is_inherited_with_cow_and_isolation` — a group endpoint
    `shared` is invoked by name from an app under the group; the app's own
    `shared` then shadows it (CoW); an app outside the group cannot reach it.
  * `manifest_binds_route_to_inherited_group_script_idempotently` — a manifest
    that declares no `greet` script binds a route to the inherited group
    `greet`; plan shows a route create (no script create), apply succeeds, and
    re-plan is a clean no-op.

Adds a `ScriptGuard` (deletes a script by id on drop) since a group-owned
script blocks its group's deletion (ON DELETE RESTRICT) — it must drop before
the GroupGuard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-25 20:42:35 +02:00
parent 701ec90b56
commit c29e4b9c53
3 changed files with 267 additions and 0 deletions

View File

@@ -38,6 +38,36 @@ impl Drop for AppGuard {
}
}
/// Deletes a script by id on drop (best-effort). Phase 4: a group-owned
/// script blocks its group's deletion (`ON DELETE RESTRICT`), so register the
/// `ScriptGuard` *after* the owning `GroupGuard` — the script drops (deletes)
/// first, leaving the group removable.
pub struct ScriptGuard {
url: String,
token: String,
id: String,
}
impl ScriptGuard {
pub fn new(url: &str, token: &str, id: &str) -> Self {
Self {
url: url.to_string(),
token: token.to_string(),
id: id.to_string(),
}
}
}
impl Drop for ScriptGuard {
fn drop(&mut self) {
let client = reqwest::blocking::Client::new();
let _ = client
.delete(format!("{}/api/v1/admin/scripts/{}", self.url, self.id))
.bearer_auth(&self.token)
.send();
}
}
pub struct UserGuard {
url: String,
token: String,