package manifest import ( "database/sql" "fmt" "os" "strings" "testing" _ "modernc.org/sqlite" ) func TestSetJSONLSaveHook(t *testing.T) { old := SetJSONLSaveHook(func() error { return fmt.Errorf("hook error") }) if old != nil { t.Error("expected nil old hook") } restore := SetJSONLSaveHook(old) if restore == nil { t.Error("expected non-nil restore function") } } func TestJSONLSaveHookError(t *testing.T) { dir := t.TempDir() m := LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") old := SetJSONLSaveHook(func() error { return fmt.Errorf("hook error") }) defer SetJSONLSaveHook(old) if err := m.Save(); err == nil { t.Error("expected hook error from Save") } m.Close() } func TestJSONLSyncFuncError(t *testing.T) { dir := t.TempDir() m := LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.syncFunc = func() error { return fmt.Errorf("sync func error") } if err := m.Save(); err == nil { t.Error("expected syncFunc error from Save") } m.Close() } func TestSQLiteOpenAppendSQLError(t *testing.T) { dir := t.TempDir() m, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } m.open = func(driverName, dataSourceName string) (*sql.DB, error) { return nil, fmt.Errorf("simulated open error") } if err := m.OpenAppend(); err == nil { t.Error("expected error from sql.Open failure") } } func TestSQLiteOpenAppendCreateTableError(t *testing.T) { dir := t.TempDir() m, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } realOpen := m.open m.open = func(driverName, dataSourceName string) (*sql.DB, error) { db, err := realOpen(driverName, dataSourceName) if err != nil { return nil, err } db.Close() return db, nil } if err := m.OpenAppend(); err == nil { t.Error("expected error from closed DB CREATE TABLE") } } func TestSQLiteHasAfterClose(t *testing.T) { dir := t.TempDir() m, err := 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 := 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 should return nil after Close, got %d entries", len(entries)) } } func TestSQLiteOpenAppendMkdirAllError(t *testing.T) { m, err := 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 TestSQLiteOpenAppendNilOpener(t *testing.T) { dir := t.TempDir() m, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } m.open = nil if err := m.OpenAppend(); err != nil { t.Errorf("expected nil opener to use sql.Open fallback, got err: %v", err) } m.Close() } func TestSQLiteOpenAppendCreateIndexError(t *testing.T) { dir := t.TempDir() m, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } realOpen := m.open m.open = func(driverName, dataSourceName string) (*sql.DB, error) { db, err := realOpen(driverName, dataSourceName) if err != nil { return nil, err } db.Close() return db, nil } if err := m.OpenAppend(); err == nil { t.Error("expected error from closed DB") } } func TestSQLiteHasQueryError(t *testing.T) { dir := t.TempDir() m, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") closedDB := m.db closedDB.Close() result := m.Has("x1") if result { t.Error("Has should return false with broken DB connection") } } func TestSQLiteEntriesQueryError(t *testing.T) { dir := t.TempDir() m, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") closedDB := m.db closedDB.Close() entries := m.Entries() if entries == nil { t.Error("Entries should return non-nil map with broken DB connection") } if len(entries) != 0 { t.Errorf("Entries should be empty with broken DB connection, got %d", len(entries)) } } func TestSQLiteHasQueryErrorWithOpenDB(t *testing.T) { dir := t.TempDir() m, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") closedDB := m.db closedDB.Close() if m.Has("x1") { t.Error("Has should return false with closed DB") } } func TestSQLiteEntriesQueryErrorWithOpenDB(t *testing.T) { dir := t.TempDir() m, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") closedDB := m.db closedDB.Close() entries := m.Entries() if entries != nil && len(entries) != 0 { t.Errorf("Entries should be empty with closed DB, got %d", len(entries)) } } func TestSQLiteCreateIndexError(t *testing.T) { dir := t.TempDir() m, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } realOpen := m.open callCount := 0 m.open = func(driverName, dataSourceName string) (*sql.DB, error) { db, err := realOpen(driverName, dataSourceName) if err != nil { return nil, err } m.execFunc = func(query string, args ...any) (sql.Result, error) { callCount++ if callCount == 2 { return nil, fmt.Errorf("injected CREATE INDEX error") } return db.Exec(query, args...) } return db, nil } if err := m.OpenAppend(); err == nil { t.Error("expected error from CREATE INDEX") m.Close() } } func TestConvertFromJSONLOpenAppendSQLError(t *testing.T) { dir := t.TempDir() m := LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() oldOpen := sqliteOpenFunc sqliteOpenFunc = func(driverName, dataSourceName string) (*sql.DB, error) { return nil, fmt.Errorf("simulated sqlite open error") } defer func() { sqliteOpenFunc = oldOpen }() _, err := ConvertFromJSONL(dir) if err == nil { t.Error("expected error from dst.OpenAppend during ConvertFromJSONL") } } func TestConvertFromJSONLDstOpenAppendError(t *testing.T) { dir := t.TempDir() m := LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.Close() realOpen := defaultSQLOpener() oldOpen := sqliteOpenFunc sqliteOpenFunc = func(driverName, dataSourceName string) (*sql.DB, error) { db, err := realOpen(driverName, dataSourceName) if err != nil { return nil, err } db.Close() return db, nil } defer func() { sqliteOpenFunc = oldOpen }() _, err := ConvertFromJSONL(dir) if err == nil { t.Error("expected error from dst.OpenAppend during ConvertFromJSONL") } } func TestConvertFromSQLiteSrcOpenAppendError(t *testing.T) { dir := t.TempDir() src, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := src.OpenAppend(); err != nil { t.Fatal(err) } src.Add("x1", "photo.jpg", 1024, "local") src.Close() realOpen := defaultSQLOpener() callCount := 0 oldOpen := sqliteOpenFunc sqliteOpenFunc = func(driverName, dataSourceName string) (*sql.DB, error) { callCount++ db, err := realOpen(driverName, dataSourceName) if err != nil { return nil, err } if callCount > 0 { db.Close() } return db, nil } defer func() { sqliteOpenFunc = oldOpen }() _, err = ConvertFromSQLite(dir) if err == nil { t.Error("expected error from src.OpenAppend during ConvertFromSQLite") } } func TestConvertFromSQLiteDstOpenAppendError(t *testing.T) { dir := t.TempDir() src, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := src.OpenAppend(); err != nil { t.Fatal(err) } src.Add("x1", "photo.jpg", 1024, "local") src.Close() jsonlPath := JSONLPath(dir) f, err := os.Create(jsonlPath) if err != nil { t.Fatal(err) } f.Close() os.Chmod(jsonlPath, 0444) defer os.Chmod(jsonlPath, 0644) _, err = ConvertFromSQLite(dir) if err == nil { t.Error("expected error from dst.OpenAppend during ConvertFromSQLite") } } func TestJSONLSaveError(t *testing.T) { dir := t.TempDir() m := LoadJSONL(dir) if err := m.OpenAppend(); err != nil { t.Fatal(err) } m.Add("x1", "photo.jpg", 1024, "local") m.file.Close() if err := m.Save(); err == nil { t.Error("expected Sync error on closed file") } m.Close() } func TestConvertFromSQLiteSaveError(t *testing.T) { dir := t.TempDir() src, err := LoadSQLite(dir) if err != nil { t.Fatal(err) } if err := src.OpenAppend(); err != nil { t.Fatal(err) } src.Add("x1", "photo.jpg", 1024, "local") src.Close() oldHook := jsonlSaveHook jsonlSaveHook = func() error { return fmt.Errorf("simulated sync error") } defer func() { jsonlSaveHook = oldHook }() _, err = ConvertFromSQLite(dir) if err == nil { t.Error("expected error from dst.Save during ConvertFromSQLite") } if err != nil && !strings.Contains(err.Error(), "save jsonl") { t.Errorf("expected save jsonl error, got: %v", err) } }