//! `pic scripts deploy` / `pic scripts ls` edge cases beyond the //! smoke test: unknown app, name override, version bumping, missing //! file, and the no-`--app` walk across every accessible app. use predicates::prelude::*; use crate::common; use crate::common::cleanup::AppGuard; #[ignore = "needs DATABASE_URL pointing at a running Postgres"] #[test] fn deploy_against_unknown_app_errors() { let Some(fx) = common::fixture_or_skip() else { return; }; let env = common::admin_env(fx); let fixture = common::fixture_path("hello.rhai"); let bogus_slug = common::unique_slug("nope"); common::pic_as(&env) .args([ "scripts", "deploy", fixture.to_str().unwrap(), "--app", &bogus_slug, ]) .assert() .failure() // Specifically 404 — `apps_get` short-circuits before the deploy // request even starts. Loose `"HTTP 4"` would have matched a // regressed 401 from broken auth. .stderr(predicate::str::contains("HTTP 404")); } #[ignore = "needs DATABASE_URL pointing at a running Postgres"] #[test] fn deploy_with_name_override() { let Some(fx) = common::fixture_or_skip() else { return; }; let env = common::admin_env(fx); let slug = common::unique_slug("scripts-named"); common::pic_as(&env) .args(["apps", "create", &slug]) .assert() .success(); let _guard = AppGuard::new(&env.url, &env.token, &slug); let fixture = common::fixture_path("hello.rhai"); common::pic_as(&env) .args([ "scripts", "deploy", fixture.to_str().unwrap(), "--app", &slug, "--name", "custom-name", ]) .assert() .success() .stdout(predicate::str::contains("Created custom-name v1")); common::pic_as(&env) .args([ "scripts", "deploy", fixture.to_str().unwrap(), "--app", &slug, "--name", "custom-name", ]) .assert() .success() .stdout(predicate::str::contains("Updated custom-name v2")); let out = common::pic_as(&env) .args(["scripts", "ls", "--app", &slug]) .output() .expect("scripts ls"); assert!(out.status.success()); let stdout = String::from_utf8(out.stdout).unwrap(); assert!( stdout .lines() .map(common::cells) .any(|c| c.get(2).copied() == Some("custom-name") && c.get(3).copied() == Some("2")), "expected custom-name v2 row, got: {stdout}", ); } #[ignore = "needs DATABASE_URL pointing at a running Postgres"] #[test] fn deploy_bumps_version_each_redeploy() { let Some(fx) = common::fixture_or_skip() else { return; }; let env = common::admin_env(fx); let slug = common::unique_slug("scripts-bump"); common::pic_as(&env) .args(["apps", "create", &slug]) .assert() .success(); let _guard = AppGuard::new(&env.url, &env.token, &slug); let fixture = common::fixture_path("hello.rhai"); for expected in ["Created hello v1", "Updated hello v2", "Updated hello v3"] { common::pic_as(&env) .args([ "scripts", "deploy", fixture.to_str().unwrap(), "--app", &slug, ]) .assert() .success() .stdout(predicate::str::contains(expected)); } } #[ignore = "needs DATABASE_URL pointing at a running Postgres"] #[test] fn deploy_missing_file_errors() { let Some(fx) = common::fixture_or_skip() else { return; }; let env = common::admin_env(fx); let slug = common::unique_slug("scripts-missing"); common::pic_as(&env) .args(["apps", "create", &slug]) .assert() .success(); let _guard = AppGuard::new(&env.url, &env.token, &slug); let missing = std::env::temp_dir().join(common::unique_slug("ghost") + ".rhai"); common::pic_as(&env) .args([ "scripts", "deploy", missing.to_str().unwrap(), "--app", &slug, ]) .assert() .failure() .stderr(predicate::str::contains("reading")); } #[ignore = "needs DATABASE_URL pointing at a running Postgres"] #[test] fn ls_without_app_walks_every_accessible_app() { let Some(fx) = common::fixture_or_skip() else { return; }; let env = common::admin_env(fx); let slug_a = common::unique_slug("scripts-walk-a"); let slug_b = common::unique_slug("scripts-walk-b"); common::pic_as(&env) .args(["apps", "create", &slug_a]) .assert() .success(); let _guard_a = AppGuard::new(&env.url, &env.token, &slug_a); common::pic_as(&env) .args(["apps", "create", &slug_b]) .assert() .success(); let _guard_b = AppGuard::new(&env.url, &env.token, &slug_b); let fixture = common::fixture_path("hello.rhai"); for slug in [&slug_a, &slug_b] { common::pic_as(&env) .args([ "scripts", "deploy", fixture.to_str().unwrap(), "--app", slug, ]) .assert() .success(); } // `pic scripts ls` (no `--app`) issues a single `GET /admin/scripts` // against the server now — there's nothing per-app to race against // a concurrent AppGuard drop. The previous implementation walked // `apps_list` followed by per-app `scripts_list_by_app` calls and // aborted on the first 404, which forced this test to retry 5× to // paper over the bug. Both the walk and the retry are gone. let out = common::pic_as(&env) .args(["scripts", "ls"]) .output() .expect("scripts ls"); assert!(out.status.success(), "scripts ls failed: {out:?}"); let stdout = String::from_utf8(out.stdout).unwrap(); let slugs: std::collections::HashSet<&str> = stdout .lines() .map(common::cells) .filter_map(|c| c.get(1).copied()) .collect(); assert!( slugs.contains(slug_a.as_str()), "missing app A in: {stdout}" ); assert!( slugs.contains(slug_b.as_str()), "missing app B in: {stdout}" ); } #[ignore = "needs DATABASE_URL pointing at a running Postgres"] #[test] fn delete_removes_script_from_ls() { let Some(fx) = common::fixture_or_skip() else { return; }; let env = common::admin_env(fx); let (id, _guard) = common::deploy_fixture(&env, "scripts-del", "hello.rhai"); common::pic_as(&env) .args(["scripts", "delete", &id]) .assert() .success() .stdout(predicate::str::contains(format!("Deleted script {id}"))); let out = common::pic_as(&env) .args(["scripts", "ls"]) .output() .expect("scripts ls"); assert!(out.status.success()); let stdout = String::from_utf8(out.stdout).unwrap(); assert!( !stdout.contains(&id), "deleted script id should not appear in ls: {stdout}" ); }