Skip to content

Commit e1810be

Browse files
committed
test: add migration idempotency validation
1 parent 9cf39b7 commit e1810be

File tree

7 files changed

+65
-12
lines changed

7 files changed

+65
-12
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,7 @@ jobs:
102102
--restrict-key=test \
103103
> before.sql
104104
105-
psql "$DATABASE_URL" -c "TRUNCATE TABLE storage.migrations;"
106-
npm run migration:run
105+
npm run migration:test-idempotency
107106
108107
pg_dump "$DATABASE_URL" \
109108
--exclude-table-data=storage.migrations \

migrations/tenant/0040-fix-prefix-race-conditions-optimized.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ BEGIN
240240
END;
241241
$$;
242242

243+
DROP TRIGGER IF EXISTS objects_delete_cleanup ON storage.objects;
244+
DROP TRIGGER IF EXISTS prefixes_delete_cleanup ON storage.prefixes;
245+
DROP TRIGGER IF EXISTS objects_update_cleanup ON storage.objects;
246+
243247
-- Trigger bindings
244248
CREATE TRIGGER objects_delete_cleanup
245249
AFTER DELETE ON storage.objects

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"format": "prettier -c --write src/**",
1111
"lint": "prettier -v && prettier -c src/**",
1212
"migration:run": "tsx src/scripts/migrate-call.ts",
13+
"migration:test-idempotency": "tsx src/scripts/test-migration-idempotency.ts",
1314
"migrations:types": "tsx src/scripts/migrations-types.ts",
1415
"docs:export": "tsx ./src/scripts/export-docs.ts",
1516
"test:dummy-data": "tsx -r dotenv/config ./src/test/db/import-dummy-data.ts",

src/internal/database/migrations/migrate.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ export async function runMigrationsOnTenant({
358358
}
359359

360360
export async function resetMigration(options: {
361-
tenantId: string
361+
tenantId?: string
362362
untilMigration: keyof typeof DBMigration
363363
markCompletedTillMigration?: keyof typeof DBMigration
364364
databaseUrl: string
@@ -447,10 +447,12 @@ export async function resetMigration(options: {
447447
}
448448
}
449449

450-
await updateTenantMigrationsState(options.tenantId, {
451-
migration: latestRunMigration,
452-
state: TenantMigrationStatus.COMPLETED,
453-
})
450+
if (options.tenantId) {
451+
await updateTenantMigrationsState(options.tenantId, {
452+
migration: latestRunMigration,
453+
state: TenantMigrationStatus.COMPLETED,
454+
})
455+
}
454456

455457
await pgClient.query(`COMMIT`)
456458

src/internal/database/migrations/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export const DBMigration = {
2+
'create-migrations-table': 0,
23
initialmigration: 1,
34
'search-files-search-function': 2,
45
'storage-schema': 3,

src/scripts/migrations-types.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,24 @@ function main() {
66
const migrationsPath = path.join(__dirname, '..', '..', 'migrations', 'tenant', '*.sql')
77
const files = glob.sync(migrationsPath).sort()
88

9-
const migrations = files.map((file, index) => {
9+
const migrations = [
10+
// this migration is hardcoded by the postgres migrations library
11+
{
12+
file: 'create-migrations-table',
13+
index: 0,
14+
},
15+
]
16+
17+
files.forEach((file, index) => {
1018
const fileName = file
1119
.split(path.sep)
1220
.pop()
1321
?.replace(/[0-9]+-/, '')
1422
.replace('.sql', '')
15-
16-
return {
17-
file: fileName,
23+
migrations.push({
24+
file: fileName || '',
1825
index: index + 1,
19-
}
26+
})
2027
})
2128

2229
const migrationsEnum = migrations.map((migration) => {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import dotenv from 'dotenv'
2+
dotenv.config()
3+
4+
import { resetMigration, runMigrationsOnTenant } from '@internal/database/migrations'
5+
import { DBMigration } from '@internal/database/migrations/types'
6+
import { getConfig } from '../config'
7+
8+
void (async () => {
9+
const { databaseURL, dbMigrationFreezeAt } = getConfig()
10+
const migrations = Object.keys(DBMigration) as (keyof typeof DBMigration)[]
11+
12+
let previousMigration: keyof typeof DBMigration = 'create-migrations-table'
13+
14+
for (const migration of migrations.slice(1)) {
15+
console.log(`Running migration ${migration}`)
16+
await runMigrationsOnTenant({
17+
databaseUrl: databaseURL,
18+
upToMigration: migration,
19+
})
20+
21+
console.log(`Resetting migration ${migration}`)
22+
await resetMigration({
23+
databaseUrl: databaseURL,
24+
untilMigration: previousMigration,
25+
})
26+
27+
console.log(`Rerunning migration ${migration}`)
28+
await runMigrationsOnTenant({
29+
databaseUrl: databaseURL,
30+
upToMigration: migration,
31+
})
32+
33+
if (dbMigrationFreezeAt === migration) {
34+
break
35+
}
36+
37+
previousMigration = migration
38+
}
39+
})()

0 commit comments

Comments
 (0)