internal/store/retention_test.go

a7c9352033c319587613cb6825035e8d99f5871a
gitbay/internal/store/retention_test.go history · blame · raw

151 lines · 4689 bytes

  1package store
  2
  3import (
  4	"testing"
  5	"time"
  6)
  7
  8func retentionFixture(t *testing.T) (*Store, int64) {
  9	t.Helper()
 10	s := open(t)
 11	if err := s.MigrateUp(); err != nil {
 12		t.Fatal(err)
 13	}
 14	uid, err := s.CreateUser("cmc", true)
 15	if err != nil {
 16		t.Fatal(err)
 17	}
 18	return s, uid
 19}
 20
 21func TestSweepRemovesExpiredSessionsAndTokens(t *testing.T) {
 22	s, uid := retentionFixture(t)
 23	if err := s.CreateWebSession("live", uid, time.Hour); err != nil {
 24		t.Fatal(err)
 25	}
 26	if err := s.CreateWebSession("dead", uid, -time.Hour); err != nil {
 27		t.Fatal(err)
 28	}
 29	if err := s.CreateLoginToken(uid, "stale", -time.Minute); err != nil {
 30		t.Fatal(err)
 31	}
 32
 33	// Retention is unset: expired rows still go, since nothing keeps them
 34	// meaningful once they cannot authenticate.
 35	got, err := s.Sweep(Retention{}, time.Now())
 36	if err != nil {
 37		t.Fatal(err)
 38	}
 39	if got["web_sessions"] != 1 || got["login_tokens"] != 1 {
 40		t.Fatalf("swept %v", got)
 41	}
 42	var n int
 43	s.DB.QueryRow("SELECT COUNT(*) FROM web_sessions").Scan(&n)
 44	if n != 1 {
 45		t.Fatalf("%d sessions remain, want the live one", n)
 46	}
 47	if _, err := s.WebSessionUser("live"); err != nil {
 48		t.Fatalf("live session swept: %v", err)
 49	}
 50}
 51
 52// Zero retention keeps forever: deleting an audit trail is not a default.
 53func TestSweepKeepsWhenUnconfigured(t *testing.T) {
 54	s, uid := retentionFixture(t)
 55	s.Audit(uid, "cmd repo create", map[string]any{"argv": []string{"a/b"}})
 56	s.DB.Exec("UPDATE audit_log SET created_at = '2020-01-01T00:00:00.000Z'")
 57
 58	if _, err := s.Sweep(Retention{}, time.Now()); err != nil {
 59		t.Fatal(err)
 60	}
 61	var n int
 62	s.DB.QueryRow("SELECT COUNT(*) FROM audit_log").Scan(&n)
 63	if n != 1 {
 64		t.Fatalf("audit row swept with retention unset (%d rows)", n)
 65	}
 66
 67	if _, err := s.Sweep(Retention{Audit: 24 * time.Hour}, time.Now()); err != nil {
 68		t.Fatal(err)
 69	}
 70	s.DB.QueryRow("SELECT COUNT(*) FROM audit_log").Scan(&n)
 71	if n != 0 {
 72		t.Fatalf("audit row survived its retention (%d rows)", n)
 73	}
 74}
 75
 76// A delivery still being retried is live state, however old its first
 77// attempt; only finished ones age out.
 78func TestSweepKeepsUnfinishedWork(t *testing.T) {
 79	s, uid := retentionFixture(t)
 80	repoID, err := s.CreateRepo("user", uid, "lib", "public")
 81	if err != nil {
 82		t.Fatal(err)
 83	}
 84	if _, err := s.AddWebhook(repoID, "https://example.test/h", "", "*"); err != nil {
 85		t.Fatal(err)
 86	}
 87	for i := 0; i < 3; i++ {
 88		if err := s.RecordEvent(repoID, uid, "push", "{}"); err != nil {
 89			t.Fatal(err)
 90		}
 91	}
 92	s.DB.Exec("UPDATE webhook_deliveries SET created_at = '2020-01-01T00:00:00.000Z'")
 93	s.DB.Exec("UPDATE webhook_deliveries SET delivered_at = '2020-01-02T00:00:00.000Z' WHERE id = 1")
 94	s.DB.Exec("UPDATE webhook_deliveries SET failed_at = '2020-01-02T00:00:00.000Z' WHERE id = 2")
 95
 96	got, err := s.Sweep(Retention{WebhookDeliveries: time.Hour}, time.Now())
 97	if err != nil {
 98		t.Fatal(err)
 99	}
100	if got["webhook_deliveries"] != 2 {
101		t.Fatalf("swept %v, want the delivered and the failed one", got)
102	}
103	var n int
104	s.DB.QueryRow("SELECT COUNT(*) FROM webhook_deliveries").Scan(&n)
105	if n != 1 {
106		t.Fatalf("%d deliveries remain, want the one still being retried", n)
107	}
108}
109
110// events.id is the parent of webhook_deliveries.event_id under ON DELETE
111// CASCADE, so sweeping events would take a queued delivery with it. The
112// sweep skips any event that still has one.
113func TestSweepEventsSpareQueuedDeliveries(t *testing.T) {
114	s, uid := retentionFixture(t)
115	repoID, err := s.CreateRepo("user", uid, "lib", "public")
116	if err != nil {
117		t.Fatal(err)
118	}
119	if _, err := s.AddWebhook(repoID, "https://example.test/h", "", "*"); err != nil {
120		t.Fatal(err)
121	}
122	if err := s.RecordEvent(repoID, uid, "push", "{}"); err != nil {
123		t.Fatal(err)
124	}
125	s.DB.Exec("UPDATE events SET created_at = '2020-01-01T00:00:00.000Z'")
126	s.DB.Exec("UPDATE webhook_deliveries SET created_at = '2020-01-01T00:00:00.000Z'")
127
128	// The delivery has neither delivered_at nor failed_at: still queued.
129	got, err := s.Sweep(Retention{Events: time.Hour, WebhookDeliveries: time.Hour}, time.Now())
130	if err != nil {
131		t.Fatal(err)
132	}
133	if got["events"] != 0 || got["webhook_deliveries"] != 0 {
134		t.Fatalf("swept %v, want nothing while the delivery is queued", got)
135	}
136	var n int
137	s.DB.QueryRow("SELECT COUNT(*) FROM webhook_deliveries").Scan(&n)
138	if n != 1 {
139		t.Fatal("queued delivery cascaded away with its event")
140	}
141
142	// Once it finishes, both go.
143	s.DB.Exec("UPDATE webhook_deliveries SET delivered_at = '2020-01-02T00:00:00.000Z'")
144	if _, err := s.Sweep(Retention{Events: time.Hour, WebhookDeliveries: time.Hour}, time.Now()); err != nil {
145		t.Fatal(err)
146	}
147	s.DB.QueryRow("SELECT COUNT(*) FROM events").Scan(&n)
148	if n != 0 {
149		t.Fatalf("%d events remain after the delivery finished", n)
150	}
151}