Export the reviewed allowlisted snapshot from private source commit 8aab3db43f35e6a49aa497f45d73701b13fc9f32 and tree 992132ea4703437dc13ffdbb04a077816c02caf9. This includes routed singleton continuity, deployment evidence, strict schema-2 configuration, restricted transport, and the independently compilable public-tree guard. AI-Assisted: OpenAI Codex Signed-off-by: Cole Speelman <crspeelman@gmail.com>
91 lines
3.7 KiB
Go
91 lines
3.7 KiB
Go
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
)
|
|
|
|
func validConfig() Config {
|
|
return Config{
|
|
SchemaVersion: 2,
|
|
Service: Service{Name: "example-site", AllowedHost: "example.test", EnvironmentFile: "/etc/tend/environment/example-site.env"},
|
|
Build: Build{Package: "./cmd/site", Binary: "example-site", Branch: "main"},
|
|
Deployment: Deployment{
|
|
Strategy: "blue_green", Root: "/opt/example-site", LockFile: SharedLockFile,
|
|
StateFile: "/opt/example-site/state.json", EventLog: "/opt/example-site/deployment-events.jsonl", HealthPath: "/healthz", ReadinessPath: "/readyz",
|
|
CandidateTimeoutSecs: 30, ActivationWindowSecs: 10, Smoke: []Smoke{{Path: "/", Contains: "Example"}},
|
|
PublicSmoke: []PublicSmoke{{URL: "https://example.test/", Contains: "Example"}},
|
|
BlueGreen: &BlueGreen{
|
|
CaddyConfig: "/etc/caddy/Caddyfile", CaddyHandler: "/etc/caddy/example.caddy",
|
|
CaddyHandlerTemplate: "/etc/example/caddy.template",
|
|
BootstrapActive: "blue",
|
|
Blue: Slot{Unit: "example-blue.service", Address: "127.0.0.1:8090", Link: "/opt/example-site/slots/blue"},
|
|
Green: Slot{Unit: "example-green.service", Address: "127.0.0.1:8091", Link: "/opt/example-site/slots/green"},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestValidateAcceptsBlueGreen(t *testing.T) {
|
|
if err := validConfig().Validate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestValidateSingletonRequiresDistinctCaddyHandoffFiles(t *testing.T) {
|
|
cfg := validConfig()
|
|
cfg.Deployment.Strategy = "singleton_candidate"
|
|
cfg.Deployment.BlueGreen = nil
|
|
cfg.Deployment.Singleton = &Singleton{
|
|
Unit: "example-site.service", Address: "127.0.0.1:8092", CandidateAddress: "127.0.0.1:18092", ListenEnv: "EXAMPLE_LISTEN",
|
|
CurrentLink: "/opt/example-site/current", PreviousLink: "/opt/example-site/previous", CaddyConfig: "/etc/caddy/Caddyfile",
|
|
CaddyHandler: "/etc/caddy/example-site.caddy", CaddyHandlerTemplate: "/etc/tend/caddy/example-site.template",
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cfg.Deployment.Singleton.CaddyHandlerTemplate = cfg.Deployment.Singleton.CaddyHandler
|
|
if err := cfg.Validate(); err == nil {
|
|
t.Fatal("expected shared handler/template path to be rejected")
|
|
}
|
|
}
|
|
|
|
func TestValidateRejectsHostileValues(t *testing.T) {
|
|
tests := map[string]func(*Config){
|
|
"unknown strategy": func(c *Config) { c.Deployment.Strategy = "shell" },
|
|
"nonloopback": func(c *Config) { c.Deployment.BlueGreen.Blue.Address = "203.0.113.7:80" },
|
|
"root path": func(c *Config) { c.Deployment.Root = "/" },
|
|
"traversal": func(c *Config) { c.Build.Package = "./cmd/../secret" },
|
|
"shared slot": func(c *Config) { c.Deployment.BlueGreen.Green.Link = c.Deployment.BlueGreen.Blue.Link },
|
|
"bad smoke": func(c *Config) { c.Deployment.Smoke[0].Path = "https://attacker.test/" },
|
|
"bad public smoke": func(c *Config) { c.Deployment.PublicSmoke[0].URL = "http://example.test/" },
|
|
"public secret query": func(c *Config) { c.Deployment.PublicSmoke[0].URL = "https://example.test/?token=secret" },
|
|
"environment sibling": func(c *Config) { c.Service.EnvironmentFile = "/etc/tend/environment-old/example.env" },
|
|
}
|
|
for name, mutate := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
cfg := validConfig()
|
|
mutate(&cfg)
|
|
if err := cfg.Validate(); err == nil {
|
|
t.Fatal("expected validation failure")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUnknownJSONFieldRejected(t *testing.T) {
|
|
b, err := json.Marshal(validConfig())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var raw map[string]any
|
|
if err := json.Unmarshal(b, &raw); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
raw["surprise"] = true
|
|
b, _ = json.Marshal(raw)
|
|
_ = b // Load exercises strict decoding from disk in command tests.
|
|
}
|