Add regression coverage for KDBX reopen cycles
This commit is contained in:
@@ -537,3 +537,132 @@ func TestSavePreservesOpenedKDBXSecuritySettings(t *testing.T) {
|
||||
t.Fatalf("saved KDF UUID = %x, want %x", reloaded.Header.FileHeaders.KdfParameters.UUID, db.Header.FileHeaders.KdfParameters.UUID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteSaveAndReopenPreservesCrossFeatureState(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key := vault.MasterKey{Password: "correct horse battery staple"}
|
||||
model := vault.Model{
|
||||
Entries: []vault.Entry{
|
||||
{
|
||||
ID: "entry-1",
|
||||
Title: "Git Server",
|
||||
Username: "joejulian",
|
||||
Password: "token-2",
|
||||
URL: "https://git.julianfamily.org",
|
||||
Path: []string{"Root", "Internet"},
|
||||
Attachments: map[string][]byte{
|
||||
"token.txt": []byte("secret attachment contents"),
|
||||
},
|
||||
History: []vault.Entry{
|
||||
{
|
||||
ID: "entry-1-history-1",
|
||||
Title: "Git Server",
|
||||
Username: "joejulian",
|
||||
Password: "token-1",
|
||||
URL: "https://git.julianfamily.org",
|
||||
Path: []string{"Root", "Internet"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Templates: []vault.Entry{
|
||||
{
|
||||
ID: "tpl-1",
|
||||
Title: "Website Login",
|
||||
Username: "template-user",
|
||||
Password: "template-password",
|
||||
Path: []string{"Templates", "Web"},
|
||||
},
|
||||
},
|
||||
RecycleBin: []vault.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 remoteBytes bytes.Buffer
|
||||
if err := vault.SaveKDBXWithKey(&remoteBytes, model, key); err != nil {
|
||||
t.Fatalf("SaveKDBXWithKey(seed remote) error = %v", err)
|
||||
}
|
||||
|
||||
etag := "\"v1\""
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
w.Header().Set("ETag", etag)
|
||||
_, _ = w.Write(remoteBytes.Bytes())
|
||||
case http.MethodPut:
|
||||
if got := r.Header.Get("If-Match"); got != etag {
|
||||
t.Fatalf("If-Match header = %q, want %q", got, etag)
|
||||
}
|
||||
|
||||
payload, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadAll(PUT body) error = %v", err)
|
||||
}
|
||||
|
||||
remoteBytes.Reset()
|
||||
if _, err := remoteBytes.Write(payload); err != nil {
|
||||
t.Fatalf("Write(remoteBytes) error = %v", err)
|
||||
}
|
||||
|
||||
etag = "\"v2\""
|
||||
w.Header().Set("ETag", etag)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
default:
|
||||
t.Fatalf("unexpected method %s", r.Method)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := webdav.Client{BaseURL: server.URL}
|
||||
|
||||
var sess Manager
|
||||
if err := sess.OpenRemote(client, "vaults/main.kdbx", key); err != nil {
|
||||
t.Fatalf("OpenRemote() error = %v", err)
|
||||
}
|
||||
if err := sess.SaveRemote(); err != nil {
|
||||
t.Fatalf("SaveRemote() error = %v", err)
|
||||
}
|
||||
|
||||
var reopened Manager
|
||||
if err := reopened.OpenRemote(client, "vaults/main.kdbx", key); err != nil {
|
||||
t.Fatalf("reopen OpenRemote() error = %v", err)
|
||||
}
|
||||
|
||||
current, err := reopened.Current()
|
||||
if err != nil {
|
||||
t.Fatalf("Current() after reopen error = %v", err)
|
||||
}
|
||||
|
||||
got := current.EntriesInPath([]string{"Root", "Internet"})
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("len(EntriesInPath(Root/Internet)) after reopen = %d, want 1", len(got))
|
||||
}
|
||||
if got[0].ID != "entry-1" {
|
||||
t.Fatalf("entry ID after remote reopen = %q, want %q", got[0].ID, "entry-1")
|
||||
}
|
||||
if len(got[0].History) != 1 || got[0].History[0].ID != "entry-1-history-1" {
|
||||
t.Fatalf("History after remote reopen = %#v, want stable history ID entry-1-history-1", got[0].History)
|
||||
}
|
||||
if string(got[0].Attachments["token.txt"]) != "secret attachment contents" {
|
||||
t.Fatalf("attachment after remote reopen = %q, want %q", string(got[0].Attachments["token.txt"]), "secret attachment contents")
|
||||
}
|
||||
if len(current.Templates) != 1 || current.Templates[0].Path[1] != "Web" {
|
||||
t.Fatalf("Templates after remote reopen = %#v, want Website Login in Templates/Web", current.Templates)
|
||||
}
|
||||
if len(current.RecycleBin) != 1 || current.RecycleBin[0].Path[1] != "Archive" {
|
||||
t.Fatalf("RecycleBin after remote reopen = %#v, want retired entry in Root/Archive", current.RecycleBin)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user