diff --git a/crates/picloud-cli/tests/apply.rs b/crates/picloud-cli/tests/apply.rs index 771b355..52b1d77 100644 --- a/crates/picloud-cli/tests/apply.rs +++ b/crates/picloud-cli/tests/apply.rs @@ -151,3 +151,105 @@ fn apply_rejects_bad_bundle_atomically() { "a failed apply must leave nothing behind:\n{s}" ); } + +#[ignore = "needs DATABASE_URL pointing at a running Postgres"] +#[test] +fn apply_reconciles_app_vars() { + let Some(fx) = common::fixture_or_skip() else { + return; + }; + let env = common::admin_env(fx); + let slug = common::unique_slug("apply-vars"); + common::pic_as(&env) + .args(["apps", "create", &slug]) + .assert() + .success(); + let _guard = AppGuard::new(&env.url, &env.token, &slug); + + let dir = manifest_dir(); + // The script returns the resolved `region` var verbatim, so an invoke + // reflects exactly what `apply` wrote. + fs::write( + dir.path().join("scripts/read.rhai"), + "vars::get(\"region\")", + ) + .unwrap(); + let manifest = |vars_block: &str| { + format!( + "[app]\nslug = \"{slug}\"\nname = \"Vars Test\"\n\n\ + [[scripts]]\nname = \"read\"\nfile = \"scripts/read.rhai\"\n\n\ + [[routes]]\nscript = \"read\"\nmethod = \"POST\"\n\ + host_kind = \"any\"\npath_kind = \"exact\"\npath = \"/read\"\n\n\ + {vars_block}" + ) + }; + let manifest_path = dir.path().join("picloud.toml"); + + // 1. Apply with `region = "eu"` → the var is created and injected. + fs::write(&manifest_path, manifest("[vars]\nregion = \"eu\"\n")).unwrap(); + let out = common::pic_as(&env) + .args(["apply", "--file"]) + .arg(&manifest_path) + .output() + .expect("apply"); + assert!( + out.status.success(), + "apply failed: {}", + String::from_utf8_lossy(&out.stderr) + ); + let id = script_id(&env, &slug); + assert_eq!( + invoke_body(&env, &id), + serde_json::json!("eu"), + "var applied" + ); + + // 2. Change the value → Update (not Create). + fs::write(&manifest_path, manifest("[vars]\nregion = \"us\"\n")).unwrap(); + common::pic_as(&env) + .args(["apply", "--file"]) + .arg(&manifest_path) + .assert() + .success(); + assert_eq!( + invoke_body(&env, &id), + serde_json::json!("us"), + "var updated" + ); + + // 3. Drop the var + `--prune` → Delete; `vars::get` now returns `()`. + fs::write(&manifest_path, manifest("")).unwrap(); + common::pic_as(&env) + .args(["apply", "--file"]) + .arg(&manifest_path) + .args(["--prune", "--yes"]) + .assert() + .success(); + assert_eq!( + invoke_body(&env, &id), + serde_json::Value::Null, + "var pruned → unresolved" + ); +} + +fn script_id(env: &common::TestEnv, slug: &str) -> String { + let ls = common::pic_as(env) + .args(["scripts", "ls", "--app", slug]) + .output() + .expect("scripts ls"); + common::parse_first_id(std::str::from_utf8(&ls.stdout).unwrap()) + .expect("scripts ls should produce one row") +} + +fn invoke_body(env: &common::TestEnv, id: &str) -> serde_json::Value { + let out = common::pic_as(env) + .args(["scripts", "invoke", id]) + .output() + .expect("scripts invoke"); + assert!( + out.status.success(), + "invoke failed: {}", + String::from_utf8_lossy(&out.stderr) + ); + serde_json::from_slice(&out.stdout).expect("invoke body is JSON") +}