Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion internal/v4/storage/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,6 @@ func validateRestoreDatabase(ctx context.Context, path string) error {
"PAYABLE_AMOUNT_PAISE > REQUESTED_AMOUNT_PAISE",
"PAYABLE_AMOUNT_PAISE % 100 BETWEEN 1 AND 99",
"PAYABLE_AMOUNT_PAISE = REQUESTED_AMOUNT_PAISE + ADJUSTMENT_PAISE",
"ADJUSTMENT_PAISE BETWEEN 1 AND 599",
"CURRENCY = 'INR'",
"JSON_VALID(METADATA_JSON)",
"STATUS IN ('PENDING','PAID','EXPIRED','CANCELLED')",
Expand Down Expand Up @@ -470,6 +469,11 @@ func validateRestoreDatabase(ctx context.Context, path string) error {
return fmt.Errorf("restore table %s is not a strict production table", table)
}
upperSQL := strings.ToUpper(createSQL.String)
if table == "payments" &&
!strings.Contains(upperSQL, "ADJUSTMENT_PAISE BETWEEN 1 AND 199") &&
!strings.Contains(upperSQL, "ADJUSTMENT_PAISE BETWEEN 1 AND 599") {
return errors.New("restore table payments is missing a supported adjustment_paise production check")
}
if requiredChecks[table] && !strings.Contains(upperSQL, "CHECK") {
return fmt.Errorf("restore table %s is missing production checks", table)
}
Expand Down
67 changes: 67 additions & 0 deletions internal/v4/storage/restore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,73 @@ func TestRestoreDrillValidatesIsolatedBackup(t *testing.T) {
}
}

func TestRestoreDrillAcceptsHistoricalPaymentAdjustmentConstraint(t *testing.T) {
ctx := context.Background()
dir := t.TempDir()
livePath := filepath.Join(dir, "paygate.db")
live, err := Open(ctx, livePath)
if err != nil {
t.Fatal(err)
}
backupPath := filepath.Join(dir, "historical-backup.db")
if err := live.BackupTo(ctx, backupPath); err != nil {
live.Close()
t.Fatal(err)
}
if err := live.Close(); err != nil {
t.Fatal(err)
}

raw, err := sql.Open("sqlite", "file:"+filepath.ToSlash(backupPath))
if err != nil {
t.Fatal(err)
}
var createSQL string
if err := raw.QueryRowContext(ctx, `SELECT sql FROM sqlite_schema WHERE type='table' AND name='payments'`).Scan(&createSQL); err != nil {
raw.Close()
t.Fatal(err)
}
oldSQL := strings.Replace(createSQL,
"adjustment_paise INTEGER NOT NULL CHECK(adjustment_paise BETWEEN 1 AND 599)",
"adjustment_paise INTEGER NOT NULL CHECK(adjustment_paise BETWEEN 1 AND 199)", 1)
if oldSQL == createSQL {
raw.Close()
t.Fatal("current payments schema did not contain widened adjustment constraint")
}
if _, err := raw.ExecContext(ctx, "PRAGMA writable_schema=ON"); err != nil {
raw.Close()
t.Fatal(err)
}
if _, err := raw.ExecContext(ctx, `UPDATE sqlite_schema SET sql=? WHERE type='table' AND name='payments'`, oldSQL); err != nil {
raw.Close()
t.Fatal(err)
}
var schemaNumber int
if err := raw.QueryRowContext(ctx, "PRAGMA schema_version").Scan(&schemaNumber); err != nil {
raw.Close()
t.Fatal(err)
}
if _, err := raw.ExecContext(ctx, fmt.Sprintf("PRAGMA schema_version=%d", schemaNumber+1)); err != nil {
raw.Close()
t.Fatal(err)
}
if _, err := raw.ExecContext(ctx, "PRAGMA writable_schema=OFF"); err != nil {
raw.Close()
t.Fatal(err)
}
if err := raw.Close(); err != nil {
t.Fatal(err)
}

report, err := RestoreDrill(ctx, backupPath, livePath, "")
if err != nil {
t.Fatalf("restore historical adjustment backup: %v", err)
}
if report.SchemaVersion != schemaVersion {
t.Fatalf("restore schema version=%d want=%d", report.SchemaVersion, schemaVersion)
}
}

func TestRestoreDrillRejectsLivePathAndWrongHash(t *testing.T) {
ctx := context.Background()
dir := t.TempDir()
Expand Down