| new file mode 100644 |
| index 000000000..8dfc8341c |
| --- /dev/null |
| +++ b/internal/console/sponsoredenrollment_test.go |
| @@ -0,0 +1,776 @@ |
| +package console |
| + |
| +import ( |
| + "bytes" |
| + "context" |
| + "database/sql" |
| + "encoding/json" |
| + "errors" |
| + "fmt" |
| + "path/filepath" |
| + "reflect" |
| + "strings" |
| + "sync" |
| + "testing" |
| + "time" |
| + |
| + "github.com/carlosframework/platform/internal/domains" |
| +) |
| + |
| +const enrollmentActor = "sponsor@example.test" |
| + |
| +var enrollmentNow = time.Date(2026, 10, 9, 18, 0, 0, 0, time.UTC) |
| + |
| +func enrollmentContext(t *testing.T) context.Context { |
| + t.Helper() |
| + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) |
| + t.Cleanup(cancel) |
| + return ctx |
| +} |
| +func enrollmentStore(t *testing.T) *Store { |
| + t.Helper() |
| + s := openSponsoredTestStore(t, "enrollment") |
| + if err := s.SetOperator(enrollmentActor, true); err != nil { |
| + t.Fatal(err) |
| + } |
| + return s |
| +} |
| +func enrollmentRequest(op int, ids ...int64) sponsoredEnrollmentRequest { |
| + r := sponsoredEnrollmentRequest{OperationID: fmt.Sprintf("%064x", op), PreviewDigest: strings.Repeat("a", 64), DecisionReference: sponsoredEnrollmentDecision, Note: "reviewed", CoverageAcknowledged: true, Items: []sponsoredEnrollmentChoice{}} |
| + for _, id := range ids { |
| + r.Items = append(r.Items, sponsoredEnrollmentChoice{AccountID: id, Sqid: domains.Sqid(id), Choice: sponsoredEnroll, Reason: "existing account", ConfirmSystemAccount: id == 1}) |
| + } |
| + return r |
| +} |
| +func enrollmentRun(t *testing.T, s *Store, r sponsoredEnrollmentRequest) sponsoredEnrollmentResult { |
| + t.Helper() |
| + v, err := s.enrollSponsoredCandidates(enrollmentContext(t), enrollmentActor, r, enrollmentNow) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + return v |
| +} |
| +func enrollmentExistingSnapshot(t *testing.T, s *Store) []sponsoredTableSnapshot { |
| + t.Helper() |
| + var out []sponsoredTableSnapshot |
| + for _, v := range sponsoredLogicalSnapshot(t, s.db) { |
| + if v.Name != "sponsored_enrollment_receipts" && v.Name != "sponsored_cohort_memberships" { |
| + out = append(out, v) |
| + } |
| + } |
| + return out |
| +} |
| +func enrollmentZero(t *testing.T, v sponsoredEnrollmentResult, err error) { |
| + t.Helper() |
| + if err == nil || !reflect.DeepEqual(v, sponsoredEnrollmentResult{}) { |
| + t.Fatalf("result=%#v error=%v", v, err) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentMixedRosterAndEvidenceOnly(t *testing.T) { |
| + s := enrollmentStore(t) |
| + seedSponsoredCoverage(t, s, false) |
| + sponsoredInsertAccount(t, s.db, 8, "review", 0, 0) |
| + first := enrollmentRun(t, s, enrollmentRequest(1, 2)) |
| + if first.Replayed { |
| + t.Fatal("first replayed") |
| + } |
| + before := enrollmentExistingSnapshot(t, s) |
| + req := enrollmentRequest(2, 1, 2, 3, 4, 5, 6, 7, 8) |
| + req.Items[2].Choice = sponsoredExclude |
| + req.Items[7].Choice = sponsoredNeedsReview |
| + got := enrollmentRun(t, s, req) |
| + want := []sponsoredEnrollmentOutcome{sponsoredAdded, sponsoredAlreadyRecorded, sponsoredExcludedByOperator, sponsoredExcludedRetired, sponsoredExcludedRetired, sponsoredAdded, sponsoredExcludedMissing, sponsoredAwaitingReview} |
| + for i, row := range got.Historical.Rows { |
| + if row.Outcome != want[i] || row.AccountID != req.Items[i].AccountID { |
| + t.Fatalf("row %d: %#v", i, row) |
| + } |
| + } |
| + f := got.Historical.Rows[5].Facts |
| + if !f.CheckoutPresent || !f.Billing.CustomerPresent || f.Billing.Subscription != "sub_full" || f.OpenBillingReviews != 2 || f.EntitlementReviews != 2 || !f.Outbox.SerialOwed || f.Outbox.PublishedRevision != 5 { |
| + t.Fatalf("facts %#v", f) |
| + } |
| + if after := enrollmentExistingSnapshot(t, s); !reflect.DeepEqual(before, after) { |
| + t.Fatal("modified existing tables") |
| + } |
| + read, err := s.readSponsoredEnrollment(enrollmentContext(t), enrollmentActor, req.OperationID) |
| + if err != nil || !reflect.DeepEqual(read, got.Historical) { |
| + t.Fatalf("read %v %#v", err, read) |
| + } |
| + if err := s.migrate(); err != nil { |
| + t.Fatal(err) |
| + } |
| + read, err = s.readSponsoredEnrollment(enrollmentContext(t), enrollmentActor, req.OperationID) |
| + if err != nil || !reflect.DeepEqual(read, got.Historical) { |
| + t.Fatal("reopen-chain changed receipt", err) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentReplayRetirementMissingAndAddOnly(t *testing.T) { |
| + s := enrollmentStore(t) |
| + for _, id := range []int64{2, 3, 4} { |
| + sponsoredInsertAccount(t, s.db, id, "account", 0, 0) |
| + } |
| + req := enrollmentRequest(10, 2, 3) |
| + got := enrollmentRun(t, s, req) |
| + sponsoredExec(t, s.db, `UPDATE accounts SET deleted_at=9 WHERE id=2`) |
| + sponsoredExec(t, s.db, `INSERT INTO billing_retirements(account_id,started_at) VALUES(3,9)`) |
| + before := sponsoredLogicalSnapshot(t, s.db) |
| + replay, err := s.enrollSponsoredCandidates(enrollmentContext(t), enrollmentActor, req, time.Time{}) |
| + if err != nil || !replay.Replayed || !reflect.DeepEqual(replay.Historical, got.Historical) { |
| + t.Fatalf("replay %#v %v", replay, err) |
| + } |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, s.db)) { |
| + t.Fatal("replay wrote") |
| + } |
| + newer := enrollmentRun(t, s, enrollmentRequest(11, 2, 3)) |
| + for _, row := range newer.Historical.Rows { |
| + if row.Outcome != sponsoredExcludedRetired || row.FirstEnrollmentOperationID != req.OperationID { |
| + t.Fatalf("retired %#v", row) |
| + } |
| + } |
| + sponsoredExec(t, s.db, `DELETE FROM accounts WHERE id=2`) |
| + missing := enrollmentRun(t, s, enrollmentRequest(12, 2)) |
| + if missing.Historical.Rows[0].Outcome != sponsoredExcludedMissing || missing.Historical.Rows[0].FirstEnrollmentOperationID != req.OperationID { |
| + t.Fatal("lost missing history") |
| + } |
| + excluded := enrollmentRequest(13, 4) |
| + excluded.Items[0].Choice = sponsoredExclude |
| + old := enrollmentRun(t, s, excluded) |
| + if old.Historical.Rows[0].FirstEnrollmentOperationID != "" { |
| + t.Fatal("unexpected link") |
| + } |
| + enrollmentRun(t, s, enrollmentRequest(14, 4)) |
| + read, err := s.readSponsoredEnrollment(enrollmentContext(t), enrollmentActor, excluded.OperationID) |
| + if err != nil || !reflect.DeepEqual(read, old.Historical) { |
| + t.Fatal("blank historical link invalidated", err) |
| + } |
| + for _, choice := range []sponsoredEnrollmentChoiceKind{sponsoredExclude, sponsoredNeedsReview} { |
| + r := enrollmentRequest(20+int(choice), 4) |
| + r.Items[0].Choice = choice |
| + v := enrollmentRun(t, s, r) |
| + if v.Historical.Rows[0].FirstEnrollmentOperationID != fmt.Sprintf("%064x", 14) { |
| + t.Fatal("replaced first membership") |
| + } |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentRequestValidationAndReplayConflict(t *testing.T) { |
| + s := enrollmentStore(t) |
| + sponsoredInsertAccount(t, s.db, 2, "account", 0, 0) |
| + req := enrollmentRequest(30, 2) |
| + enrollmentRun(t, s, req) |
| + mutations := []func(*sponsoredEnrollmentRequest){ |
| + func(r *sponsoredEnrollmentRequest) { r.Note = "changed" }, func(r *sponsoredEnrollmentRequest) { r.PreviewDigest = strings.Repeat("b", 64) }, func(r *sponsoredEnrollmentRequest) { r.Items[0].Reason = "changed" }, func(r *sponsoredEnrollmentRequest) { r.Items[0].Choice = sponsoredExclude }, |
| + } |
| + for _, mutate := range mutations { |
| + r := req |
| + r.Items = append([]sponsoredEnrollmentChoice{}, req.Items...) |
| + mutate(&r) |
| + before := sponsoredLogicalSnapshot(t, s.db) |
| + v, err := s.enrollSponsoredCandidates(enrollmentContext(t), enrollmentActor, r, enrollmentNow) |
| + enrollmentZero(t, v, err) |
| + if !errors.Is(err, errSponsoredEnrollmentConflict) { |
| + t.Fatal(err) |
| + } |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, s.db)) { |
| + t.Fatal("conflict wrote") |
| + } |
| + } |
| + invalid := []sponsoredEnrollmentRequest{enrollmentRequest(31), enrollmentRequest(32, 2, 2), enrollmentRequest(33, 3, 2), enrollmentRequest(34, 0)} |
| + for _, mutate := range []func(*sponsoredEnrollmentRequest){func(r *sponsoredEnrollmentRequest) { r.OperationID = "x" }, func(r *sponsoredEnrollmentRequest) { r.PreviewDigest = "x" }, func(r *sponsoredEnrollmentRequest) { r.DecisionReference = "other" }, func(r *sponsoredEnrollmentRequest) { r.CoverageAcknowledged = false }, func(r *sponsoredEnrollmentRequest) { r.Items[0].Sqid = "wrong" }, func(r *sponsoredEnrollmentRequest) { r.Items[0].Choice = 99 }, func(r *sponsoredEnrollmentRequest) { r.Items[0].Reason = "" }, func(r *sponsoredEnrollmentRequest) { r.Note = strings.Repeat("x", 257) }, func(r *sponsoredEnrollmentRequest) { r.Items[0].ConfirmSystemAccount = true }} { |
| + r := enrollmentRequest(35, 2) |
| + mutate(&r) |
| + invalid = append(invalid, r) |
| + } |
| + ops := enrollmentRequest(36, 1) |
| + ops.Items[0].ConfirmSystemAccount = false |
| + invalid = append(invalid, ops) |
| + many := enrollmentRequest(37) |
| + for i := int64(1); i <= 1001; i++ { |
| + many.Items = append(many.Items, enrollmentRequest(1, i).Items[0]) |
| + } |
| + invalid = append(invalid, many) |
| + before := sponsoredLogicalSnapshot(t, s.db) |
| + for _, r := range invalid { |
| + v, err := s.enrollSponsoredCandidates(enrollmentContext(t), enrollmentActor, r, enrollmentNow) |
| + enrollmentZero(t, v, err) |
| + if !errors.Is(err, errSponsoredEnrollmentInvalid) { |
| + t.Fatalf("want invalid: %v", err) |
| + } |
| + } |
| + for _, actor := range []string{"Sponsor@example.test", " sponsor@example.test", "bad", ""} { |
| + v, err := s.enrollSponsoredCandidates(enrollmentContext(t), actor, enrollmentRequest(40, 2), enrollmentNow) |
| + enrollmentZero(t, v, err) |
| + } |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, s.db)) { |
| + t.Fatal("invalid request wrote") |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentEscapingAndReceiptLimit(t *testing.T) { |
| + s := enrollmentStore(t) |
| + for k, reason := range []string{strings.Repeat("<&>", 85) + "<", strings.Repeat("\u2028", 85) + "<"} { |
| + r := enrollmentRequest(50 + k) |
| + for i := int64(2); i <= 1001; i++ { |
| + item := enrollmentRequest(1, i).Items[0] |
| + item.Reason = reason |
| + r.Items = append(r.Items, item) |
| + } |
| + v := enrollmentRun(t, s, r) |
| + for _, row := range v.Historical.Rows { |
| + if row.Outcome != sponsoredExcludedMissing { |
| + t.Fatal("all-excluded result", row) |
| + } |
| + } |
| + var members int |
| + if err := s.db.QueryRow(`SELECT count(*) FROM sponsored_cohort_memberships`).Scan(&members); err != nil || members != 0 { |
| + t.Fatal("all-excluded wrote membership", members, err) |
| + } |
| + } |
| + sponsoredInsertAccount(t, s.db, 2, strings.Repeat("x", sponsoredEnrollmentMaxBody), 0, 0) |
| + before := sponsoredLogicalSnapshot(t, s.db) |
| + v, err := s.enrollSponsoredCandidates(enrollmentContext(t), enrollmentActor, enrollmentRequest(55, 2), enrollmentNow) |
| + enrollmentZero(t, v, err) |
| + if !errors.Is(err, errSponsoredEnrollmentTooLarge) { |
| + t.Fatal(err) |
| + } |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, s.db)) { |
| + t.Fatal("oversize wrote") |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentAtomicRollback(t *testing.T) { |
| + for _, fail := range []string{"receipt", "first", "later"} { |
| + t.Run(fail, func(t *testing.T) { |
| + s := enrollmentStore(t) |
| + for _, id := range []int64{2, 3} { |
| + sponsoredInsertAccount(t, s.db, id, "account", 0, 0) |
| + } |
| + q := `CREATE TRIGGER fail_enrollment BEFORE INSERT ON sponsored_enrollment_receipts BEGIN SELECT RAISE(ABORT,'injected'); END` |
| + if fail != "receipt" { |
| + id := 2 |
| + if fail == "later" { |
| + id = 3 |
| + } |
| + q = fmt.Sprintf(`CREATE TRIGGER fail_enrollment BEFORE INSERT ON sponsored_cohort_memberships WHEN NEW.account_id=%d BEGIN SELECT RAISE(ABORT,'injected'); END`, id) |
| + } |
| + sponsoredExec(t, s.db, q) |
| + before := sponsoredLogicalSnapshot(t, s.db) |
| + v, err := s.enrollSponsoredCandidates(enrollmentContext(t), enrollmentActor, enrollmentRequest(60, 2, 3), enrollmentNow) |
| + enrollmentZero(t, v, err) |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, s.db)) { |
| + t.Fatal("partial commit") |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentWriterReservationDirect(t *testing.T) { |
| + path := filepath.Join(t.TempDir(), "writers.db") |
| + reader := openSponsoredStoreAt(t, path) |
| + writer := openSponsoredStoreAt(t, path) |
| + if err := reader.SetOperator(enrollmentActor, true); err != nil { |
| + t.Fatal(err) |
| + } |
| + sponsoredInsertAccount(t, reader.db, 2, "account", 0, 0) |
| + enrollmentRun(t, reader, enrollmentRequest(70, 2)) |
| + before := sponsoredLogicalSnapshot(t, reader.db) |
| + ctx := enrollmentContext(t) |
| + tx, err := reader.db.BeginTx(ctx, nil) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer tx.Rollback() |
| + got, err := enrollSponsoredCandidatesTx(ctx, tx, enrollmentActor, enrollmentRequest(70, 2), enrollmentNow, func() error { |
| + bounded, cancel := context.WithTimeout(ctx, 200*time.Millisecond) |
| + defer cancel() |
| + _, err := writer.db.ExecContext(bounded, `INSERT INTO users (address,created_at,operator) VALUES(?,?,?) ON CONFLICT(address) DO UPDATE SET operator=excluded.operator`, enrollmentActor, enrollmentNow.Unix(), 0) |
| + if err == nil { |
| + return errors.New("second writer succeeded under reservation") |
| + } |
| + if !errors.Is(err, context.DeadlineExceeded) && !strings.Contains(strings.ToLower(err.Error()), "locked") && !strings.Contains(err.Error(), "SQLITE_BUSY") { |
| + return err |
| + } |
| + return nil |
| + }) |
| + if err != nil || !got.Replayed { |
| + t.Fatalf("reservation: result=%#v err=%v", got, err) |
| + } |
| + if err := tx.Commit(); err != nil { |
| + t.Fatal(err) |
| + } |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, reader.db)) { |
| + t.Fatal("reservation or replay changed bytes") |
| + } |
| + if err := writer.SetOperator(enrollmentActor, false); err != nil { |
| + t.Fatal(err) |
| + } |
| + v, err := reader.enrollSponsoredCandidates(ctx, enrollmentActor, enrollmentRequest(70, 2), enrollmentNow) |
| + enrollmentZero(t, v, err) |
| + if !errors.Is(err, errSponsoredEnrollmentDenied) { |
| + t.Fatal(err) |
| + } |
| + if _, err := reader.readSponsoredEnrollment(ctx, enrollmentActor, strings.Repeat("f", 64)); !errors.Is(err, errSponsoredEnrollmentDenied) { |
| + t.Fatal("operation disclosed", err) |
| + } |
| + if err := writer.SetOperator("other@example.test", true); err != nil { |
| + t.Fatal(err) |
| + } |
| + if _, err := reader.readSponsoredEnrollment(ctx, "other@example.test", fmt.Sprintf("%064x", 70)); err != nil { |
| + t.Fatal(err) |
| + } |
| + v, err = reader.enrollSponsoredCandidates(ctx, "other@example.test", enrollmentRequest(70, 2), enrollmentNow) |
| + enrollmentZero(t, v, err) |
| + if !errors.Is(err, errSponsoredEnrollmentConflict) { |
| + t.Fatal(err) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentConcurrentOperations(t *testing.T) { |
| + for _, kind := range []string{"same", "different", "changed"} { |
| + t.Run(kind, func(t *testing.T) { |
| + path := filepath.Join(t.TempDir(), "race.db") |
| + a := openSponsoredStoreAt(t, path) |
| + b := openSponsoredStoreAt(t, path) |
| + if err := a.SetOperator(enrollmentActor, true); err != nil { |
| + t.Fatal(err) |
| + } |
| + sponsoredInsertAccount(t, a.db, 2, "account", 0, 0) |
| + reqs := []sponsoredEnrollmentRequest{enrollmentRequest(80, 2), enrollmentRequest(80, 2)} |
| + if kind == "different" { |
| + reqs[1].OperationID = fmt.Sprintf("%064x", 81) |
| + } |
| + if kind == "changed" { |
| + reqs[1].Note = "different" |
| + } |
| + ctx := enrollmentContext(t) |
| + start := make(chan struct{}) |
| + var wg sync.WaitGroup |
| + results := make([]sponsoredEnrollmentResult, 2) |
| + errs := make([]error, 2) |
| + for i, s := range []*Store{a, b} { |
| + wg.Add(1) |
| + go func(i int, s *Store) { |
| + defer wg.Done() |
| + <-start |
| + results[i], errs[i] = s.enrollSponsoredCandidates(ctx, enrollmentActor, reqs[i], enrollmentNow) |
| + }(i, s) |
| + } |
| + close(start) |
| + wg.Wait() |
| + var count int |
| + if err := a.db.QueryRow(`SELECT count(*) FROM sponsored_cohort_memberships`).Scan(&count); err != nil || count != 1 { |
| + t.Fatal("membership count", count, err) |
| + } |
| + switch kind { |
| + case "same": |
| + if errs[0] != nil || errs[1] != nil || results[0].Replayed == results[1].Replayed { |
| + t.Fatal(results, errs) |
| + } |
| + case "different": |
| + if errs[0] != nil || errs[1] != nil { |
| + t.Fatal(errs) |
| + } |
| + x, y := results[0].Historical.Rows[0].Outcome, results[1].Historical.Rows[0].Outcome |
| + if !((x == sponsoredAdded && y == sponsoredAlreadyRecorded) || (y == sponsoredAdded && x == sponsoredAlreadyRecorded)) { |
| + t.Fatal(x, y) |
| + } |
| + case "changed": |
| + if !((errs[0] == nil && errors.Is(errs[1], errSponsoredEnrollmentConflict)) || (errs[1] == nil && errors.Is(errs[0], errSponsoredEnrollmentConflict))) { |
| + t.Fatal(errs) |
| + } |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentSchemaRefusals(t *testing.T) { |
| + cases := map[string]string{ |
| + "partial": `DROP TABLE sponsored_cohort_memberships`, |
| + "different trigger": `DROP TRIGGER sponsored_receipts_no_update; CREATE TRIGGER sponsored_receipts_no_update BEFORE UPDATE ON sponsored_enrollment_receipts BEGIN SELECT RAISE(ABORT,'different'); END`, |
| + "extra trigger": `CREATE TRIGGER extra_sponsored BEFORE INSERT ON sponsored_cohort_memberships BEGIN SELECT 1; END`, |
| + "extra index": `CREATE INDEX extra_sponsored ON sponsored_cohort_memberships(sqid)`, |
| + "missing trigger": `DROP TRIGGER sponsored_members_parent`, |
| + "wrong table": `DROP TRIGGER sponsored_members_parent; CREATE TRIGGER sponsored_members_parent BEFORE INSERT ON accounts BEGIN SELECT 1; END`, |
| + "rowid": `DROP TABLE sponsored_cohort_memberships; CREATE TABLE sponsored_cohort_memberships(account_id INTEGER PRIMARY KEY,sqid TEXT NOT NULL,first_operation_id TEXT NOT NULL)`, |
| + } |
| + for name, q := range cases { |
| + t.Run(name, func(t *testing.T) { |
| + path := filepath.Join(t.TempDir(), "schema.db") |
| + s, err := Open(path) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + sponsoredExec(t, s.db, q) |
| + before := sponsoredLogicalSnapshot(t, s.db) |
| + if err := s.Close(); err != nil { |
| + t.Fatal(err) |
| + } |
| + reopened, err := Open(path) |
| + if reopened != nil { |
| + reopened.Close() |
| + } |
| + if !errors.Is(err, errSponsoredEnrollmentCorrupt) { |
| + t.Fatal("did not refuse", err) |
| + } |
| + dsn, err := consoleSQLiteDSN(path) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + db, err := sql.Open("sqlite", dsn) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer db.Close() |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, db)) { |
| + t.Fatal("repaired corrupt schema") |
| + } |
| + }) |
| + } |
| + for _, shadow := range []string{"sponsored_enrollment_receipts", "SpOnSoReD_CoHoRt_MeMbErShIpS", "SPONSORED_MEMBERS_PARENT"} { |
| + t.Run("temp "+shadow, func(t *testing.T) { |
| + s := enrollmentStore(t) |
| + before := sponsoredLogicalSnapshot(t, s.db) |
| + sponsoredExec(t, s.db, `CREATE TEMP TABLE "`+shadow+`"(x)`) |
| + if err := s.migrate(); !errors.Is(err, errSponsoredEnrollmentCorrupt) { |
| + t.Fatal("temp shadow accepted", err) |
| + } |
| + sponsoredExec(t, s.db, `DROP TABLE temp."`+shadow+`"`) |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, s.db)) { |
| + t.Fatal("temp refusal changed main") |
| + } |
| + if err := s.migrate(); err != nil { |
| + t.Fatal(err) |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentImmutableRoutes(t *testing.T) { |
| + s := enrollmentStore(t) |
| + for _, id := range []int64{2, 3} { |
| + sponsoredInsertAccount(t, s.db, id, "account", 0, 0) |
| + } |
| + enrollmentRun(t, s, enrollmentRequest(90, 2)) |
| + enrollmentRun(t, s, enrollmentRequest(91, 3)) |
| + sponsoredExec(t, s.db, `PRAGMA recursive_triggers=OFF`) |
| + before := sponsoredLogicalSnapshot(t, s.db) |
| + queries := []string{ |
| + `UPDATE sponsored_enrollment_receipts SET actor='changed'`, `DELETE FROM sponsored_enrollment_receipts`, |
| + `INSERT OR REPLACE INTO sponsored_enrollment_receipts SELECT * FROM sponsored_enrollment_receipts`, `REPLACE INTO sponsored_enrollment_receipts SELECT * FROM sponsored_enrollment_receipts`, |
| + `UPDATE OR REPLACE sponsored_enrollment_receipts SET operation_id=(SELECT min(operation_id) FROM sponsored_enrollment_receipts)`, |
| + `INSERT INTO sponsored_enrollment_receipts SELECT * FROM sponsored_enrollment_receipts WHERE true ON CONFLICT(operation_id) DO UPDATE SET actor='changed'`, |
| + `UPDATE sponsored_cohort_memberships SET sqid='changed'`, `DELETE FROM sponsored_cohort_memberships`, |
| + `INSERT OR REPLACE INTO sponsored_cohort_memberships SELECT * FROM sponsored_cohort_memberships`, `REPLACE INTO sponsored_cohort_memberships SELECT * FROM sponsored_cohort_memberships`, |
| + `UPDATE OR REPLACE sponsored_cohort_memberships SET account_id=2`, |
| + `INSERT INTO sponsored_cohort_memberships SELECT * FROM sponsored_cohort_memberships WHERE true ON CONFLICT(account_id) DO UPDATE SET sqid='changed'`, |
| + `INSERT INTO sponsored_cohort_memberships VALUES(4,'bad','ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')`, |
| + } |
| + for _, table := range []string{"sponsored_enrollment_receipts", "sponsored_cohort_memberships"} { |
| + for _, alias := range []string{"rowid", "_rowid_", "oid"} { |
| + queries = append(queries, `UPDATE OR REPLACE `+table+` SET `+alias+`=1`, `INSERT OR REPLACE INTO `+table+`(`+alias+`) VALUES(1)`) |
| + } |
| + } |
| + for _, q := range queries { |
| + if _, err := s.db.Exec(q); err == nil { |
| + t.Fatalf("mutation succeeded: %s", q) |
| + } |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, s.db)) { |
| + t.Fatal("immutable evidence changed", q) |
| + } |
| + } |
| + if _, err := s.readSponsoredEnrollment(enrollmentContext(t), enrollmentActor, fmt.Sprintf("%064x", 90)); err != nil { |
| + t.Fatal(err) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentCanonicalCorruption(t *testing.T) { |
| + for _, name := range []string{"case", "duplicate", "newline", "digest", "columns", "version", "order", "added link", "count", "nonadded self"} { |
| + t.Run(name, func(t *testing.T) { |
| + s := enrollmentStore(t) |
| + for _, id := range []int64{2, 3} { |
| + sponsoredInsertAccount(t, s.db, id, "account", 0, 0) |
| + } |
| + req := enrollmentRequest(100, 2, 3) |
| + original := enrollmentRun(t, s, req).Historical |
| + var body []byte |
| + if err := s.db.QueryRow(`SELECT body FROM sponsored_enrollment_receipts WHERE operation_id=?`, req.OperationID).Scan(&body); err != nil { |
| + t.Fatal(err) |
| + } |
| + sponsoredExec(t, s.db, `DROP TRIGGER sponsored_receipts_no_update`) |
| + switch name { |
| + case "case": |
| + body = bytes.Replace(body, []byte(`"Version"`), []byte(`"version"`), 1) |
| + case "duplicate": |
| + body = bytes.Replace(body, []byte(`"Version":1`), []byte(`"Version":1,"Version":1`), 1) |
| + case "newline": |
| + body = append(body, '\n') |
| + case "version": |
| + original.Version = 2 |
| + body, _ = json.Marshal(original) |
| + case "order": |
| + original.Rows[0], original.Rows[1] = original.Rows[1], original.Rows[0] |
| + body, _ = json.Marshal(original) |
| + case "added link": |
| + original.Rows[0].FirstEnrollmentOperationID = strings.Repeat("e", 64) |
| + body, _ = json.Marshal(original) |
| + case "count": |
| + sponsoredExec(t, s.db, `DROP TRIGGER sponsored_members_no_delete`) |
| + sponsoredExec(t, s.db, `DELETE FROM sponsored_cohort_memberships WHERE account_id=2`) |
| + case "nonadded self": |
| + original.Rows[0].Outcome = sponsoredAlreadyRecorded |
| + body, _ = json.Marshal(original) |
| + } |
| + sponsoredExec(t, s.db, `UPDATE sponsored_enrollment_receipts SET body=?,body_digest=? WHERE operation_id=?`, body, sponsoredEnrollmentDigest("receipt", body), req.OperationID) |
| + if name == "digest" { |
| + sponsoredExec(t, s.db, `UPDATE sponsored_enrollment_receipts SET request_digest=?`, strings.Repeat("0", 64)) |
| + } |
| + if name == "columns" { |
| + sponsoredExec(t, s.db, `UPDATE sponsored_enrollment_receipts SET recorded_at=recorded_at+1`) |
| + } |
| + got, err := s.readSponsoredEnrollment(enrollmentContext(t), enrollmentActor, req.OperationID) |
| + if !errors.Is(err, errSponsoredEnrollmentCorrupt) || !reflect.DeepEqual(got, sponsoredEnrollmentReceipt{}) { |
| + t.Fatalf("corrupt accepted %#v %v", got, err) |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentContextAndSourceCorruption(t *testing.T) { |
| + s := enrollmentStore(t) |
| + sponsoredInsertAccount(t, s.db, 2, "account", 0, 0) |
| + ctx, cancel := context.WithCancel(context.Background()) |
| + cancel() |
| + v, err := s.enrollSponsoredCandidates(ctx, enrollmentActor, enrollmentRequest(110, 2), enrollmentNow) |
| + enrollmentZero(t, v, err) |
| + tx, err := s.db.BeginTx(enrollmentContext(t), nil) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + ctx, cancel = context.WithCancel(context.Background()) |
| + v, err = enrollSponsoredCandidatesTx(ctx, tx, enrollmentActor, enrollmentRequest(111, 2), enrollmentNow, func() error { cancel(); return nil }) |
| + enrollmentZero(t, v, err) |
| + tx.Rollback() |
| + for _, q := range []string{`UPDATE accounts SET created_at='x' WHERE id=2`, `UPDATE accounts SET created_at=0,sqid='wrong' WHERE id=2`} { |
| + sponsoredExec(t, s.db, q) |
| + before := sponsoredLogicalSnapshot(t, s.db) |
| + v, err := s.enrollSponsoredCandidates(enrollmentContext(t), enrollmentActor, enrollmentRequest(112, 2), enrollmentNow) |
| + enrollmentZero(t, v, err) |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, s.db)) { |
| + t.Fatal("source corruption wrote") |
| + } |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentMigrationRollsBackNaturalDDLCollision(t *testing.T) { |
| + for _, kind := range []string{"trigger", "table"} { |
| + t.Run(kind, func(t *testing.T) { |
| + path := filepath.Join(t.TempDir(), "ddl.db") |
| + s, err := Open(path) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + sponsoredExec(t, s.db, `DROP TABLE sponsored_cohort_memberships`) |
| + sponsoredExec(t, s.db, `DROP TABLE sponsored_enrollment_receipts`) |
| + sponsoredExec(t, s.db, `CREATE TABLE enrollment_unrelated(x)`) |
| + sponsoredExec(t, s.db, `INSERT INTO enrollment_unrelated VALUES(7)`) |
| + if kind == "trigger" { |
| + sponsoredExec(t, s.db, `CREATE TRIGGER sponsored_members_parent BEFORE INSERT ON enrollment_unrelated BEGIN SELECT 1; END`) |
| + } else { |
| + sponsoredExec(t, s.db, `CREATE TABLE sponsored_receipts_no_update(x)`) |
| + sponsoredExec(t, s.db, `INSERT INTO sponsored_receipts_no_update VALUES(9)`) |
| + } |
| + before := sponsoredLogicalSnapshot(t, s.db) |
| + if err := s.Close(); err != nil { |
| + t.Fatal(err) |
| + } |
| + opened, err := Open(path) |
| + if opened != nil { |
| + opened.Close() |
| + } |
| + if !errors.Is(err, errSponsoredEnrollmentCorrupt) { |
| + t.Fatalf("reserved-name collision must be corrupt on first Open: %v", err) |
| + } |
| + dsn, err := consoleSQLiteDSN(path) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + db, err := sql.Open("sqlite", dsn) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer db.Close() |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, db)) { |
| + t.Fatal("collision changed database") |
| + } |
| + var count int |
| + if err := db.QueryRow(`SELECT count(*) FROM sqlite_master WHERE tbl_name IN ('sponsored_enrollment_receipts','sponsored_cohort_memberships')`).Scan(&count); err != nil || count != 0 { |
| + t.Fatal("new objects survived", count, err) |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentSchemaCreationRollsBackMidDDL(t *testing.T) { |
| + s := enrollmentStore(t) |
| + sponsoredExec(t, s.db, `DROP TABLE sponsored_cohort_memberships`) |
| + sponsoredExec(t, s.db, `DROP TABLE sponsored_enrollment_receipts`) |
| + sponsoredExec(t, s.db, `CREATE TABLE enrollment_unrelated(x)`) |
| + sponsoredExec(t, s.db, `INSERT INTO enrollment_unrelated VALUES(7)`) |
| + sponsoredExec(t, s.db, `CREATE TRIGGER sponsored_members_parent BEFORE INSERT ON enrollment_unrelated BEGIN SELECT 1; END`) |
| + before := sponsoredLogicalSnapshot(t, s.db) |
| + tx, err := s.db.Begin() |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer tx.Rollback() |
| + if err := createSponsoredEnrollmentSchemaTx(tx); err == nil { |
| + t.Fatal("late DDL collision accepted") |
| + } |
| + var count int |
| + if err := tx.QueryRow(`SELECT count(*) FROM sqlite_master WHERE tbl_name IN ('sponsored_enrollment_receipts','sponsored_cohort_memberships')`).Scan(&count); err != nil || count != len(sponsoredEnrollmentSchema)-1 { |
| + t.Fatal("collision did not occur after earlier DDL", count, err) |
| + } |
| + if err := tx.Rollback(); err != nil { |
| + t.Fatal(err) |
| + } |
| + if !reflect.DeepEqual(before, sponsoredLogicalSnapshot(t, s.db)) { |
| + t.Fatal("partial DDL committed") |
| + } |
| + if err := s.db.QueryRow(`SELECT count(*) FROM sqlite_master WHERE tbl_name IN ('sponsored_enrollment_receipts','sponsored_cohort_memberships')`).Scan(&count); err != nil || count != 0 { |
| + t.Fatal("new objects survived", count, err) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentParentExistenceWithoutParentDecode(t *testing.T) { |
| + s := enrollmentStore(t) |
| + sponsoredInsertAccount(t, s.db, 2, "account", 0, 0) |
| + first := enrollmentRequest(120, 2) |
| + enrollmentRun(t, s, first) |
| + sponsoredExec(t, s.db, `DROP TRIGGER sponsored_receipts_no_update`) |
| + sponsoredExec(t, s.db, `UPDATE sponsored_enrollment_receipts SET body=? WHERE operation_id=?`, []byte("not canonical"), first.OperationID) |
| + next := enrollmentRequest(121, 2) |
| + v := enrollmentRun(t, s, next) |
| + if v.Historical.Rows[0].Outcome != sponsoredAlreadyRecorded { |
| + t.Fatal("not already recorded") |
| + } |
| + if _, err := s.readSponsoredEnrollment(enrollmentContext(t), enrollmentActor, next.OperationID); err != nil { |
| + t.Fatal("traversed parent body", err) |
| + } |
| + if _, err := s.readSponsoredEnrollment(enrollmentContext(t), enrollmentActor, first.OperationID); !errors.Is(err, errSponsoredEnrollmentCorrupt) { |
| + t.Fatal("parent self-validation absent", err) |
| + } |
| + sponsoredExec(t, s.db, `DROP TRIGGER sponsored_receipts_no_delete`) |
| + sponsoredExec(t, s.db, `DELETE FROM sponsored_enrollment_receipts WHERE operation_id=?`, first.OperationID) |
| + if _, err := s.readSponsoredEnrollment(enrollmentContext(t), enrollmentActor, next.OperationID); !errors.Is(err, errSponsoredEnrollmentCorrupt) { |
| + t.Fatal("missing parent accepted", err) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentRetirementBetweenPreviewAndEnrollment(t *testing.T) { |
| + for _, kind := range []string{"deleted", "pending billing", "committed billing"} { |
| + t.Run(kind, func(t *testing.T) { |
| + path := filepath.Join(t.TempDir(), "retirement.db") |
| + reader := openSponsoredStoreAt(t, path) |
| + writer := openSponsoredStoreAt(t, path) |
| + if err := reader.SetOperator(enrollmentActor, true); err != nil { |
| + t.Fatal(err) |
| + } |
| + for _, id := range []int64{2, 3} { |
| + sponsoredInsertAccount(t, reader.db, id, "account", 0, 0) |
| + } |
| + ctx := enrollmentContext(t) |
| + before, err := reader.readSponsoredCandidates(ctx) |
| + if err != nil || before.Rows[0].DeletedAt != 0 || before.Rows[0].BillingRetirement { |
| + t.Fatal("preview", err) |
| + } |
| + switch kind { |
| + case "deleted": |
| + _, err = writer.db.ExecContext(ctx, `UPDATE accounts SET deleted_at=1 WHERE id=2`) |
| + case "pending billing": |
| + _, err = writer.db.ExecContext(ctx, `INSERT INTO billing_retirements(account_id,started_at) VALUES(2,1)`) |
| + case "committed billing": |
| + _, err = writer.db.ExecContext(ctx, `INSERT INTO billing_retirements(account_id,started_at,committed_at) VALUES(2,1,2)`) |
| + } |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + v, err := reader.enrollSponsoredCandidates(ctx, enrollmentActor, enrollmentRequest(130, 2, 3), enrollmentNow) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if v.Historical.Rows[0].Outcome != sponsoredExcludedRetired || v.Historical.Rows[1].Outcome != sponsoredAdded { |
| + t.Fatalf("outcomes %#v", v.Historical.Rows) |
| + } |
| + var n int |
| + if err := reader.db.QueryRowContext(ctx, `SELECT count(*) FROM sponsored_cohort_memberships WHERE account_id=2`).Scan(&n); err != nil || n != 0 { |
| + t.Fatal("retired enrolled", n, err) |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentReceiptExactSizeBoundaries(t *testing.T) { |
| + for _, delta := range []int{-1, 0, 1} { |
| + t.Run(fmt.Sprint(delta), func(t *testing.T) { |
| + s := enrollmentStore(t) |
| + sponsoredInsertAccount(t, s.db, 2, "", 0, 0) |
| + req := enrollmentRequest(140, 2) |
| + fact := sponsoredEnrollmentCopyFacts(readSponsoredTestInventory(t, s).Rows[0]) |
| + want := sponsoredEnrollmentReceipt{Version: 1, Actor: enrollmentActor, Request: req, RecordedAt: enrollmentNow.Unix(), Rows: []sponsoredEnrollmentObservation{{AccountID: 2, Sqid: domains.Sqid(2), Outcome: sponsoredAdded, Facts: fact, FirstEnrollmentOperationID: req.OperationID}}} |
| + empty, err := json.Marshal(want) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + name := strings.Repeat("x", sponsoredEnrollmentMaxBody-len(empty)+delta) |
| + sponsoredExec(t, s.db, `UPDATE accounts SET name=? WHERE id=2`, name) |
| + v, err := s.enrollSponsoredCandidates(enrollmentContext(t), enrollmentActor, req, enrollmentNow) |
| + if delta > 0 { |
| + enrollmentZero(t, v, err) |
| + if !errors.Is(err, errSponsoredEnrollmentTooLarge) { |
| + t.Fatal(err) |
| + } |
| + var count int |
| + if err := s.db.QueryRow(`SELECT count(*) FROM sponsored_enrollment_receipts`).Scan(&count); err != nil || count != 0 { |
| + t.Fatal("oversize receipt stored", count, err) |
| + } |
| + return |
| + } |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + var size int |
| + if err := s.db.QueryRow(`SELECT length(body) FROM sponsored_enrollment_receipts WHERE operation_id=?`, req.OperationID).Scan(&size); err != nil || size != sponsoredEnrollmentMaxBody+delta { |
| + t.Fatal(size, err) |
| + } |
| + read, err := s.readSponsoredEnrollment(enrollmentContext(t), enrollmentActor, req.OperationID) |
| + if err != nil || !reflect.DeepEqual(read, v.Historical) { |
| + t.Fatal("boundary read", err) |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestSponsoredEnrollmentFreshMigrationPreservesExistingData(t *testing.T) { |
| + s := enrollmentStore(t) |
| + seedSponsoredCoverage(t, s, false) |
| + if err := s.migrate(); err != nil { |
| + t.Fatal(err) |
| + } |
| + sponsoredExec(t, s.db, `DROP TABLE sponsored_cohort_memberships`) |
| + sponsoredExec(t, s.db, `DROP TABLE sponsored_enrollment_receipts`) |
| + before := enrollmentExistingSnapshot(t, s) |
| + if err := s.migrate(); err != nil { |
| + t.Fatal(err) |
| + } |
| + if !reflect.DeepEqual(before, enrollmentExistingSnapshot(t, s)) { |
| + t.Fatal("migration changed existing data") |
| + } |
| + for _, table := range []string{"sponsored_cohort_memberships", "sponsored_enrollment_receipts"} { |
| + var n int |
| + if err := s.db.QueryRow(`SELECT count(*) FROM ` + table).Scan(&n); err != nil || n != 0 { |
| + t.Fatal("migration enrolled", table, n, err) |
| + } |
| + } |
| +} |