package storage import ( "fmt" "path/filepath" "sync" "testing" "github.com/sandrodach/mindsift/internal/domain" ) // Many writers at once on one SQLite file (FLAW-23): transactions that read before they write must // wait for the write lock instead of failing with SQLITE_BUSY. func TestConcurrentWritersDoNotFail(t *testing.T) { store, err := NewStore(filepath.Join(t.TempDir(), "load.db")) if err != nil { t.Fatal(err) } defer store.Close() u, _ := store.CreateUser("load@example.com", "hash", "Last", "member") tl, _ := store.CreateTaskList(u.ID, "", "", "Last", "", "") var ids []string for i := 0; i < 20; i++ { th, _ := store.CreateThoughtWithDetails(u.ID, fmt.Sprintf("Aufgabe %d", i), "normal", "manual", "", tl.ID, "todo", "") _, _ = store.UpdateThoughtTriage(u.ID, th.ID, "keep", "task", "", "", tl.ID) ids = append(ids, th.ID) } var wg sync.WaitGroup errs := make(chan error, 400) for w := 0; w < 16; w++ { wg.Add(1) go func(w int) { defer wg.Done() for i := 0; i < 15; i++ { status := "todo" if (w+i)%2 == 0 { status = "done" } items := []domain.ThoughtReorderItem{{ID: ids[(w+i)%len(ids)], SortOrder: i, KanbanStatus: &status}} if err := store.ReorderTasks(tl.ID, items); err != nil { errs <- fmt.Errorf("reorder: %w", err) } if _, err := store.CreateThought(u.ID, fmt.Sprintf("Gedanke %d-%d", w, i), "normal", "manual", ""); err != nil { errs <- fmt.Errorf("create: %w", err) } } }(w) } wg.Wait() close(errs) failed := 0 for err := range errs { if failed < 3 { t.Error(err) } failed++ } if failed > 0 { t.Fatalf("%d of %d writes failed", failed, 16*15*2) } }