package manifest_test import ( "os" "path/filepath" "testing" "gitea.k3s.k0.nu/tools/photocli/internal/manifest" ) func TestLoadJSONLEmptyDir(t *testing.T) { m := manifest.LoadJSONL(t.TempDir()) if m == nil { t.Fatal("expected non-nil manifest") } } func TestLoadJSONLNonexistentDir(t *testing.T) { m := manifest.LoadJSONL(filepath.Join(t.TempDir(), "no-such-dir")) if m == nil { t.Fatal("expected non-nil manifest even for nonexistent dir") } } func TestLoadJSONLExistingFile(t *testing.T) { dir := t.TempDir() path := manifest.JSONLPath(dir) if err := os.WriteFile(path, []byte(`{"id":"abc","filename":"a.jpg","size":100,"cloud":"gcs","exported":1234}`+"\n"), 0644); err != nil { t.Fatal(err) } m := manifest.LoadJSONL(dir) if !m.Has("abc") { t.Fatal("expected to find entry with id abc") } } func TestJSONLAddAndHas(t *testing.T) { m := manifest.LoadJSONL(t.TempDir()) if m.Has("x") { t.Fatal("expected Has to return false for unknown id") } m.Add("x", "photo.jpg", 42, "s3") if !m.Has("x") { t.Fatal("expected Has to return true after Add") } if m.Has("y") { t.Fatal("expected Has to return false for y") } } func TestJSONLSaveAndReload(t *testing.T) { dir := t.TempDir() m := manifest.LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("id1", "file1.jpg", 10, "aws") m.Add("id2", "file2.jpg", 20, "gcs") if err := m.Save(); err != nil { t.Fatal(err) } m.Close() m2 := manifest.LoadJSONL(dir) if !m2.Has("id1") { t.Fatal("expected id1 after reload") } if !m2.Has("id2") { t.Fatal("expected id2 after reload") } } func TestJSONLOpenAppendCreatesDirs(t *testing.T) { dir := t.TempDir() subdir := filepath.Join(dir, "a", "b") m := manifest.LoadJSONL(subdir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Close() if _, err := os.Stat(filepath.Dir(manifest.JSONLPath(subdir))); err != nil { t.Fatalf("expected dirs to be created: %v", err) } } func TestJSONLCloseIdempotent(t *testing.T) { m := manifest.LoadJSONL(t.TempDir()) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Close() m.Close() } func TestJSONLOpenAppendIdempotent(t *testing.T) { m := manifest.LoadJSONL(t.TempDir()) if err := m.OpenAppend(); err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatalf("expected second OpenAppend to succeed: %v", err) } m.Close() } func TestJSONLEntries(t *testing.T) { m := manifest.LoadJSONL(t.TempDir()) m.Add("e1", "f1.jpg", 1, "c1") m.Add("e2", "f2.jpg", 2, "c2") entries := m.Entries() if len(entries) != 2 { t.Fatalf("expected 2 entries, got %d", len(entries)) } if _, ok := entries["e1"]; !ok { t.Fatal("missing e1") } if _, ok := entries["e2"]; !ok { t.Fatal("missing e2") } } func TestLoadSQLiteEmptyDir(t *testing.T) { m, err := manifest.LoadSQLite(t.TempDir()) if err != nil { t.Fatal(err) } if m == nil { t.Fatal("expected non-nil manifest") } m.Close() } func TestLoadSQLiteNonexistentDir(t *testing.T) { m, err := manifest.LoadSQLite(filepath.Join(t.TempDir(), "no-such-dir")) if err != nil { t.Fatal(err) } if m == nil { t.Fatal("expected non-nil manifest even for nonexistent dir") } m.Close() } func TestSQLiteAddHasSaveClose(t *testing.T) { m, err := manifest.LoadSQLite(t.TempDir()) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("sid1", "sfile.jpg", 99, "azure") if !m.Has("sid1") { t.Fatal("expected Has to return true after Add") } if m.Has("nope") { t.Fatal("expected Has to return false for unknown id") } if err := m.Save(); err != nil { t.Fatal(err) } m.Close() } func TestSQLiteRoundTrip(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("rid1", "rfile1.jpg", 10, "aws") m.Add("rid2", "rfile2.jpg", 20, "gcs") if err := m.Save(); err != nil { t.Fatal(err) } m.Close() m2, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m2.OpenAppend(); err != nil { t.Fatal(err) } defer m2.Close() if !m2.Has("rid1") { t.Fatal("expected rid1 after reload") } if !m2.Has("rid2") { t.Fatal("expected rid2 after reload") } } func TestSQLiteEntries(t *testing.T) { m, err := manifest.LoadSQLite(t.TempDir()) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } defer m.Close() m.Add("se1", "sf1.jpg", 1, "sc1") m.Add("se2", "sf2.jpg", 2, "sc2") entries := m.Entries() if len(entries) != 2 { t.Fatalf("expected 2 entries, got %d", len(entries)) } if _, ok := entries["se1"]; !ok { t.Fatal("missing se1") } if _, ok := entries["se2"]; !ok { t.Fatal("missing se2") } } func TestSQLiteCloseIdempotent(t *testing.T) { m, err := manifest.LoadSQLite(t.TempDir()) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Close() m.Close() } func TestSQLiteOpenAppendIdempotent(t *testing.T) { m, err := manifest.LoadSQLite(t.TempDir()) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatalf("expected second OpenAppend to succeed: %v", err) } m.Close() } func TestOpenJSONLNoFileExists(t *testing.T) { m, err := manifest.Open(t.TempDir(), manifest.FormatJSONL) if err != nil { t.Fatal(err) } m.Close() } func TestOpenSQLiteNoFileExists(t *testing.T) { m, err := manifest.Open(t.TempDir(), manifest.FormatSQLite) if err != nil { t.Fatal(err) } m.Close() } func TestOpenJSONLConvertsFromSQLite(t *testing.T) { dir := t.TempDir() sm, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := sm.OpenAppend(); err != nil { t.Fatal(err) } sm.Add("conv1", "conv1.jpg", 5, "aws") sm.Close() m, err := manifest.Open(dir, manifest.FormatJSONL) if err != nil { t.Fatal(err) } defer m.Close() if !m.Has("conv1") { t.Fatal("expected converted entry conv1 to exist") } if _, err := os.Stat(manifest.SQLitePath(dir)); err == nil { t.Fatal("expected sqlite file to be removed after conversion") } } func TestOpenSQLiteConvertsFromJSONL(t *testing.T) { dir := t.TempDir() jm := manifest.LoadJSONL(dir) if err := jm.OpenAppend(); err != nil { t.Fatal(err) } jm.Add("jconv1", "jconv1.jpg", 7, "gcs") jm.Save() jm.Close() m, err := manifest.Open(dir, manifest.FormatSQLite) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } defer m.Close() if !m.Has("jconv1") { t.Fatal("expected converted entry jconv1 to exist") } if _, err := os.Stat(manifest.JSONLPath(dir)); err == nil { t.Fatal("expected jsonl file to be removed after conversion") } } func TestOpenBothFormatsPrefersRequested(t *testing.T) { dir := t.TempDir() jm := manifest.LoadJSONL(dir) if err := jm.OpenAppend(); err != nil { t.Fatal(err) } jm.Add("both1", "both1.jpg", 1, "aws") jm.Save() jm.Close() sm, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := sm.OpenAppend(); err != nil { t.Fatal(err) } sm.Add("both2", "both2.jpg", 2, "gcs") sm.Close() m, err := manifest.Open(dir, manifest.FormatJSONL) if err != nil { t.Fatal(err) } m.Close() if _, err := os.Stat(manifest.JSONLPath(dir)); err != nil { t.Fatal("expected jsonl file to exist after Open with FormatJSONL") } jm2 := manifest.LoadJSONL(dir) if err := jm2.OpenAppend(); err != nil { t.Fatal(err) } jm2.Add("both1", "both1.jpg", 1, "aws") jm2.Save() jm2.Close() sm2, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := sm2.OpenAppend(); err != nil { t.Fatal(err) } sm2.Add("both2", "both2.jpg", 2, "gcs") sm2.Close() m2, err := manifest.Open(dir, manifest.FormatSQLite) if err != nil { t.Fatal(err) } m2.Close() if _, err := os.Stat(manifest.SQLitePath(dir)); err != nil { t.Fatal("expected sqlite file to exist after Open with FormatSQLite") } } func TestParseFormatValid(t *testing.T) { tests := []struct { input string want manifest.Format }{ {"jsonl", manifest.FormatJSONL}, {"JSONL", manifest.FormatJSONL}, {"json", manifest.FormatJSONL}, {"JSON", manifest.FormatJSONL}, {"sqlite", manifest.FormatSQLite}, {"SQLITE", manifest.FormatSQLite}, {"db", manifest.FormatSQLite}, {"DB", manifest.FormatSQLite}, {"sqlite3", manifest.FormatSQLite}, {"SQLite3", manifest.FormatSQLite}, } for _, tt := range tests { got, err := manifest.ParseFormat(tt.input) if err != nil { t.Errorf("ParseFormat(%q): unexpected error: %v", tt.input, err) continue } if got != tt.want { t.Errorf("ParseFormat(%q) = %q, want %q", tt.input, got, tt.want) } } } func TestParseFormatInvalid(t *testing.T) { _, err := manifest.ParseFormat("csv") if err == nil { t.Fatal("expected error for invalid format") } } func TestOpenAppendReadOnlyPath(t *testing.T) { dir := t.TempDir() roDir := filepath.Join(dir, "readonly") if err := os.MkdirAll(roDir, 0755); err != nil { t.Fatal(err) } path := manifest.JSONLPath(roDir) if err := os.WriteFile(path, []byte("{}\n"), 0444); err != nil { t.Fatal(err) } m := manifest.LoadJSONL(roDir) err := m.OpenAppend() if err == nil { t.Fatal("expected error opening read-only path") } m.Close() } func TestJSONLEntriesEmpty(t *testing.T) { m := manifest.LoadJSONL(t.TempDir()) entries := m.Entries() if len(entries) != 0 { t.Fatalf("expected 0 entries, got %d", len(entries)) } } func TestSQLiteEntriesEmpty(t *testing.T) { m, err := manifest.LoadSQLite(t.TempDir()) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } defer m.Close() entries := m.Entries() if len(entries) != 0 { t.Fatalf("expected 0 entries, got %d", len(entries)) } } func TestOpenJSONLWithExistingJSONL(t *testing.T) { dir := t.TempDir() jm := manifest.LoadJSONL(dir) if err := jm.OpenAppend(); err != nil { t.Fatal(err) } jm.Add("exist1", "exist1.jpg", 10, "aws") jm.Save() jm.Close() m, err := manifest.Open(dir, manifest.FormatJSONL) if err != nil { t.Fatal(err) } defer m.Close() if !m.Has("exist1") { t.Fatal("expected exist1 from existing jsonl") } } func TestOpenSQLiteWithExistingSQLite(t *testing.T) { dir := t.TempDir() sm, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := sm.OpenAppend(); err != nil { t.Fatal(err) } sm.Add("sexist1", "sexist1.jpg", 15, "azure") sm.Close() m, err := manifest.Open(dir, manifest.FormatSQLite) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } defer m.Close() if !m.Has("sexist1") { t.Fatal("expected sexist1 from existing sqlite") } } func TestLoadJSONLWithEmptyLines(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "downloads.jsonl") os.WriteFile(path, []byte("\n\n{\"id\":\"x1\",\"filename\":\"a.jpg\",\"size\":1024,\"cloud\":\"local\",\"exported\":100}\n\n"), 0644) m := manifest.LoadJSONL(dir) if !m.Has("x1") { t.Error("should skip empty lines and parse valid JSON") } } func TestLoadJSONLMalformedJSON(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "downloads.jsonl") os.WriteFile(path, []byte("not-json\n{\"id\":\"x1\",\"filename\":\"a.jpg\",\"size\":1024,\"cloud\":\"local\",\"exported\":100}\n"), 0644) m := manifest.LoadJSONL(dir) if !m.Has("x1") { t.Error("should skip malformed lines and parse valid ones") } } func TestLoadJSONLEmptyID(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "downloads.jsonl") os.WriteFile(path, []byte("{\"id\":\"\",\"filename\":\"a.jpg\",\"size\":1024,\"cloud\":\"local\",\"exported\":100}\n"), 0644) m := manifest.LoadJSONL(dir) if m.Has("") { t.Error("should skip entries with empty ID") } } func TestJSONLAddBeforeOpenAppend(t *testing.T) { dir := t.TempDir() m := manifest.LoadJSONL(dir) m.Add("x1", "photo.jpg", 1024, "local") if !m.Has("x1") { t.Error("Add before OpenAppend should store in memory") } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Close() data, err := os.ReadFile(filepath.Join(dir, "downloads.jsonl")) if err != nil { t.Fatal(err) } if len(data) > 0 { t.Error("entries added before OpenAppend should not be in file") } } func TestSQLiteHasBeforeOpenAppend(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if m.Has("x1") { t.Error("Has on unopened sqlite should return false") } } func TestSQLiteAddBeforeOpenAppend(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Close() } func TestSQLiteEntriesBeforeOpenAppend(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } entries := m.Entries() if entries != nil { t.Error("Entries on unopened sqlite should return nil") } } func TestSQLiteOpenAppendReadOnlyPath(t *testing.T) { m, err := manifest.LoadSQLite("/proc/cannot-create-dir-here") if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err == nil { t.Error("expected error from OpenAppend on read-only path") } } func TestFileExistsOnDirectory(t *testing.T) { dir := t.TempDir() if manifest.FileExists(dir) { t.Error("manifest.FileExists should return false for directories") } } func TestConvertFromJSONLOpenAppendError(t *testing.T) { dir := t.TempDir() m := manifest.LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() os.Chmod(filepath.Join(dir, "downloads.jsonl"), 0444) defer os.Chmod(filepath.Join(dir, "downloads.jsonl"), 0644) _, err := manifest.ConvertFromJSONL(dir) if err == nil { t.Error("expected error when JSONL cannot be opened for reading during conversion") } } func TestConvertFromSQLiteReadError(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() result, err := manifest.ConvertFromSQLite(dir) if err != nil { t.Fatalf("unexpected error: %v", err) } result.Close() if _, err := os.Stat(manifest.JSONLPath(dir)); err != nil { t.Errorf("JSONL file should exist after conversion: %v", err) } if os.Remove(manifest.SQLitePath(dir)) != nil { t.Logf("sqlite file removed as expected") } } func TestJSONLSaveWithoutFile(t *testing.T) { dir := t.TempDir() m := manifest.LoadJSONL(dir) if err := m.Save(); err != nil { t.Errorf("Save without file should return nil, got %v", err) } } func TestJSONLOpenAppendTwice(t *testing.T) { dir := t.TempDir() m := manifest.LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Error("second OpenAppend should return nil, got", err) } m.Close() } func TestSQLiteHasWithoutOpen(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if m.Has("anything") { t.Error("Has on unopened SQLite should return false") } } func TestSQLiteAddWithoutOpen(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") if m.Has("x1") { t.Error("Add without OpenAppend should not persist") } } func TestSQLiteEntriesWithoutOpen(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } entries := m.Entries() if entries != nil { t.Errorf("Entries without open should return nil, got %v", entries) } } func TestSQLiteCloseWithoutOpen(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } m.Close() } func TestSQLiteOpenAppendCreateDir(t *testing.T) { dir := t.TempDir() subdir := filepath.Join(dir, "subdir") m, err := manifest.LoadSQLite(subdir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Errorf("OpenAppend should create dir, got %v", err) } m.Close() } func TestSQLiteSave(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") if err := m.Save(); err != nil { t.Errorf("SQLite Save should return nil, got %v", err) } m.Close() } func TestOpenDefaultSQLite(t *testing.T) { dir := t.TempDir() m, err := manifest.Open(dir, manifest.FormatSQLite) if err != nil { t.Fatal(err) } m.Close() } func TestOpenDefaultJSONLNoFile(t *testing.T) { dir := t.TempDir() m, err := manifest.Open(dir, manifest.FormatJSONL) if err != nil { t.Fatal(err) } m.Close() } func TestParseFormat(t *testing.T) { tests := []struct { input string want manifest.Format err bool }{ {"jsonl", manifest.FormatJSONL, false}, {"json", manifest.FormatJSONL, false}, {"sqlite", manifest.FormatSQLite, false}, {"db", manifest.FormatSQLite, false}, {"sqlite3", manifest.FormatSQLite, false}, {"invalid", "", true}, } for _, tt := range tests { got, err := manifest.ParseFormat(tt.input) if tt.err && err == nil { t.Errorf("ParseFormat(%q) expected error", tt.input) } if !tt.err && got != tt.want { t.Errorf("ParseFormat(%q) = %v, want %v", tt.input, got, tt.want) } } } func TestJSONLSaveWithFileSync(t *testing.T) { dir := t.TempDir() m := manifest.LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") if err := m.Save(); err != nil { t.Errorf("Save with open file should succeed, got %v", err) } m.Close() } func TestConvertFromJSONLSuccess(t *testing.T) { dir := t.TempDir() m := manifest.LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("c1", "photo.jpg", 1024, "cloud") m.Close() result, err := manifest.ConvertFromJSONL(dir) if err != nil { t.Fatalf("unexpected error: %v", err) } if !result.Has("c1") { t.Error("converted SQLite manifest should have c1") } result.Close() if manifest.FileExists(manifest.JSONLPath(dir)) { t.Error("JSONL file should be deleted after conversion") } } func TestConvertFromJSONLSQLiteOpenError(t *testing.T) { dir := t.TempDir() m := manifest.LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() subdir := filepath.Join(dir, "readonly") if err := os.MkdirAll(subdir, 0755); err != nil { t.Fatal(err) } srcPath := manifest.JSONLPath(dir) dstPath := filepath.Join(subdir, "downloads.jsonl") data, err := os.ReadFile(srcPath) if err != nil { t.Fatal(err) } if err := os.WriteFile(dstPath, data, 0644); err != nil { t.Fatal(err) } } func TestJSONLOpenAppendMkdirError(t *testing.T) { dir := t.TempDir() jsonlFile := filepath.Join(dir, "downloads.jsonl") if err := os.WriteFile(jsonlFile, []byte("{}\n"), 0644); err != nil { t.Fatal(err) } m := manifest.LoadJSONL(dir) m.Close() } func TestSQLiteOpenAppendMkdirError(t *testing.T) { m, err := manifest.LoadSQLite("/proc/cannot-write") if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err == nil { t.Error("expected error creating dir under /proc") m.Close() } } func TestSQLiteHasAfterClose(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() if m.Has("x1") { t.Error("Has should return false after Close") } } func TestSQLiteEntriesAfterClose(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() entries := m.Entries() if entries != nil { t.Errorf("Entries after Close should return nil, got %d entries", len(entries)) } } func TestConvertFromJSONLNoFile(t *testing.T) { dir := t.TempDir() result, err := manifest.ConvertFromJSONL(dir) if err != nil { t.Fatalf("ConvertFromJSONL with no existing file should work: %v", err) } result.Close() } func TestConvertFromSQLiteNoFile(t *testing.T) { dir := t.TempDir() result, err := manifest.ConvertFromSQLite(dir) if err != nil { t.Fatalf("ConvertFromSQLite with no existing file should work: %v", err) } result.Close() if _, err := os.Stat(manifest.JSONLPath(dir)); err != nil { t.Errorf("JSONL file should exist after conversion: %v", err) } } func TestConvertFromJSONLCloseSrcError(t *testing.T) { } func TestConvertFromSQLiteSaveError(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() jsonlPath := manifest.JSONLPath(dir) os.WriteFile(jsonlPath, []byte(""), 0444) defer os.Chmod(jsonlPath, 0644) _, err = manifest.ConvertFromSQLite(dir) if err == nil { t.Error("expected error from dst.Save during ConvertFromSQLite") } } func TestOpenExistingJSONL(t *testing.T) { dir := t.TempDir() m := manifest.LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Save() m.Close() m2, err := manifest.Open(dir, manifest.FormatJSONL) if err != nil { t.Fatal(err) } if !m2.Has("x1") { t.Error("should have x1 after reopening") } m2.Close() } func TestOpenExistingSQLite(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() m2, err := manifest.Open(dir, manifest.FormatSQLite) if err != nil { t.Fatal(err) } if err := m2.OpenAppend(); err != nil { t.Fatal(err) } if !m2.Has("x1") { t.Error("should have x1 after reopening") } m2.Close() } func TestOpenConvertJSONLToSQLite(t *testing.T) { dir := t.TempDir() m := manifest.LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() m2, err := manifest.Open(dir, manifest.FormatSQLite) if err != nil { t.Fatal(err) } if !m2.Has("x1") { t.Error("should have x1 after conversion") } m2.Close() if manifest.FileExists(manifest.JSONLPath(dir)) { t.Error("JSONL file should be deleted after conversion") } } func TestOpenConvertSQLiteToJSONL(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() m2, err := manifest.Open(dir, manifest.FormatJSONL) if err != nil { t.Fatal(err) } if !m2.Has("x1") { t.Error("should have x1 after conversion") } m2.Close() if manifest.FileExists(manifest.SQLitePath(dir)) { t.Error("SQLite file should be deleted after conversion") } } func TestSQLiteOpenAppendInvalidPath(t *testing.T) { m, err := manifest.LoadSQLite("/dev/null/impossible/path") if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err == nil { t.Error("expected error from OpenAppend with invalid path") m.Close() } } func TestConvertFromSQLiteJSONLOpenAppendError(t *testing.T) { dir := t.TempDir() m, err := manifest.LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() jsonlPath := manifest.JSONLPath(dir) os.WriteFile(jsonlPath, []byte("{}\n"), 0444) defer os.Chmod(jsonlPath, 0644) _, err = manifest.ConvertFromSQLite(dir) if err == nil { t.Error("expected error from dst.OpenAppend during ConvertFromSQLite") } } func TestJSONLOpenAppendMkdirAllError(t *testing.T) { m := manifest.LoadJSONL("/dev/null/impossible/path/downloads.jsonl") if err := m.OpenAppend(); err == nil { t.Error("expected error from OpenAppend with impossible path") m.Close() } }