package backup import ( "encoding/json" "errors" "strings" "testing" "time" ) func TestManifestJSONShape(t *testing.T) { fp := "abc" m := Manifest{ FormatVersion: FormatVersion, CreatedAt: time.Date(2026, 9, 7, 12, 0, 0, 0, time.UTC), VantageVersion: "dev", Hostname: "box", MongoDB: "vantage", MongoServerVersion: "7.0.5", KeyFingerprint: &fp, Collections: []CollectionEntry{{Name: "servers", Documents: 3, Bytes: 120, SHA256: "dead"}}, Excluded: []string{"audit_logs"}, } raw, err := json.Marshal(m) if err != nil { t.Fatalf("marshal: %v", err) } for _, want := range []string{ `"format_version":1`, `"created_at":"2026-09-07T12:00:00Z"`, `"key_fingerprint":"abc"`, `"mongo_server_version":"7.0.5"`, `"excluded":["audit_logs"]`, } { if !strings.Contains(string(raw), want) { t.Fatalf("manifest JSON missing %s\ngot: %s", want, raw) } } } func TestManifestNullFingerprint(t *testing.T) { raw, err := json.Marshal(Manifest{FormatVersion: FormatVersion}) if err != nil { t.Fatalf("marshal: %v", err) } if !strings.Contains(string(raw), `"key_fingerprint":null`) { t.Fatalf("absent key must marshal as null, got: %s", raw) } } func TestManifestCheckRejectsOtherVersions(t *testing.T) { if err := (Manifest{FormatVersion: FormatVersion}).Check(); err != nil { t.Fatalf("current version rejected: %v", err) } for _, v := range []int{0, 2, 99} { if err := (Manifest{FormatVersion: v}).Check(); !errors.Is(err, ErrUnknownFormat) { t.Fatalf("version %d: got %v, want ErrUnknownFormat", v, err) } } } func TestManifestCollectionLookup(t *testing.T) { m := Manifest{Collections: []CollectionEntry{{Name: "keys", Documents: 1}}} if _, ok := m.Collection("keys"); !ok { t.Fatal("known collection not found") } if _, ok := m.Collection("nope"); ok { t.Fatal("unknown collection reported as found") } } func TestCiphertextCollections(t *testing.T) { got := CiphertextCollections() want := []string{"keys", "secrets", "auth_providers", "console_sessions"} if len(got) != len(want) { t.Fatalf("got %v, want %v", got, want) } for i := range want { if got[i] != want[i] { t.Fatalf("got %v, want %v", got, want) } } } // TestManifestRefusesUnusableCollectionNames covers names being used as path // components inside the extraction directory. An archive is operator-supplied // input and may not be one we wrote. func TestManifestRefusesUnusableCollectionNames(t *testing.T) { for _, name := range []string{"", ".", "..", "../etc/passwd", "a/b", "a..b"} { m := Manifest{ FormatVersion: FormatVersion, Collections: []CollectionEntry{{Name: name}}, } if err := m.Check(); !errors.Is(err, ErrBadCollectionName) { t.Fatalf("collection name %q was accepted (err %v)", name, err) } } m := Manifest{ FormatVersion: FormatVersion, Collections: []CollectionEntry{{Name: "workflow_log_lines"}}, } if err := m.Check(); err != nil { t.Fatalf("an ordinary collection name was refused: %v", err) } }