//! `enabled` three-state lifecycle, end to end: disabling a script via the //! manifest makes it non-invocable (404 on the execute-by-id bypass), and //! re-enabling restores it — proving the data path + runtime honoring. use std::fs; use std::path::Path; use serde_json::Value; use tempfile::TempDir; use crate::common; use crate::common::cleanup::AppGuard; #[ignore = "needs DATABASE_URL pointing at a running Postgres"] #[test] fn disabling_a_script_makes_it_uninvocable_then_reenable() { let Some(fx) = common::fixture_or_skip() else { return; }; let env = common::admin_env(fx); let slug = common::unique_slug("enabled"); common::pic_as(&env) .args(["apps", "create", &slug]) .assert() .success(); let _guard = AppGuard::new(&env.url, &env.token, &slug); let dir = TempDir::new().unwrap(); fs::create_dir_all(dir.path().join("scripts")).unwrap(); fs::write(dir.path().join("scripts/hello.rhai"), "\"hi\"").unwrap(); let manifest_path = dir.path().join("picloud.toml"); let manifest = |enabled_line: &str| { format!( "[app]\nslug = \"{slug}\"\nname = \"Enabled\"\n\n\ [[scripts]]\nname = \"hello\"\nfile = \"scripts/hello.rhai\"\n{enabled_line}" ) }; // Active → invocable. fs::write(&manifest_path, manifest("")).unwrap(); apply(&env, &manifest_path); let id = script_id(&env, &slug, "hello"); assert_eq!(invoke_status(&env, &id), 200, "active script must invoke"); // Disabled → 404 (not invocable), but still deployed (re-pull would show it). fs::write(&manifest_path, manifest("enabled = false\n")).unwrap(); apply(&env, &manifest_path); assert_eq!( invoke_status(&env, &id), 404, "disabled script must 404 on execute-by-id" ); // Re-enabled → invocable again. fs::write(&manifest_path, manifest("enabled = true\n")).unwrap(); apply(&env, &manifest_path); assert_eq!( invoke_status(&env, &id), 200, "re-enabled script must invoke" ); } fn apply(env: &common::TestEnv, manifest_path: &Path) { common::pic_as(env) .args(["apply", "--file"]) .arg(manifest_path) .assert() .success(); } /// Resolve a script's id via the admin API (the manifest carries no ids). fn script_id(env: &common::TestEnv, slug: &str, name: &str) -> String { let client = reqwest::blocking::Client::new(); let scripts: Vec = client .get(format!("{}/api/v1/admin/scripts?app={slug}", env.url)) .bearer_auth(&env.token) .send() .unwrap() .json() .unwrap(); scripts .into_iter() .find(|s| s["name"] == name) .and_then(|s| s["id"].as_str().map(String::from)) .expect("script id") } /// POST the execute-by-id bypass and return the HTTP status code. fn invoke_status(env: &common::TestEnv, id: &str) -> u16 { let client = reqwest::blocking::Client::new(); client .post(format!("{}/api/v1/execute/{id}", env.url)) .bearer_auth(&env.token) .body("{}") .send() .unwrap() .status() .as_u16() }