From 0ce25a971202efa0a994e7eebe3468ff19477a09 Mon Sep 17 00:00:00 2001 From: Joe Julian Date: Mon, 13 Apr 2026 07:00:51 -0700 Subject: [PATCH] Add failing vault view behavior tests --- TODO.md | 4 -- internal/vaultview/view_test.go | 101 ++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 internal/vaultview/view_test.go diff --git a/TODO.md b/TODO.md index 164fb45..2519600 100644 --- a/TODO.md +++ b/TODO.md @@ -8,10 +8,6 @@ The product is not complete until the global exit criteria at the end of this fi ## Priority Bugs -- Vault root view bug: - add failing behavior tests for explicit `Vault`, `VaultRoot`, and - `VaultRecycleBin` datastore views, including `keepass` path translation and - recycle-bin projection. - Vault root view bug: introduce explicit `Vault`, `VaultRoot`, and `VaultRecycleBin` view factories in `internal/vaultview` and move hidden-root behavior out of UI heuristics. diff --git a/internal/vaultview/view_test.go b/internal/vaultview/view_test.go new file mode 100644 index 0000000..ef0904f --- /dev/null +++ b/internal/vaultview/view_test.go @@ -0,0 +1,101 @@ +package vaultview + +import ( + "slices" + "testing" + + "git.julianfamily.org/keepassgo/internal/vault" +) + +func TestVaultRootProjectsKeepassStorageRoot(t *testing.T) { + t.Parallel() + + model := vault.Model{ + Entries: []vault.Entry{ + {ID: "bellagio-ledger", Title: "Bellagio Ledger", Path: []string{"keepass", "Crew", "Internet"}}, + {ID: "fountain-cameras", Title: "Fountain Cameras", Path: []string{"keepass", "Crew", "Security"}}, + }, + Groups: [][]string{ + {"keepass"}, + {"keepass", "Crew"}, + {"keepass", "Crew", "Internet"}, + {"keepass", "Crew", "Security"}, + {"Recycle Bin"}, + }, + } + + view := VaultRoot(model) + + if got := view.ChildGroups(nil); !slices.Equal(got, []string{"Crew"}) { + t.Fatalf("VaultRoot(model).ChildGroups(nil) = %v, want [Crew]", got) + } + if got := view.ChildGroups([]string{"Crew"}); !slices.Equal(got, []string{"Internet", "Security"}) { + t.Fatalf("VaultRoot(model).ChildGroups([Crew]) = %v, want [Internet Security]", got) + } + + gotEntries := view.EntriesInPath([]string{"Crew", "Internet"}) + if len(gotEntries) != 1 || !slices.Equal(gotEntries[0].Path, []string{"Crew", "Internet"}) { + t.Fatalf("VaultRoot(model).EntriesInPath([Crew Internet]) = %#v, want logical path [Crew Internet]", gotEntries) + } + + if got := view.ToPhysicalPath(nil); !slices.Equal(got, []string{"keepass"}) { + t.Fatalf("VaultRoot(model).ToPhysicalPath(nil) = %v, want [keepass]", got) + } + if got := view.ToPhysicalPath([]string{"Crew", "Internet"}); !slices.Equal(got, []string{"keepass", "Crew", "Internet"}) { + t.Fatalf("VaultRoot(model).ToPhysicalPath([Crew Internet]) = %v, want [keepass Crew Internet]", got) + } + if got := view.FromPhysicalPath([]string{"keepass", "Crew", "Internet"}); !slices.Equal(got, []string{"Crew", "Internet"}) { + t.Fatalf("VaultRoot(model).FromPhysicalPath([keepass Crew Internet]) = %v, want [Crew Internet]", got) + } +} + +func TestVaultRecycleBinProjectsRecycleTree(t *testing.T) { + t.Parallel() + + model := vault.Model{ + RecycleBin: []vault.Entry{ + {ID: "bellagio-ledger", Title: "Bellagio Ledger", Path: []string{"Crew", "Internet"}}, + {ID: "fountain-cameras", Title: "Fountain Cameras", Path: []string{"Crew", "Security"}}, + }, + } + + view := VaultRecycleBin(model) + + if got := view.ChildGroups(nil); !slices.Equal(got, []string{"Crew"}) { + t.Fatalf("VaultRecycleBin(model).ChildGroups(nil) = %v, want [Crew]", got) + } + if got := view.ChildGroups([]string{"Crew"}); !slices.Equal(got, []string{"Internet", "Security"}) { + t.Fatalf("VaultRecycleBin(model).ChildGroups([Crew]) = %v, want [Internet Security]", got) + } + + gotEntries := view.EntriesInPath([]string{"Crew", "Internet"}) + if len(gotEntries) != 1 || !slices.Equal(gotEntries[0].Path, []string{"Crew", "Internet"}) { + t.Fatalf("VaultRecycleBin(model).EntriesInPath([Crew Internet]) = %#v, want logical recycle-bin path [Crew Internet]", gotEntries) + } + + if got := view.ToPhysicalPath([]string{"Crew", "Internet"}); !slices.Equal(got, []string{"Crew", "Internet"}) { + t.Fatalf("VaultRecycleBin(model).ToPhysicalPath([Crew Internet]) = %v, want [Crew Internet]", got) + } +} + +func TestVaultReturnsPhysicalPathsUnchanged(t *testing.T) { + t.Parallel() + + model := vault.Model{ + Entries: []vault.Entry{ + {ID: "bellagio-ledger", Title: "Bellagio Ledger", Path: []string{"keepass", "Crew", "Internet"}}, + }, + } + + view := Vault(model) + + if got := view.ChildGroups(nil); !slices.Equal(got, []string{"keepass"}) { + t.Fatalf("Vault(model).ChildGroups(nil) = %v, want [keepass]", got) + } + if got := view.ToPhysicalPath([]string{"keepass", "Crew"}); !slices.Equal(got, []string{"keepass", "Crew"}) { + t.Fatalf("Vault(model).ToPhysicalPath([keepass Crew]) = %v, want [keepass Crew]", got) + } + if got := view.FromPhysicalEntry(model.Entries[0]); !slices.Equal(got.Path, []string{"keepass", "Crew", "Internet"}) { + t.Fatalf("Vault(model).FromPhysicalEntry(entry).Path = %v, want [keepass Crew Internet]", got.Path) + } +}