package store_test import ( "context" "testing" "time" "github.com/wotra/wotra/internal/domain" "github.com/wotra/wotra/internal/store" ) func TestBalanceAdjustmentStoreCRUD(t *testing.T) { db, err := store.Open(":memory:") if err != nil { t.Fatal(err) } defer db.Close() s := store.NewBalanceAdjustmentStore(db) ctx := context.Background() now := time.Now().UnixMilli() // SumDelta on empty table. total, count, err := s.SumDelta(ctx) if err != nil { t.Fatalf("SumDelta empty: %v", err) } if total != 0 || count != 0 { t.Fatalf("empty: want (0,0), got (%d,%d)", total, count) } // List on empty table returns nil slice, no error. list, err := s.List(ctx) if err != nil { t.Fatalf("List empty: %v", err) } if len(list) != 0 { t.Errorf("List empty: want 0, got %d", len(list)) } // Create two adjustments. a1 := &domain.BalanceAdjustment{ ID: "id-1", DeltaMs: 3_600_000, Note: "carry-over", EffectiveAt: now - 86400000, CreatedAt: now, UpdatedAt: now, } a2 := &domain.BalanceAdjustment{ ID: "id-2", DeltaMs: -1_800_000, Note: "", EffectiveAt: now, CreatedAt: now, UpdatedAt: now, } if err := s.Create(ctx, a1); err != nil { t.Fatalf("Create a1: %v", err) } if err := s.Create(ctx, a2); err != nil { t.Fatalf("Create a2: %v", err) } // GetByID round-trip. got, err := s.GetByID(ctx, "id-1") if err != nil { t.Fatalf("GetByID: %v", err) } if got.DeltaMs != a1.DeltaMs || got.Note != a1.Note { t.Errorf("GetByID: want {%d,%q}, got {%d,%q}", a1.DeltaMs, a1.Note, got.DeltaMs, got.Note) } // GetByID missing. _, err = s.GetByID(ctx, "no-such") if err != store.ErrAdjustmentNotFound { t.Errorf("GetByID missing: want ErrAdjustmentNotFound, got %v", err) } // SumDelta with two rows. total, count, err = s.SumDelta(ctx) if err != nil { t.Fatalf("SumDelta: %v", err) } if count != 2 { t.Errorf("count: want 2, got %d", count) } want := int64(3_600_000 - 1_800_000) if total != want { t.Errorf("total: want %d, got %d", want, total) } // List returns newest effective_at first (a2 has now, a1 has now-1day). list, err = s.List(ctx) if err != nil { t.Fatalf("List: %v", err) } if len(list) != 2 { t.Fatalf("List len: want 2, got %d", len(list)) } if list[0].ID != "id-2" { t.Errorf("List[0]: want id-2, got %s", list[0].ID) } // Update a1. a1.DeltaMs = 7_200_000 a1.Note = "updated" a1.UpdatedAt = now + 1000 if err := s.Update(ctx, a1); err != nil { t.Fatalf("Update: %v", err) } got, _ = s.GetByID(ctx, "id-1") if got.DeltaMs != 7_200_000 || got.Note != "updated" { t.Errorf("after Update: want {7200000,updated}, got {%d,%q}", got.DeltaMs, got.Note) } // Update missing. missing := &domain.BalanceAdjustment{ID: "no-such", DeltaMs: 1, UpdatedAt: now} if err := s.Update(ctx, missing); err != store.ErrAdjustmentNotFound { t.Errorf("Update missing: want ErrAdjustmentNotFound, got %v", err) } // Delete a2. if err := s.Delete(ctx, "id-2"); err != nil { t.Fatalf("Delete: %v", err) } total, count, _ = s.SumDelta(ctx) if count != 1 || total != 7_200_000 { t.Errorf("after Delete: want (7200000,1), got (%d,%d)", total, count) } // Delete missing. if err := s.Delete(ctx, "no-such"); err != store.ErrAdjustmentNotFound { t.Errorf("Delete missing: want ErrAdjustmentNotFound, got %v", err) } }