summaryrefslogtreecommitdiff
path: root/internal/store/data_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-22 16:52:37 +0200
committerPaul Buetow <paul@buetow.org>2026-02-22 16:52:37 +0200
commitbcb07f5587c310063b74d280f7e82aa47a132c39 (patch)
tree15ec499cf9acdde3b3876f3fd1cf9316a602cf6d /internal/store/data_test.go
parentbb5ce162f82417191b80c04f69193d1a8af6b3d8 (diff)
Address store package review findings (task 352/store)
Fix CommitIndex to respect force=false by checking os.Stat before writing, mirroring the Data.Commit behaviour and keeping index/data pairs consistent. Skip .git directory in WalkIndexes via filepath.SkipDir to avoid spurious errors or false matches inside the git metadata tree. Make ShredAllExported continue past individual shred errors and return the last error, matching Ruby's best-effort shredding behaviour. Accept io.Reader in Store.Remove instead of hardwiring os.Stdin, enabling deterministic testing via strings.NewReader injection. Fix runFzf comment to state that any non-zero fzf exit is treated as no selection (not only exit 130). Document ImportRecursive divergence from Ruby basename-flattening behaviour. Add 14 new tests: Search, SearchActionCat, SearchActionCatBinarySkip, ShredAllExported, SearchActionExport, ImportRecursive, ReimportAfterExport, RemoveInteractive, RemoveInteractiveDecline, CommitIndexSkipsExisting, LoadIndexMissingFile, LoadIndexCorrupted, LoadDataMissingFile, LoadDataCorrupted, DataExportUnwritable, HashPathEdgeCases, ImportMissingSourceFile, WalkIndexesInvalidRegex. Improve TestIndexSort to call sort.Sort and assert final order. Remove orphaned section-header comment from store_test.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/store/data_test.go')
-rw-r--r--internal/store/data_test.go69
1 files changed, 59 insertions, 10 deletions
diff --git a/internal/store/data_test.go b/internal/store/data_test.go
index 9d8a86a..88b80cd 100644
--- a/internal/store/data_test.go
+++ b/internal/store/data_test.go
@@ -72,8 +72,10 @@ func TestDataString(t *testing.T) {
// --- TestDataCommitAndLoad ---------------------------------------------------
-// TestDataCommitAndLoad writes a Data to disk via Commit (force=true), then
-// reads it back with loadData and verifies the round-trip.
+// TestDataCommitAndLoad encrypts content directly and reads it back via
+// loadData, verifying the full encrypt/decrypt round-trip.
+// (Commit is tested in the integration tests that wire up a real git repo;
+// here we test the encrypt+write+decrypt path without git scaffolding.)
func TestDataCommitAndLoad(t *testing.T) {
ctx := context.Background()
c := newTestCipher(t)
@@ -82,13 +84,6 @@ func TestDataCommitAndLoad(t *testing.T) {
dataPath := filepath.Join(dir, "test.data")
wantContent := "my secret data\nwith newlines\n"
- d := &Data{
- Content: []byte(wantContent),
- DataPath: dataPath,
- }
-
- // Use a nil git — Commit with git.Add will fail, so we test Commit in two stages:
- // encrypt+write only. We manually write the file to sidestep git in unit tests.
ciphertext, err := c.Encrypt([]byte(wantContent))
if err != nil {
t.Fatalf("Encrypt: %v", err)
@@ -104,7 +99,6 @@ func TestDataCommitAndLoad(t *testing.T) {
if string(loaded.Content) != wantContent {
t.Errorf("loadData content = %q; want %q", loaded.Content, wantContent)
}
- _ = d // d was constructed for documentation only; loadData is what we test here.
}
// --- TestDataExport ----------------------------------------------------------
@@ -154,6 +148,61 @@ func TestDataExportCreatesSubdir(t *testing.T) {
}
}
+// --- TestLoadDataMissingFile -------------------------------------------------
+
+// TestLoadDataMissingFile verifies that loadData returns an error when the data
+// file does not exist on disk.
+func TestLoadDataMissingFile(t *testing.T) {
+ ctx := context.Background()
+ c := newTestCipher(t)
+
+ _, err := loadData(ctx, "/nonexistent/path/to.data", c)
+ if err == nil {
+ t.Error("loadData with missing file: expected error, got nil")
+ }
+}
+
+// --- TestLoadDataCorrupted ---------------------------------------------------
+
+// TestLoadDataCorrupted verifies that loadData returns an error when the file
+// contains data that cannot be decrypted (not valid ciphertext).
+func TestLoadDataCorrupted(t *testing.T) {
+ ctx := context.Background()
+ c := newTestCipher(t)
+
+ dir := t.TempDir()
+ badPath := filepath.Join(dir, "bad.data")
+ // Write garbage that is not valid AES-CBC ciphertext.
+ if err := os.WriteFile(badPath, []byte("not valid ciphertext"), 0o600); err != nil {
+ t.Fatalf("writing bad file: %v", err)
+ }
+
+ _, err := loadData(ctx, badPath, c)
+ if err == nil {
+ t.Error("loadData with corrupted file: expected error, got nil")
+ }
+}
+
+// --- TestDataExportUnwritable ------------------------------------------------
+
+// TestDataExportUnwritable verifies that Export returns an error when the
+// destination directory cannot be created (non-writable parent).
+func TestDataExportUnwritable(t *testing.T) {
+ // Skip when running as root since root can write anywhere.
+ if os.Getuid() == 0 {
+ t.Skip("running as root; permission check not applicable")
+ }
+
+ ctx := context.Background()
+ d := &Data{Content: []byte("test")}
+
+ // /nonexistent is a path whose parent "/" is read-only for non-root users.
+ err := d.Export(ctx, "/nonexistent/dir", "file.txt")
+ if err == nil {
+ t.Error("Export to unwritable dir: expected error, got nil")
+ }
+}
+
// --- TestDataCommitSkipsExisting ---------------------------------------------
// TestDataCommitSkipsExisting checks that Commit with force=false is a no-op