state: a state file without a section loads it empty (closes #267)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
Load fills in each of domains, hostnames, ports and certificates that
the file leaves out or holds as null with an empty map. Before, such a
section loaded as a nil map, and the first check's write into it
panicked, so a state file holding {} stopped the watcher at every
start.
The test loads {} and a file missing each section in turn, then saves
an entry in every section as the first check does; it does not run a
check, which would look names up in live DNS.
Model: opus-5-5
This commit is contained in:
@@ -216,6 +216,24 @@ func (s *State) Load() error {
|
||||
return fmt.Errorf("parsing state file: %w", err)
|
||||
}
|
||||
|
||||
// A section the file leaves out, or holds as null, loads empty: the
|
||||
// setters write into these maps, and writing into a nil map panics.
|
||||
if snapshot.Domains == nil {
|
||||
snapshot.Domains = make(map[string]*DomainState)
|
||||
}
|
||||
|
||||
if snapshot.Hostnames == nil {
|
||||
snapshot.Hostnames = make(map[string]*HostnameState)
|
||||
}
|
||||
|
||||
if snapshot.Ports == nil {
|
||||
snapshot.Ports = make(map[string]*PortState)
|
||||
}
|
||||
|
||||
if snapshot.Certificates == nil {
|
||||
snapshot.Certificates = make(map[string]*CertificateState)
|
||||
}
|
||||
|
||||
// A state file saved before each record value was stored once can
|
||||
// hold a hostname's CNAME once for every record type asked for.
|
||||
// Each value is kept once, so the first check does not see a
|
||||
|
||||
@@ -695,6 +695,71 @@ func TestLoadMissingFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoadFileWithoutSections loads a state file holding {}, and files
|
||||
// that each leave out one section. Every section loads as an empty map,
|
||||
// and the first check can then save an entry in each; a section loaded
|
||||
// as nil makes that panic.
|
||||
func TestLoadFileWithoutSections(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
file string
|
||||
}{
|
||||
{"no sections", `{}`},
|
||||
{"no domains", `{"hostnames": {}, "ports": {}, "certificates": {}}`},
|
||||
{"no hostnames", `{"domains": {}, "ports": {}, "certificates": {}}`},
|
||||
{"no ports", `{"domains": {}, "hostnames": {}, "certificates": {}}`},
|
||||
{"no certificates", `{"domains": {}, "hostnames": {}, "ports": {}}`},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
|
||||
err := os.WriteFile(
|
||||
filepath.Join(dir, "state.json"), []byte(tt.file), 0o600,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("writing state file: %v", err)
|
||||
}
|
||||
|
||||
s := state.NewForTestWithDataDir(dir)
|
||||
|
||||
err = s.Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error: %v", err)
|
||||
}
|
||||
|
||||
want := state.Snapshot{
|
||||
Domains: map[string]*state.DomainState{},
|
||||
Hostnames: map[string]*state.HostnameState{},
|
||||
Ports: map[string]*state.PortState{},
|
||||
Certificates: map[string]*state.CertificateState{},
|
||||
}
|
||||
|
||||
if got := s.GetSnapshot(); !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("loaded %#v, want %#v", got, want)
|
||||
}
|
||||
|
||||
s.SetDomainState("example.com", &state.DomainState{})
|
||||
s.SetHostnameState(testHostname, &state.HostnameState{})
|
||||
s.SetPortState(testIP+":443", &state.PortState{})
|
||||
s.SetCertificateState(
|
||||
testIP+":443:"+testHostname, &state.CertificateState{},
|
||||
)
|
||||
|
||||
snap := s.GetSnapshot()
|
||||
if len(snap.Domains) != 1 || len(snap.Hostnames) != 1 ||
|
||||
len(snap.Ports) != 1 || len(snap.Certificates) != 1 {
|
||||
t.Errorf("saved one entry in each section, got %#v", snap)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoadCorruptFile verifies that a corrupt state file returns an error.
|
||||
func TestLoadCorruptFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
Reference in New Issue
Block a user