package service import ( "context" "fmt" "os" "testing" "time" "github.com/itworx/pulse/internal/database" ) func TestPostgresProviderKeepsLatestCertificateOutsideHistoryWindow(t *testing.T) { dsn := os.Getenv("PULSE_TEST_DATABASE_URL") if dsn == "" { t.Skip("PULSE_TEST_DATABASE_URL is not set") } ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second) defer cancel() pool, err := database.NewPool(ctx, database.Config{URL: dsn, MaxConns: 4, MinConns: 1}) if err != nil { t.Fatal(err) } defer pool.Close() if err := database.Migrate(ctx, pool); err != nil { t.Fatal(err) } run := fmt.Sprintf("%012x", time.Now().UnixNano()&0xffffffffffff) serviceID := "00000000-0000-0000-0000-" + run httpProbeID := "00000000-0000-0000-0001-" + run tlsProbeID := "00000000-0000-0000-0002-" + run certificateID := "00000000-0000-0000-0003-" + run now := time.Now().UTC().Truncate(time.Microsecond) certificateObservedAt := now.Add(-10 * time.Minute) expiresAt := now.Add(80 * 24 * time.Hour) if _, err := pool.Exec(ctx, `INSERT INTO services (id,name,state,revision) VALUES ($1,$2,'unknown',1)`, serviceID, "certificate-window-"+run); err != nil { t.Fatal(err) } if _, err := pool.Exec(ctx, `INSERT INTO probes (id,service_id,name,probe_type,target,interval_seconds,timeout_seconds,enabled) VALUES ($1,$3,'HTTP','http','{}'::jsonb,30,10,true),($2,$3,'TLS','tls','{}'::jsonb,21600,10,true)`, httpProbeID, tlsProbeID, serviceID); err != nil { t.Fatal(err) } if _, err := pool.Exec(ctx, `INSERT INTO probe_results (id,probe_id,observed_at,completed_at,state,response_time_ms,attributes) VALUES ($1,$2,$3,$3,'up',25,'{}'::jsonb)`, "00000000-0000-0000-0004-"+run, tlsProbeID, certificateObservedAt); err != nil { t.Fatal(err) } if _, err := pool.Exec(ctx, `INSERT INTO service_certificates (id,service_id,observed_at,expires_at,issuer,subject,hostname_valid,verification_state) VALUES ($1,$2,$3,$4,'Pulse test issuer','CN=pulse.test',true,'valid')`, certificateID, serviceID, certificateObservedAt, expiresAt); err != nil { t.Fatal(err) } for index := 0; index < 8; index++ { observedAt := now.Add(-time.Duration(index) * time.Second) resultID := fmt.Sprintf("00000000-0000-0000-%04x-%012x", index+5, time.Now().UnixNano()&0xffffffffffff) if _, err := pool.Exec(ctx, `INSERT INTO probe_results (id,probe_id,observed_at,completed_at,state,response_time_ms,status_code,attributes) VALUES ($1,$2,$3,$3,'up',20,200,'{}'::jsonb)`, resultID, httpProbeID, observedAt); err != nil { t.Fatal(err) } } provider, err := NewPostgresProvider(pool, StatusPolicy{FreshnessMaxAge: time.Minute, MaxServices: 1000, MaxHistory: 5}) if err != nil { t.Fatal(err) } snapshot, err := provider.Snapshot(ctx) if err != nil { t.Fatal(err) } for _, item := range snapshot.Services { if item.ID != serviceID { continue } if len(item.History) != 5 { t.Fatalf("history was not bounded: %d", len(item.History)) } certificate := item.Certificate if certificate == nil || certificate.ID != certificateID || certificate.ServiceID != serviceID || certificate.VerificationState != "valid" || certificate.ExpiresAt == nil || !certificate.ExpiresAt.Equal(expiresAt) { t.Fatalf("latest certificate was not projected independently of bounded probe history: %+v", certificate) } for _, result := range item.History { if result.Certificate != nil { t.Fatal("latest certificate was duplicated across history rows") } } return } t.Fatalf("run-scoped service %s was not projected", serviceID) }