package config import ( "testing" "time" ) // Backups are OFF unless an operator says where, and the deployment says so loudly at boot rather // than inventing a directory of its own. A default path would land beside the books and the database // — the one failure a backup exists to survive. func TestBackupsAreOffUntilAnOperatorSaysWhere(t *testing.T) { c, err := Load() if err != nil { t.Fatal(err) } if c.BackupEnabled() || c.Backup.Dir != "" { t.Fatalf("a backup directory was invented: %q", c.Backup.Dir) } // The defaults that apply once it IS on. Six hours and fourteen points is four days of history, // which is what covers a fault discovered after a weekend. if c.Backup.Every != 6*time.Hour || c.Backup.Keep != 14 { t.Errorf("schedule defaults: every=%s keep=%d", c.Backup.Every, c.Backup.Keep) } if c.Backup.PgDumpBin != "pg_dump" || c.Backup.PgRestoreBin != "pg_restore" { t.Errorf("the standard tools are not the default: %q %q", c.Backup.PgDumpBin, c.Backup.PgRestoreBin) } } // A relative backup directory is refused at boot, like the state directory and for a sharper reason: // it would put the deployment's only copy of itself wherever the process happened to be started. func TestARelativeBackupDirectoryIsRefusedAtBoot(t *testing.T) { t.Setenv("TM_PLATFORM_BACKUP_DIR", "backups") if _, err := Load(); err == nil { t.Fatal("a relative backup directory was accepted") } t.Setenv("TM_PLATFORM_BACKUP_DIR", "/srv/backups") c, err := Load() if err != nil || !c.BackupEnabled() { t.Fatalf("an absolute one was not: %v", err) } } // Two settings that would quietly destroy what they configure: a schedule of zero takes a full copy // of the deployment on every sweep tick, and keeping zero points deletes the one the pass has just // written. Both are refused — by the LOADER, which takes only positive durations and counts, so // there is one definition of "positive" rather than a per-knob one. // // ⚠ This test is why the second, per-knob guard is not in the code. It was written first, gated on // the backup directory being set, and this test — which asserts the refusal happens either way — // showed the guard could never fire: the loader had already refused. A check whose message no // operator can ever read is not a safety net, it is a claim about behaviour that is false. func TestABackupScheduleThatWouldEatItselfIsRefused(t *testing.T) { for _, dir := range []string{"/srv/backups", ""} { t.Setenv("TM_PLATFORM_BACKUP_DIR", dir) t.Setenv("TM_PLATFORM_BACKUP_EVERY", "0s") t.Setenv("TM_PLATFORM_BACKUP_KEEP", "14") if _, err := Load(); err == nil { t.Errorf("dir=%q: a zero interval was accepted: a restore point per sweep tick", dir) } t.Setenv("TM_PLATFORM_BACKUP_EVERY", "1h") t.Setenv("TM_PLATFORM_BACKUP_KEEP", "0") if _, err := Load(); err == nil { t.Errorf("dir=%q: keeping zero points was accepted: the pass would delete its own work", dir) } t.Setenv("TM_PLATFORM_BACKUP_KEEP", "1") if _, err := Load(); err != nil { t.Errorf("dir=%q: keeping one point was refused: %v", dir, err) } } } // The session policy has ONE definition, and `tmplatformctl token issue` reads it through this // function rather than keeping its own defaults. Two readers is how a token minted by the operator // comes to outlive the sessions the deployment says it issues. func TestTheSessionPolicyHasOneDefinitionForBothReaders(t *testing.T) { idle, maxAge, err := SessionTTLs() if err != nil { t.Fatal(err) } c, err := Load() if err != nil { t.Fatal(err) } if idle != c.SessionIdleTTL || maxAge != c.SessionMaxAge { t.Fatalf("the CLI and the daemon read different clocks: %s/%s vs %s/%s", idle, maxAge, c.SessionIdleTTL, c.SessionMaxAge) } t.Setenv("TM_PLATFORM_SESSION_IDLE", "72h") t.Setenv("TM_PLATFORM_SESSION_MAX_AGE", "240h") idle, maxAge, err = SessionTTLs() if err != nil || idle != 72*time.Hour || maxAge != 240*time.Hour { t.Fatalf("the operator's own policy did not reach the CLI: %s/%s (%v)", idle, maxAge, err) } // The same refusal the daemon makes: an idle window longer than the absolute one is a policy // that cannot mean anything. t.Setenv("TM_PLATFORM_SESSION_IDLE", "500h") if _, _, err := SessionTTLs(); err == nil { t.Error("an idle TTL beyond the absolute ceiling was accepted by the CLI's reader") } }