test(stateful-templates): group email template journey + load group secrets (M5.5)
CLI journey: set a group secret, apply a group [[triggers.email]] template referencing it, create an app under the group → `pic triggers ls --app` shows a materialized email trigger; re-apply is a NoOp. Required loading a group's own set-secret names into CurrentState (was hardcoded empty) so the apply plan-time email-secret check resolves the ref against the GROUP store. Informational only — the secrets diff is never executed on apply (secrets are set out-of-band via `pic secret set`); the state token now folds in group secrets, matching apps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,8 +33,8 @@ fn group_script_id(env: &common::TestEnv, group: &str, name: &str) -> String {
|
||||
.unwrap_or_else(|| panic!("group script `{name}` not found:\n{table}"))
|
||||
}
|
||||
|
||||
/// Count `cron` rows in `pic triggers ls --app`.
|
||||
fn app_cron_count(env: &common::TestEnv, app: &str) -> usize {
|
||||
/// Count rows of `kind` in `pic triggers ls --app`.
|
||||
fn app_trigger_count(env: &common::TestEnv, app: &str, kind: &str) -> usize {
|
||||
let out = common::pic_as(env)
|
||||
.args(["triggers", "ls", "--app", app])
|
||||
.output()
|
||||
@@ -43,10 +43,15 @@ fn app_cron_count(env: &common::TestEnv, app: &str) -> usize {
|
||||
.unwrap()
|
||||
.lines()
|
||||
.map(common::cells)
|
||||
.filter(|c| c.contains(&"cron"))
|
||||
.filter(|c| c.contains(&kind))
|
||||
.count()
|
||||
}
|
||||
|
||||
/// Count `cron` rows in `pic triggers ls --app`.
|
||||
fn app_cron_count(env: &common::TestEnv, app: &str) -> usize {
|
||||
app_trigger_count(env, app, "cron")
|
||||
}
|
||||
|
||||
#[ignore = "needs DATABASE_URL pointing at a running Postgres"]
|
||||
#[test]
|
||||
fn group_cron_template_materializes_for_descendant_apps() {
|
||||
@@ -118,3 +123,86 @@ fn group_cron_template_materializes_for_descendant_apps() {
|
||||
"re-apply must not duplicate the materialized copy"
|
||||
);
|
||||
}
|
||||
|
||||
/// §4.5 M5.5 — a group EMAIL template materializes for a descendant app. The
|
||||
/// template resolves its `inbound_secret_ref` against the GROUP's own secret
|
||||
/// store once at apply (shared-group-secret model); creating an app under the
|
||||
/// group materializes a per-app email trigger, visible in `pic triggers ls
|
||||
/// --app`. Re-applying the group is a NoOp (no duplicate copy).
|
||||
#[ignore = "needs DATABASE_URL pointing at a running Postgres"]
|
||||
#[test]
|
||||
fn group_email_template_materializes_for_descendant_apps() {
|
||||
let Some(fx) = common::fixture_or_skip() else {
|
||||
return;
|
||||
};
|
||||
let env = common::admin_env(fx);
|
||||
let group = common::unique_slug("mat-em-grp");
|
||||
|
||||
let _g = GroupGuard::new(&env.url, &env.token, &group);
|
||||
common::pic_as(&env)
|
||||
.args(["groups", "create", &group])
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// A group secret the email template references, resolved at apply.
|
||||
common::pic_as(&env)
|
||||
.args(["secrets", "set", "--group", &group, "mail-sig"])
|
||||
.write_stdin("s3cret-hmac")
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Group handler + an email TEMPLATE binding it.
|
||||
let dir = manifest_dir();
|
||||
fs::write(
|
||||
dir.path().join("scripts/inbound.rhai"),
|
||||
r#"log::info("inbound"); "ok""#,
|
||||
)
|
||||
.unwrap();
|
||||
common::pic_as(&env)
|
||||
.args(["scripts", "deploy"])
|
||||
.arg(dir.path().join("scripts/inbound.rhai"))
|
||||
.args(["--group", &group, "--name", "inbound"])
|
||||
.assert()
|
||||
.success();
|
||||
let _gs = ScriptGuard::new(
|
||||
&env.url,
|
||||
&env.token,
|
||||
&group_script_id(&env, &group, "inbound"),
|
||||
);
|
||||
let gmanifest = format!(
|
||||
"[group]\nslug = \"{group}\"\nname = \"MatEmG\"\n\n\
|
||||
[[triggers.email]]\nscript = \"inbound\"\ninbound_secret_ref = \"mail-sig\"\n"
|
||||
);
|
||||
let gpath = dir.path().join("group.toml");
|
||||
fs::write(&gpath, &gmanifest).unwrap();
|
||||
common::pic_as(&env)
|
||||
.args(["apply", "--file"])
|
||||
.arg(&gpath)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// An app created under the group materializes a per-app email copy.
|
||||
let app = common::unique_slug("mat-em-app");
|
||||
let _a = AppGuard::new(&env.url, &env.token, &app);
|
||||
common::pic_as(&env)
|
||||
.args(["apps", "create", &app, "--group", &group])
|
||||
.assert()
|
||||
.success();
|
||||
assert_eq!(
|
||||
app_trigger_count(&env, &app, "email"),
|
||||
1,
|
||||
"a descendant app must have a materialized email trigger"
|
||||
);
|
||||
|
||||
// Re-apply of the group is a NoOp for materialization (no duplicate copy).
|
||||
common::pic_as(&env)
|
||||
.args(["apply", "--file"])
|
||||
.arg(&gpath)
|
||||
.assert()
|
||||
.success();
|
||||
assert_eq!(
|
||||
app_trigger_count(&env, &app, "email"),
|
||||
1,
|
||||
"re-apply must not duplicate the materialized email copy"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user