package freshness import ( "context" "testing" "time" "github.com/jackc/pgx/v5" "github.com/stretchr/testify/require" ) type fakeRow struct { scan func(dest ...any) error } func (r fakeRow) Scan(dest ...any) error { return r.scan(dest...) } func TestBuildSnapshotHealthyState(t *testing.T) { now := time.Date(2026, 4, 10, 22, 10, 16, 0, time.UTC) call := 0 queryRow := func(_ context.Context, _ string, _ ...any) pgx.Row { call++ switch call { case 1: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 200 *dest[1].(*time.Time) = now.Add(-2 * time.Second) return nil }} case 2: return fakeRow{scan: func(dest ...any) error { *dest[0].(*string) = "0xabc" *dest[1].(*int64) = 198 *dest[2].(*time.Time) = now.Add(-5 * time.Second) return nil }} case 3: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 198 *dest[1].(*time.Time) = now.Add(-5 * time.Second) return nil }} case 4: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 198 *dest[1].(*time.Time) = now.Add(-5 * time.Second) return nil }} case 5: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 128 *dest[1].(*int64) = 12 *dest[2].(*int64) = 34 return nil }} default: t.Fatalf("unexpected call %d", call) return nil } } probe := func(context.Context) (*Reference, error) { ts := now.Add(-1 * time.Second).Format(time.RFC3339) age := int64(1) block := int64(200) return &Reference{ BlockNumber: &block, Timestamp: &ts, AgeSeconds: &age, Source: SourceReported, Confidence: ConfidenceHigh, Provenance: ProvenanceRPC, Completeness: CompletenessComplete, }, nil } snapshot, completeness, sampling, diagnostics, err := BuildSnapshot(context.Background(), 138, queryRow, probe, now, nil, nil) require.NoError(t, err) require.Equal(t, int64(200), *snapshot.ChainHead.BlockNumber) require.Equal(t, int64(198), *snapshot.LatestIndexedTransaction.BlockNumber) require.Equal(t, int64(2), *snapshot.LatestNonEmptyBlock.DistanceFromHead) require.Equal(t, CompletenessComplete, completeness.TransactionsFeed) require.NotNil(t, sampling.StatsGeneratedAt) require.Equal(t, "active", diagnostics.ActivityState) } func TestBuildSnapshotFreshHeadStaleTransactionVisibility(t *testing.T) { now := time.Date(2026, 4, 11, 0, 10, 16, 0, time.UTC) call := 0 queryRow := func(_ context.Context, _ string, _ ...any) pgx.Row { call++ switch call { case 1: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 3875999 *dest[1].(*time.Time) = now.Add(-3 * time.Second) return nil }} case 2: return fakeRow{scan: func(dest ...any) error { *dest[0].(*string) = "0xstale" *dest[1].(*int64) = 3860660 *dest[2].(*time.Time) = now.Add(-(9*time.Hour + 8*time.Minute)) return nil }} case 3: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 3860660 *dest[1].(*time.Time) = now.Add(-(9*time.Hour + 8*time.Minute)) return nil }} case 4: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 3875998 *dest[1].(*time.Time) = now.Add(-4 * time.Second) return nil }} case 5: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 128 *dest[1].(*int64) = 3 *dest[2].(*int64) = 9 return nil }} default: t.Fatalf("unexpected call %d", call) return nil } } probe := func(context.Context) (*Reference, error) { ts := now.Add(-1 * time.Second).Format(time.RFC3339) age := int64(1) block := int64(3876000) return &Reference{ BlockNumber: &block, Timestamp: &ts, AgeSeconds: &age, Source: SourceReported, Confidence: ConfidenceHigh, Provenance: ProvenanceRPC, Completeness: CompletenessComplete, }, nil } snapshot, completeness, _, diagnostics, err := BuildSnapshot(context.Background(), 138, queryRow, probe, now, nil, nil) require.NoError(t, err) require.Equal(t, int64(15340), *snapshot.LatestNonEmptyBlock.DistanceFromHead) require.Equal(t, CompletenessStale, completeness.TransactionsFeed) require.Equal(t, CompletenessComplete, completeness.BlocksFeed) require.Equal(t, "fresh_head_stale_transaction_visibility", diagnostics.ActivityState) } func TestBuildSnapshotQuietChainButCurrent(t *testing.T) { now := time.Date(2026, 4, 10, 23, 10, 16, 0, time.UTC) call := 0 queryRow := func(_ context.Context, _ string, _ ...any) pgx.Row { call++ switch call { case 1: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 3875000 *dest[1].(*time.Time) = now.Add(-1 * time.Second) return nil }} case 2: return fakeRow{scan: func(dest ...any) error { *dest[0].(*string) = "0xquiet" *dest[1].(*int64) = 3874902 *dest[2].(*time.Time) = now.Add(-512 * time.Second) return nil }} case 3: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 3874902 *dest[1].(*time.Time) = now.Add(-512 * time.Second) return nil }} case 4: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 3874902 *dest[1].(*time.Time) = now.Add(-512 * time.Second) return nil }} case 5: return fakeRow{scan: func(dest ...any) error { *dest[0].(*int64) = 128 *dest[1].(*int64) = 0 *dest[2].(*int64) = 0 return nil }} default: t.Fatalf("unexpected call %d", call) return nil } } probe := func(context.Context) (*Reference, error) { ts := now.Add(-1 * time.Second).Format(time.RFC3339) age := int64(1) block := int64(3875000) return &Reference{ BlockNumber: &block, Timestamp: &ts, AgeSeconds: &age, Source: SourceReported, Confidence: ConfidenceHigh, Provenance: ProvenanceRPC, Completeness: CompletenessComplete, }, nil } snapshot, completeness, _, diagnostics, err := BuildSnapshot(context.Background(), 138, queryRow, probe, now, nil, nil) require.NoError(t, err) require.Equal(t, int64(98), *snapshot.LatestNonEmptyBlock.DistanceFromHead) require.Equal(t, CompletenessComplete, completeness.TransactionsFeed) require.Equal(t, "quiet_chain", diagnostics.ActivityState) } func TestBuildSnapshotUnknownFieldsRemainNullSafe(t *testing.T) { queryRow := func(_ context.Context, _ string, _ ...any) pgx.Row { return fakeRow{scan: func(dest ...any) error { return pgx.ErrNoRows }} } snapshot, completeness, sampling, diagnostics, err := BuildSnapshot(context.Background(), 138, queryRow, nil, time.Now().UTC(), nil, nil) require.NoError(t, err) require.Nil(t, snapshot.ChainHead.BlockNumber) require.Equal(t, CompletenessUnavailable, completeness.TransactionsFeed) require.NotNil(t, sampling.StatsGeneratedAt) require.Equal(t, "limited_observability", diagnostics.ActivityState) }