139 lines
4.5 KiB
Go
139 lines
4.5 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
func TestFixedSectionFiles(t *testing.T) {
|
|
expected := map[Section]string{
|
|
SectionRoot: "config.json",
|
|
SectionSMTP: "smtp.json",
|
|
SectionEmail: "email.json",
|
|
SectionSSL: "ssl.json",
|
|
SectionPlugins: "plugins.json",
|
|
}
|
|
for section, want := range expected {
|
|
got, ok := FixedSectionFile(section)
|
|
if !ok || got != want {
|
|
t.Fatalf("FixedSectionFile(%q) = %q, %t", section, got, ok)
|
|
}
|
|
}
|
|
if _, ok := FixedSectionFile(Section("../config")); ok {
|
|
t.Fatal("unknown section received a file mapping")
|
|
}
|
|
}
|
|
|
|
func TestValidateSectionReferences(t *testing.T) {
|
|
generation := uint64(12)
|
|
references := validSectionReferences(generation)
|
|
if err := ValidateSectionReferences(references, generation); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
mutate func(map[Section]SectionRef)
|
|
want error
|
|
}{
|
|
{name: "missing", mutate: func(items map[Section]SectionRef) { delete(items, SectionSMTP) }, want: ErrInvalidSectionReference},
|
|
{name: "unknown", mutate: func(items map[Section]SectionRef) { items[Section("unknown")] = items[SectionSMTP] }, want: ErrInvalidSectionReference},
|
|
{name: "path", mutate: func(items map[Section]SectionRef) {
|
|
item := items[SectionSMTP]
|
|
item.File = "../smtp.json"
|
|
items[SectionSMTP] = item
|
|
}, want: ErrInvalidSectionReference},
|
|
{name: "schema", mutate: func(items map[Section]SectionRef) {
|
|
item := items[SectionSMTP]
|
|
item.Schema++
|
|
items[SectionSMTP] = item
|
|
}, want: ErrInvalidSectionReference},
|
|
{name: "generation", mutate: func(items map[Section]SectionRef) {
|
|
item := items[SectionSMTP]
|
|
item.Generation++
|
|
items[SectionSMTP] = item
|
|
}, want: ErrSectionGenerationMismatch},
|
|
{name: "digest", mutate: func(items map[Section]SectionRef) {
|
|
item := items[SectionSMTP]
|
|
item.SHA256 = "ABC"
|
|
items[SectionSMTP] = item
|
|
}, want: ErrInvalidSectionReference},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
candidate := cloneSectionReferences(references)
|
|
test.mutate(candidate)
|
|
if err := ValidateSectionReferences(candidate, generation); !errors.Is(err, test.want) {
|
|
t.Fatalf("error = %v, want %v", err, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSectionMetaAndDigest(t *testing.T) {
|
|
digest := SectionDigest([]byte(`{"meta":{"schema":1,"generation":3}}`))
|
|
if !validSHA256(digest) {
|
|
t.Fatalf("invalid digest %q", digest)
|
|
}
|
|
if err := ValidateSectionMeta(SectionSMTP, SectionMeta{Schema: CurrentSectionSchema, Generation: 3}, 3); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := ValidateSectionMeta(SectionRoot, SectionMeta{Schema: CurrentSectionSchema, Generation: 3}, 3); !errors.Is(err, ErrInvalidSection) {
|
|
t.Fatalf("error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRootConfigSectionReferencesRoundTrip(t *testing.T) {
|
|
root := testRootConfig()
|
|
root.Domains = DomainConfig{Primary: "mail.example.com", Accepted: []string{"example.com"}, Web: "mail.example.com"}
|
|
root.HTTP = HTTPConfig{Port: 80, HTTPSPort: 443, HTTPSEnabled: true}
|
|
root.Sections = validSectionReferences(root.Generation)
|
|
data, err := json.Marshal(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var decoded RootConfig
|
|
if err := ParseStrict(data, &decoded); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := ValidateRootConfig(decoded); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if decoded.Sections[SectionPlugins].File != "plugins.json" {
|
|
t.Fatalf("decoded references = %+v", decoded.Sections)
|
|
}
|
|
}
|
|
|
|
func TestRootConfigCloneDoesNotShareSectionData(t *testing.T) {
|
|
root := testRootConfig()
|
|
root.Domains.Accepted = []string{"example.com"}
|
|
root.Sections = validSectionReferences(root.Generation)
|
|
clone := cloneRootConfig(root)
|
|
clone.Domains.Accepted[0] = "attacker.example"
|
|
item := clone.Sections[SectionSMTP]
|
|
item.File = "changed.json"
|
|
clone.Sections[SectionSMTP] = item
|
|
if root.Domains.Accepted[0] != "example.com" || root.Sections[SectionSMTP].File != "smtp.json" {
|
|
t.Fatal("root clone shares mutable section data")
|
|
}
|
|
}
|
|
|
|
func validSectionReferences(generation uint64) map[Section]SectionRef {
|
|
result := make(map[Section]SectionRef, len(requiredPartitionSections))
|
|
for _, section := range requiredPartitionSections {
|
|
file, _ := FixedSectionFile(section)
|
|
schema, _ := FixedSectionSchema(section)
|
|
result[section] = SectionRef{File: file, Schema: schema, Generation: generation, SHA256: SectionDigest([]byte(section))}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func cloneSectionReferences(source map[Section]SectionRef) map[Section]SectionRef {
|
|
result := make(map[Section]SectionRef, len(source))
|
|
for section, reference := range source {
|
|
result[section] = reference
|
|
}
|
|
return result
|
|
}
|