| 1729 |
1729 |
|
/// hand it a dummy signing secret, and disable file scanning (no AV/YARA on the
|
| 1730 |
1730 |
|
/// build host). The worktree is a clean git checkout, so no stray `.env` shadows
|
| 1731 |
1731 |
|
/// these (and dotenvy never overrides already-set vars).
|
|
1732 |
+ |
///
|
|
1733 |
+ |
/// This list has to carry EVERY var the server's `Config::from_env` treats as
|
|
1734 |
+ |
/// mandatory, because `code_smoke` is the only gate that reaches that function
|
|
1735 |
+ |
/// at all: `boot_smoke` and the docs check both short-circuit in `main` before
|
|
1736 |
+ |
/// config is loaded. So when the server makes a new var required, this is where
|
|
1737 |
+ |
/// it has to be answered, and nothing connects the two lists automatically.
|
|
1738 |
+ |
///
|
|
1739 |
+ |
/// That has already bitten once. `CDN_BASE_URL` became mandatory on 2026-07-30
|
|
1740 |
+ |
/// (server 3e3b1d15, closing a cover URL that could expire), 13 days after this
|
|
1741 |
+ |
/// function was written, and `code_smoke` failed with `MissingCdnBaseUrl` the
|
|
1742 |
+ |
/// first time it ran on the host — which was 2026-08-03, because the gate was
|
|
1743 |
+ |
/// configured in the repo and absent from `/etc/sando` in between.
|
|
1744 |
+ |
///
|
|
1745 |
+ |
/// The values are deliberately throwaway. The gate asks whether this code can
|
|
1746 |
+ |
/// migrate, seed, boot and serve; whether a given deployment's env is complete
|
|
1747 |
+ |
/// is the `config_check_env_file` guard's job, on the node, against that node's
|
|
1748 |
+ |
/// real env file.
|
| 1732 |
1749 |
|
fn code_smoke_env(cmd: &mut tokio::process::Command, ctx: &GateCtx, db_url: &str) {
|
|
1750 |
+ |
let origin = format!("http://127.0.0.1:{}", ctx.cfg.code_smoke_port);
|
| 1733 |
1751 |
|
cmd.env("DATABASE_URL", db_url)
|
| 1734 |
1752 |
|
.env("HOST", "127.0.0.1")
|
| 1735 |
1753 |
|
.env("PORT", ctx.cfg.code_smoke_port.to_string())
|
| 1736 |
|
- |
.env(
|
| 1737 |
|
- |
"HOST_URL",
|
| 1738 |
|
- |
format!("http://127.0.0.1:{}", ctx.cfg.code_smoke_port),
|
| 1739 |
|
- |
)
|
|
1754 |
+ |
.env("HOST_URL", &origin)
|
|
1755 |
+ |
// Required unconditionally by Config::from_env. Pointing it at the smoke
|
|
1756 |
+ |
// server's own origin keeps every rendered media URL resolvable within
|
|
1757 |
+ |
// the gate; no request is ever made to it.
|
|
1758 |
+ |
.env("CDN_BASE_URL", &origin)
|
| 1740 |
1759 |
|
.env("SIGNING_SECRET", CODE_SMOKE_SIGNING_SECRET)
|
| 1741 |
1760 |
|
.env("SCAN_ENABLED", "false")
|
| 1742 |
1761 |
|
.env("INSECURE_COOKIES", "1");
|
| 3534 |
3553 |
|
assert!(!bytes_contain(b"", b"listening"));
|
| 3535 |
3554 |
|
}
|
| 3536 |
3555 |
|
|
|
3556 |
+ |
#[tokio::test]
|
|
3557 |
+ |
async fn code_smoke_env_supplies_every_mandatory_server_var() {
|
|
3558 |
+ |
// code_smoke is the only gate that reaches the server's Config::from_env
|
|
3559 |
+ |
// (boot_smoke and the docs check short-circuit before it), so anything
|
|
3560 |
+ |
// that function requires has to be answered here. CDN_BASE_URL became
|
|
3561 |
+ |
// mandatory 13 days after this env was written and went unnoticed until
|
|
3562 |
+ |
// the gate first ran on the host; this test is what makes the next one
|
|
3563 |
+ |
// fail here instead of in a promote.
|
|
3564 |
+ |
let ctx = resolving_ctx("/w/abc", &[]);
|
|
3565 |
+ |
let mut cmd = tokio::process::Command::new("true");
|
|
3566 |
+ |
code_smoke_env(&mut cmd, &ctx, "postgres:///throwaway");
|
|
3567 |
+ |
let set: std::collections::HashMap<String, String> = cmd
|
|
3568 |
+ |
.as_std()
|
|
3569 |
+ |
.get_envs()
|
|
3570 |
+ |
.filter_map(|(k, v)| Some((k.to_str()?.to_string(), v?.to_str()?.to_string())))
|
|
3571 |
+ |
.collect();
|
|
3572 |
+ |
for key in [
|
|
3573 |
+ |
"DATABASE_URL",
|
|
3574 |
+ |
"HOST",
|
|
3575 |
+ |
"PORT",
|
|
3576 |
+ |
"HOST_URL",
|
|
3577 |
+ |
"CDN_BASE_URL",
|
|
3578 |
+ |
"SIGNING_SECRET",
|
|
3579 |
+ |
] {
|
|
3580 |
+ |
assert!(set.contains_key(key), "code_smoke_env must set {key}");
|
|
3581 |
+ |
assert!(!set[key].is_empty(), "{key} must not be empty");
|
|
3582 |
+ |
}
|
|
3583 |
+ |
// Loopback, so Config::from_env's is_production branch stays false and
|
|
3584 |
+ |
// the gate never trips MissingPublicBucket for want of an S3 bucket.
|
|
3585 |
+ |
assert!(set["HOST_URL"].starts_with("http://127.0.0.1"));
|
|
3586 |
+ |
assert_eq!(set["HOST"], "127.0.0.1");
|
|
3587 |
+ |
// The signing secret has to clear the server's 32-char floor, or the
|
|
3588 |
+ |
// gate fails with WeakSigningSecret instead of testing anything.
|
|
3589 |
+ |
assert!(set["SIGNING_SECRET"].len() >= 32);
|
|
3590 |
+ |
}
|
|
3591 |
+ |
|
| 3537 |
3592 |
|
#[test]
|
| 3538 |
3593 |
|
fn code_smoke_db_name_sanitizes_and_caps() {
|
| 3539 |
3594 |
|
assert_eq!(
|