package config import ( "os" "path/filepath" "testing" "textmachine/platform/internal/money" "time" ) func TestDefaultsAndOverrides(t *testing.T) { c, err := Load() if err != nil { t.Fatal(err) } if c.Addr != "127.0.0.1:8080" { t.Fatalf("default addr = %q; a control plane must not bind the world by default", c.Addr) } if c.Migrate { t.Fatal("migrations must be opt-in") } t.Setenv("TM_PLATFORM_ADDR", ":9000") t.Setenv("TM_PLATFORM_SESSION_IDLE", "30m") t.Setenv("TM_PLATFORM_TRUSTED_ORIGINS", "https://app.example.org, https://desktop.example.org ") c, err = Load() if err != nil { t.Fatal(err) } if c.Addr != ":9000" || c.SessionIdleTTL != 30*time.Minute { t.Fatalf("config = %+v", c) } if len(c.TrustedOrigins) != 2 || c.TrustedOrigins[1] != "https://desktop.example.org" { t.Fatalf("origins = %q", c.TrustedOrigins) } } // A RELATIVE state directory names two different directories: the exit marker is written by a unit // whose WorkingDirectory is the BOOK's directory and read by this daemon from its own. The run then // ends, its marker lands where nothing looks, and the reconciler restarts the run forever. func TestARelativeStateDirectoryIsRefusedAtBoot(t *testing.T) { t.Setenv("TM_PLATFORM_STATE_DIR", "var/run-state") if _, err := Load(); err == nil { t.Fatal("a relative state directory was accepted; the marker it writes is unreadable by the daemon") } t.Setenv("TM_PLATFORM_STATE_DIR", "/var/lib/other") c, err := Load() if err != nil { t.Fatal(err) } if c.Runner.StateDir != "/var/lib/other" { t.Errorf("state dir = %q", c.Runner.StateDir) } } // The engine's ceiling flag landed (row 145, D39.122), so its form is the default rather than one // more thing an operator must set before any run can start. An override that cannot carry the amount // is refused instead of being sent as a literal. func TestTheCeilingArgumentHasTheLandedFormByDefault(t *testing.T) { c, err := Load() if err != nil { t.Fatal(err) } if c.Runner.CeilingArg != "--ceiling-usd {{usd}}" { t.Errorf("default ceiling argument = %q", c.Runner.CeilingArg) } } func TestImpossibleSessionWindowIsRefusedAtBoot(t *testing.T) { t.Setenv("TM_PLATFORM_SESSION_IDLE", "100h") t.Setenv("TM_PLATFORM_SESSION_MAX_AGE", "1h") if _, err := Load(); err == nil { t.Fatal("an idle window longer than the absolute one must fail at boot, not at 3am") } } func TestMalformedDurationIsRefused(t *testing.T) { t.Setenv("TM_PLATFORM_SESSION_IDLE", "fortnight") if _, err := Load(); err == nil { t.Fatal("want a parse error") } } // A secret read from a file never enters the process environment, which is where a credential // leaks from first (child processes inherit it, /proc exposes it). systemd hands one over this way. func TestSecretsCanComeFromFiles(t *testing.T) { path := filepath.Join(t.TempDir(), "dsn") if err := os.WriteFile(path, []byte("postgres://u:p@h/db\n"), 0o600); err != nil { t.Fatal(err) } t.Setenv("TM_PLATFORM_DSN_FILE", path) c, err := Load() if err != nil { t.Fatal(err) } if c.DSN != "postgres://u:p@h/db" { t.Fatalf("DSN = %q (trailing newline must be trimmed)", c.DSN) } t.Setenv("TM_PLATFORM_DSN_FILE", filepath.Join(t.TempDir(), "absent")) if _, err := Load(); err == nil { t.Fatal("a named secret file that cannot be read must fail at boot, not at the first query") } } // Half a login configuration mounts a surface that fails at the first click instead of at boot. func TestPartialOIDCConfigurationIsRefused(t *testing.T) { t.Setenv("TM_PLATFORM_OIDC_ISSUER", "https://accounts.google.com") t.Setenv("TM_PLATFORM_OIDC_CLIENT_ID", "id") if _, err := Load(); err == nil { t.Fatal("an issuer without a secret and a redirect must be refused") } t.Setenv("TM_PLATFORM_OIDC_CLIENT_SECRET", "s") t.Setenv("TM_PLATFORM_OIDC_REDIRECT_URL", "https://app.example.org/auth/callback") c, err := Load() if err != nil { t.Fatal(err) } if !c.LoginEnabled() { t.Fatal("a complete configuration must enable sign-in") } } // The free tier is a number an operator sets, and a bad one must not become a silent zero. func TestSignupGrantIsParsedNotGuessed(t *testing.T) { if c, err := Load(); err != nil || c.SignupGrantMicroUSD != 5*money.PerUSD { t.Fatalf("default grant = %d (%v)", c.SignupGrantMicroUSD, err) } t.Setenv("TM_PLATFORM_SIGNUP_GRANT_USD", "2.50") c, err := Load() if err != nil { t.Fatal(err) } if c.SignupGrantMicroUSD != 2_500_000 { t.Fatalf("grant = %d", c.SignupGrantMicroUSD) } t.Setenv("TM_PLATFORM_SIGNUP_GRANT_USD", "five dollars") if _, err := Load(); err == nil { t.Fatal("an unparseable grant must fail at boot") } } // PD-58. The session clocks are a declared conformance point, not a preference: ASVS 5.0 7.1.1 // takes the baseline from NIST SP 800-63B-4, whose AAL1 rule is that the overall reauthentication // timeout SHOULD be no more than 30 days. The reasoning lives in STACK_DECISIONS §13; this keeps // the defaults from drifting past it without someone changing that document too. // Mutation caught: raising either default beyond the norm. func TestSessionClocksStayWithinTheDeclaredBaseline(t *testing.T) { c, err := Load() if err != nil { t.Fatal(err) } const aal1Overall = 30 * 24 * time.Hour if c.SessionMaxAge > aal1Overall { t.Errorf("absolute session lifetime is %s, above the NIST SP 800-63B-4 AAL1 figure of %s: a deviation needs a written justification (ASVS 7.1.1)", c.SessionMaxAge, aal1Overall) } if c.SessionIdleTTL <= 0 || c.SessionIdleTTL > c.SessionMaxAge { t.Errorf("idle window is %s against an absolute of %s: an idle window that cannot expire first is not one", c.SessionIdleTTL, c.SessionMaxAge) } } // The runner's settings decide whether a run can start at all, so the failure modes have to be // visible at LOAD, where an operator is watching, rather than at the first click. func TestTheRunnerSettingsFailAtLoadAndNotAtTheFirstRun(t *testing.T) { t.Setenv("TM_PLATFORM_RUN_TASKS_MAX", "not a number") if _, err := Load(); err == nil { t.Error("a non-numeric task cap was accepted") } t.Setenv("TM_PLATFORM_RUN_TASKS_MAX", "0") if _, err := Load(); err == nil { t.Error("a task cap of zero was accepted") } t.Setenv("TM_PLATFORM_RUN_TASKS_MAX", "") t.Setenv("TM_PLATFORM_USD_PER_CHAPTER", "0") if _, err := Load(); err == nil { t.Error("a per-chapter rate of zero was accepted: it makes every ceiling free") } t.Setenv("TM_PLATFORM_USD_PER_CHAPTER", "") t.Setenv("TM_PLATFORM_RESYNC_EVERY", "-5m") if _, err := Load(); err == nil { t.Error("a negative resync interval was accepted") } } // The default rate is resolved HERE and nowhere else, so there is one answer to "what is this // instance using" instead of one per caller. (It used to be resolved by the daemon, which meant a // zero in the config and a real number in the process — found by the owner's question, 08.08.) func TestThePerChapterRateAlwaysHasAValue(t *testing.T) { t.Setenv("TM_PLATFORM_USD_PER_CHAPTER", "") c, err := Load() if err != nil { t.Fatal(err) } if c.Runner.PerChapterMicroUSD <= 0 { t.Fatalf("the rate is %d: the default has to be resolved in config", c.Runner.PerChapterMicroUSD) } t.Setenv("TM_PLATFORM_USD_PER_CHAPTER", "0.05") if c, err = Load(); err != nil || c.Runner.PerChapterMicroUSD != 50_000 { t.Fatalf("an explicit rate gave %d (%v)", c.Runner.PerChapterMicroUSD, err) } } // An instance with no engine binary is a read replica, not a broken one: the library is not run // machinery, and refusing to start would turn a partial capability into a total outage. func TestAnInstanceWithoutAnEngineStillLoads(t *testing.T) { t.Setenv("TM_PLATFORM_ENGINE_BIN", "") c, err := Load() if err != nil { t.Fatal(err) } if c.RunsEnabled() { t.Error("runs are reported as enabled with no engine binary configured") } }