Add regression coverage for KDBX reopen cycles
This commit is contained in:
@@ -3,6 +3,7 @@ package vault
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/tobischo/gokeepasslib/v3"
|
||||
@@ -602,6 +603,114 @@ func TestKDBXRoundTripsEntryAttachments(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestKDBXReopenCyclesPreserveStableIDsAndCrossFeatureState(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
model := Model{
|
||||
Entries: []Entry{
|
||||
{
|
||||
ID: "entry-1",
|
||||
Title: "Git Server",
|
||||
Username: "joejulian",
|
||||
Password: "token-2",
|
||||
URL: "https://git.julianfamily.org",
|
||||
Notes: "Current credential",
|
||||
Path: []string{"Root", "Internet"},
|
||||
Attachments: map[string][]byte{
|
||||
"token.txt": []byte("secret attachment contents"),
|
||||
},
|
||||
History: []Entry{
|
||||
{
|
||||
ID: "entry-1-history-1",
|
||||
Title: "Git Server",
|
||||
Username: "joejulian",
|
||||
Password: "token-1",
|
||||
URL: "https://git.julianfamily.org",
|
||||
Notes: "Original credential",
|
||||
Path: []string{"Root", "Internet"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Templates: []Entry{
|
||||
{
|
||||
ID: "tpl-1",
|
||||
Title: "Website Login",
|
||||
Username: "template-user",
|
||||
Password: "template-password",
|
||||
Path: []string{"Templates", "Web"},
|
||||
},
|
||||
},
|
||||
RecycleBin: []Entry{
|
||||
{
|
||||
ID: "deleted-1",
|
||||
Title: "Retired Entry",
|
||||
Username: "archived-user",
|
||||
Password: "retired-token",
|
||||
Path: []string{"Root", "Archive"},
|
||||
},
|
||||
},
|
||||
Groups: [][]string{
|
||||
{"Root", "Archive"},
|
||||
{"Root", "Empty Group"},
|
||||
{"Templates", "Web"},
|
||||
},
|
||||
}
|
||||
|
||||
var encoded bytes.Buffer
|
||||
if err := SaveKDBX(&encoded, model, "correct horse battery staple"); err != nil {
|
||||
t.Fatalf("SaveKDBX(first cycle) error = %v", err)
|
||||
}
|
||||
|
||||
reopened, err := LoadKDBX(bytes.NewReader(encoded.Bytes()), "correct horse battery staple")
|
||||
if err != nil {
|
||||
t.Fatalf("LoadKDBX(first cycle) error = %v", err)
|
||||
}
|
||||
|
||||
encoded.Reset()
|
||||
if err := SaveKDBX(&encoded, reopened, "correct horse battery staple"); err != nil {
|
||||
t.Fatalf("SaveKDBX(second cycle) error = %v", err)
|
||||
}
|
||||
|
||||
reopened, err = LoadKDBX(bytes.NewReader(encoded.Bytes()), "correct horse battery staple")
|
||||
if err != nil {
|
||||
t.Fatalf("LoadKDBX(second cycle) error = %v", err)
|
||||
}
|
||||
|
||||
got := reopened.EntriesInPath([]string{"Root", "Internet"})
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("len(EntriesInPath(Root/Internet)) = %d, want 1", len(got))
|
||||
}
|
||||
if got[0].ID != "entry-1" {
|
||||
t.Fatalf("entry ID after reopen cycles = %q, want %q", got[0].ID, "entry-1")
|
||||
}
|
||||
if len(got[0].History) != 1 {
|
||||
t.Fatalf("len(History) after reopen cycles = %d, want 1", len(got[0].History))
|
||||
}
|
||||
if got[0].History[0].ID != "entry-1-history-1" {
|
||||
t.Fatalf("history ID after reopen cycles = %q, want %q", got[0].History[0].ID, "entry-1-history-1")
|
||||
}
|
||||
if string(got[0].Attachments["token.txt"]) != "secret attachment contents" {
|
||||
t.Fatalf("attachment after reopen cycles = %q, want %q", string(got[0].Attachments["token.txt"]), "secret attachment contents")
|
||||
}
|
||||
|
||||
if len(reopened.Templates) != 1 || reopened.Templates[0].Path[1] != "Web" {
|
||||
t.Fatalf("Templates after reopen cycles = %#v, want Website Login in Templates/Web", reopened.Templates)
|
||||
}
|
||||
if len(reopened.RecycleBin) != 1 || reopened.RecycleBin[0].Path[1] != "Archive" {
|
||||
t.Fatalf("RecycleBin after reopen cycles = %#v, want recycled entry in Root/Archive", reopened.RecycleBin)
|
||||
}
|
||||
|
||||
rootGroups := reopened.ChildGroups([]string{"Root"})
|
||||
if !slices.Equal(rootGroups, []string{"Archive", "Empty Group", "Internet"}) {
|
||||
t.Fatalf("ChildGroups(Root) after reopen cycles = %v, want [Archive Empty Group Internet]", rootGroups)
|
||||
}
|
||||
templateGroups := reopened.ChildGroups([]string{"Templates"})
|
||||
if !slices.Equal(templateGroups, []string{"Web"}) {
|
||||
t.Fatalf("ChildGroups(Templates) after reopen cycles = %v, want [Web]", templateGroups)
|
||||
}
|
||||
}
|
||||
|
||||
func mustGroup(name string, children ...any) gokeepasslib.Group {
|
||||
group := gokeepasslib.NewGroup()
|
||||
group.Name = name
|
||||
|
||||
Reference in New Issue
Block a user