Files
MechaCat02 80fbf2e642 test: give three no-teeth assertions their teeth
- roles: member invoke asserts the script's actual stdout, not just exit 0;
- api: app-slug script filter asserts the returned script's name;
- workflow_orchestrator: unknown-workflow asserts WorkflowError::NotFound,
  not just is_err().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 20:13:12 +02:00

152 lines
5.4 KiB
Rust

//! RBAC mirror of the dashboard's role-shadowing specs. A Member user
//! is minted via the admin API, granted (or denied) membership on an
//! app, then `pic` is driven against the member's bearer token to
//! confirm the server's capability gates surface as expected exit
//! codes / error messages.
use predicates::prelude::*;
use crate::common;
use crate::common::cleanup::AppGuard;
use crate::common::member;
#[ignore = "needs DATABASE_URL pointing at a running Postgres"]
#[test]
fn member_apps_ls_only_shows_their_apps() {
let Some(fx) = common::fixture_or_skip() else {
return;
};
let admin_env = common::admin_env(fx);
let slug_visible = common::unique_slug("roles-visible");
let slug_hidden = common::unique_slug("roles-hidden");
common::pic_as(&admin_env)
.args(["apps", "create", &slug_visible])
.assert()
.success();
let _g1 = AppGuard::new(&admin_env.url, &admin_env.token, &slug_visible);
common::pic_as(&admin_env)
.args(["apps", "create", &slug_hidden])
.assert()
.success();
let _g2 = AppGuard::new(&admin_env.url, &admin_env.token, &slug_hidden);
let m = member::member_user(fx, &common::unique_username("rls"));
member::grant_membership(fx, &slug_visible, &m.id, "viewer");
let member_env = common::custom_env(&fx.url, &m.token);
common::seed_credentials(&member_env, &m.username);
let out = common::pic_as(&member_env)
.args(["apps", "ls"])
.output()
.expect("apps ls");
assert!(out.status.success(), "apps ls failed: {out:?}");
let stdout = String::from_utf8(out.stdout).unwrap();
assert!(
stdout.contains(&slug_visible),
"member should see {slug_visible}, got: {stdout}"
);
assert!(
!stdout.contains(&slug_hidden),
"member should NOT see {slug_hidden}, got: {stdout}"
);
}
#[ignore = "needs DATABASE_URL pointing at a running Postgres"]
#[test]
fn viewer_cannot_deploy_but_editor_can() {
let Some(fx) = common::fixture_or_skip() else {
return;
};
let admin_env = common::admin_env(fx);
let slug = common::unique_slug("roles-write");
common::pic_as(&admin_env)
.args(["apps", "create", &slug])
.assert()
.success();
let _g = AppGuard::new(&admin_env.url, &admin_env.token, &slug);
let m = member::member_user(fx, &common::unique_username("vw"));
member::grant_membership(fx, &slug, &m.id, "viewer");
let member_env = common::custom_env(&fx.url, &m.token);
common::seed_credentials(&member_env, &m.username);
let fixture = common::fixture_path("hello.rhai");
common::pic_as(&member_env)
.args([
"scripts",
"deploy",
fixture.to_str().unwrap(),
"--app",
&slug,
])
.assert()
.failure()
// `Forbidden` → 403. A regressed predicate of `"HTTP 4"` would
// have masked an auth break (401) as an authz issue.
.stderr(predicate::str::contains("HTTP 403"));
// Promote to Editor and retry — the same command should now succeed.
member::update_membership(fx, &slug, &m.id, "editor");
common::pic_as(&member_env)
.args([
"scripts",
"deploy",
fixture.to_str().unwrap(),
"--app",
&slug,
])
.assert()
.success()
.stdout(predicate::str::contains("hello").and(predicate::str::contains("created")));
}
#[ignore = "needs DATABASE_URL pointing at a running Postgres"]
#[test]
fn member_can_invoke_any_script_with_id() {
let Some(fx) = common::fixture_or_skip() else {
return;
};
// `/api/v1/execute/{id}` is the unguarded data-plane ingress — even
// a member with no app membership can hit it as long as they hold
// a valid token (the orchestrator doesn't gate it).
let admin_env = common::admin_env(fx);
let (id, _guard) = common::deploy_fixture(&admin_env, "roles-inv", "hello.rhai");
let m = member::member_user(fx, &common::unique_username("inv"));
let member_env = common::custom_env(&fx.url, &m.token);
common::seed_credentials(&member_env, &m.username);
// Assert the script actually RAN, not just that the command exited 0 — a
// no-op that printed nothing, or a 200 with an error envelope the CLI didn't
// treat as failure, would pass a bare `.success()`. `hello.rhai` emits
// "hello". (The sibling roles test one screen up does exactly this.)
common::pic_as(&member_env)
.args(["scripts", "invoke", &id])
.assert()
.success()
.stdout(predicate::str::contains("hello"));
}
#[ignore = "needs DATABASE_URL pointing at a running Postgres"]
#[test]
fn non_member_cannot_read_logs() {
let Some(fx) = common::fixture_or_skip() else {
return;
};
let admin_env = common::admin_env(fx);
let (id, _guard) = common::deploy_fixture(&admin_env, "roles-log", "hello.rhai");
let m = member::member_user(fx, &common::unique_username("rl"));
let member_env = common::custom_env(&fx.url, &m.token);
common::seed_credentials(&member_env, &m.username);
common::pic_as(&member_env)
.args(["logs", &id])
.assert()
.failure()
// Non-member → 403 from the authz layer, not 404 — the script
// exists; the caller just can't see it.
.stderr(predicate::str::contains("HTTP 403"));
}