package store import ( "context" "testing" "time" ) func TestHostRepoStatsHistory_PartialUpsertPreservesPriorValues(t *testing.T) { t.Parallel() s := openTestStore(t) ctx := context.Background() const hostID = "h-history-1" seedHost(t, s, hostID) day := "2026-05-07" now := time.Now().UTC() // 1. First write of the day: total_size only. if err := s.UpsertHostRepoStatsHistory(ctx, hostID, day, HostRepoStats{TotalSizeBytes: int64ptr(100)}, now); err != nil { t.Fatalf("upsert 1: %v", err) } // 2. Second write: snapshot_count only — total_size MUST survive. if err := s.UpsertHostRepoStatsHistory(ctx, hostID, day, HostRepoStats{SnapshotCount: int64ptr(7)}, now.Add(time.Minute)); err != nil { t.Fatalf("upsert 2: %v", err) } // 3. Third write: a prune-only patch (no size, no count). Both // prior values must survive. if err := s.UpsertHostRepoStatsHistory(ctx, hostID, day, HostRepoStats{}, now.Add(2*time.Minute)); err != nil { t.Fatalf("upsert 3: %v", err) } pts, err := s.ListHostRepoStatsHistory(ctx, hostID, time.Time{}) if err != nil { t.Fatalf("list: %v", err) } if len(pts) != 1 { t.Fatalf("want 1 point, got %d", len(pts)) } p := pts[0] if p.TotalSizeBytes == nil || *p.TotalSizeBytes != 100 { t.Errorf("TotalSizeBytes: want 100, got %v", p.TotalSizeBytes) } if p.SnapshotCount == nil || *p.SnapshotCount != 7 { t.Errorf("SnapshotCount: want 7, got %v", p.SnapshotCount) } } func TestHostRepoStatsHistory_OrderingAndSinceFilter(t *testing.T) { t.Parallel() s := openTestStore(t) ctx := context.Background() const hostID = "h-history-2" seedHost(t, s, hostID) now := time.Now().UTC() for i, day := range []string{"2026-05-01", "2026-05-02", "2026-05-04", "2026-05-07"} { v := int64(100 + i*10) if err := s.UpsertHostRepoStatsHistory(ctx, hostID, day, HostRepoStats{TotalSizeBytes: &v}, now); err != nil { t.Fatalf("upsert %s: %v", day, err) } } all, err := s.ListHostRepoStatsHistory(ctx, hostID, time.Time{}) if err != nil { t.Fatalf("list all: %v", err) } if len(all) != 4 { t.Fatalf("want 4 points, got %d", len(all)) } wantDays := []string{"2026-05-01", "2026-05-02", "2026-05-04", "2026-05-07"} for i, p := range all { got := p.Day.Format("2006-01-02") if got != wantDays[i] { t.Errorf("point %d: want day %s, got %s", i, wantDays[i], got) } } since, _ := time.Parse("2006-01-02", "2026-05-03") recent, err := s.ListHostRepoStatsHistory(ctx, hostID, since) if err != nil { t.Fatalf("list since: %v", err) } if len(recent) != 2 { t.Fatalf("since 2026-05-03: want 2 points, got %d", len(recent)) } } func TestHostRepoStatsHistory_CascadeOnHostDelete(t *testing.T) { t.Parallel() s := openTestStore(t) ctx := context.Background() const hostID = "h-history-3" seedHost(t, s, hostID) if err := s.UpsertHostRepoStatsHistory(ctx, hostID, "2026-05-07", HostRepoStats{TotalSizeBytes: int64ptr(42)}, time.Now().UTC()); err != nil { t.Fatalf("upsert: %v", err) } if err := s.DeleteHost(ctx, hostID); err != nil { t.Fatalf("delete host: %v", err) } pts, err := s.ListHostRepoStatsHistory(ctx, hostID, time.Time{}) if err != nil { t.Fatalf("list after delete: %v", err) } if len(pts) != 0 { t.Fatalf("want 0 points after host delete, got %d", len(pts)) } }