@prairielearn/migrations 3.0.16 → 3.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @prairielearn/migrations
2
2
 
3
+ ## 3.0.17
4
+
5
+ ### Patch Changes
6
+
7
+ - be4444e: Upgrade all JavaScript dependencies
8
+ - Updated dependencies [be4444e]
9
+ - @prairielearn/named-locks@3.0.17
10
+ - @prairielearn/postgres@2.1.13
11
+ - @prairielearn/logger@2.0.15
12
+ - @prairielearn/error@2.0.15
13
+
3
14
  ## 3.0.16
4
15
 
5
16
  ### Patch Changes
@@ -1,4 +1,4 @@
1
- import { assert } from 'chai';
1
+ import { afterAll, assert, beforeAll, beforeEach, describe, it } from 'vitest';
2
2
  import * as error from '@prairielearn/error';
3
3
  import * as namedLocks from '@prairielearn/named-locks';
4
4
  import { makePostgresTestUtils, queryAsync, queryRow, queryRows } from '@prairielearn/postgres';
@@ -66,7 +66,7 @@ async function insertTestBatchedMigration() {
66
66
  return migration;
67
67
  }
68
68
  describe('BatchedMigrationExecutor', () => {
69
- before(async () => {
69
+ beforeAll(async () => {
70
70
  await postgresTestUtils.createDatabase();
71
71
  await namedLocks.init(postgresTestUtils.getPoolConfig(), (err) => {
72
72
  throw err;
@@ -76,7 +76,7 @@ describe('BatchedMigrationExecutor', () => {
76
76
  beforeEach(async () => {
77
77
  await postgresTestUtils.resetDatabase();
78
78
  });
79
- after(async () => {
79
+ afterAll(async () => {
80
80
  await namedLocks.close();
81
81
  await postgresTestUtils.dropDatabase();
82
82
  });
@@ -1 +1 @@
1
- {"version":3,"file":"batched-migration-runner.test.js","sourceRoot":"","sources":["../../src/batched-migrations/batched-migration-runner.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert } from 'chai';\n\nimport * as error from '@prairielearn/error';\nimport * as namedLocks from '@prairielearn/named-locks';\nimport { makePostgresTestUtils, queryAsync, queryRow, queryRows } from '@prairielearn/postgres';\n\nimport { SCHEMA_MIGRATIONS_PATH, init } from '../index.js';\n\nimport { BatchedMigrationJobRowSchema } from './batched-migration-job.js';\nimport { BatchedMigrationRunner } from './batched-migration-runner.js';\nimport {\n BatchedMigrationRowSchema,\n insertBatchedMigration,\n makeBatchedMigration,\n updateBatchedMigrationStatus,\n} from './batched-migration.js';\n\nconst postgresTestUtils = makePostgresTestUtils({\n database: 'prairielearn_migrations',\n});\n\nfunction makeTestBatchMigration() {\n let executionCount = 0;\n let failingIds: bigint[] = [];\n\n return makeBatchedMigration({\n async getParameters() {\n return {\n min: 1n,\n max: 10000n,\n batchSize: 1000,\n };\n },\n async execute(start: bigint, end: bigint) {\n executionCount += 1;\n const shouldFail = failingIds.some((id) => id >= start && id <= end);\n if (shouldFail) {\n // Throw an error with some data to make sure it gets persisted. We\n // specifically use BigInt values here to make sure that they are\n // correctly serialized to strings.\n throw new error.AugmentedError('Execution failure', { data: { start, end } });\n }\n },\n setFailingIds(ids: bigint[]) {\n failingIds = ids;\n },\n get executionCount() {\n return executionCount;\n },\n });\n}\n\nasync function getBatchedMigration(migrationId: string) {\n return await queryRow(\n 'SELECT * FROM batched_migrations WHERE id = $id;',\n { id: migrationId },\n BatchedMigrationRowSchema,\n );\n}\n\nasync function getBatchedMigrationJobs(migrationId: string) {\n return await queryRows(\n 'SELECT * FROM batched_migration_jobs WHERE batched_migration_id = $batched_migration_id ORDER BY id ASC;',\n { batched_migration_id: migrationId },\n BatchedMigrationJobRowSchema,\n );\n}\n\nasync function resetFailedBatchedMigrationJobs(migrationId: string) {\n await queryAsync(\n \"UPDATE batched_migration_jobs SET status = 'pending', updated_at = CURRENT_TIMESTAMP WHERE batched_migration_id = $batched_migration_id AND status = 'failed'\",\n {\n batched_migration_id: migrationId,\n },\n );\n}\n\nasync function insertTestBatchedMigration() {\n const migrationImplementation = makeTestBatchMigration();\n const parameters = await migrationImplementation.getParameters();\n const migration = await insertBatchedMigration({\n project: 'test',\n filename: '20230406184103_test_batch_migration.js',\n timestamp: '20230406184103',\n batch_size: parameters.batchSize,\n min_value: parameters.min,\n max_value: parameters.max,\n status: 'running',\n });\n if (!migration) throw new Error('Failed to insert batched migration');\n return migration;\n}\n\ndescribe('BatchedMigrationExecutor', () => {\n before(async () => {\n await postgresTestUtils.createDatabase();\n await namedLocks.init(postgresTestUtils.getPoolConfig(), (err) => {\n throw err;\n });\n await init([SCHEMA_MIGRATIONS_PATH], 'prairielearn_migrations');\n });\n\n beforeEach(async () => {\n await postgresTestUtils.resetDatabase();\n });\n\n after(async () => {\n await namedLocks.close();\n await postgresTestUtils.dropDatabase();\n });\n\n it('runs one iteration of a batched migration', async () => {\n const migration = await insertTestBatchedMigration();\n\n const migrationImplementation = makeTestBatchMigration();\n const executor = new BatchedMigrationRunner(migration, migrationImplementation);\n await executor.run({ iterations: 1 });\n\n const jobs = await getBatchedMigrationJobs(migration.id);\n assert.lengthOf(jobs, 1);\n\n const finalMigration = await getBatchedMigration(migration.id);\n assert.equal(finalMigration.status, 'running');\n\n assert.equal(migrationImplementation.executionCount, 1);\n });\n\n it('runs an entire batched migration', async () => {\n const migration = await insertTestBatchedMigration();\n\n const migrationImplementation = makeTestBatchMigration();\n const runner = new BatchedMigrationRunner(migration, migrationImplementation);\n await runner.run();\n\n const jobs = await getBatchedMigrationJobs(migration.id);\n assert.lengthOf(jobs, 10);\n assert.equal(jobs[0].min_value, 1n);\n assert.equal(jobs[0].max_value, 1000n);\n assert.equal(jobs.at(-1)?.min_value, 9001n);\n assert.equal(jobs.at(-1)?.max_value, 10000n);\n assert.isTrue(jobs.every((job) => job.started_at !== null));\n assert.isTrue(jobs.every((job) => job.finished_at !== null));\n assert.isTrue(jobs.every((job) => job.status === 'succeeded'));\n assert.isTrue(jobs.every((job) => job.attempts === 1));\n\n const finalMigration = await getBatchedMigration(migration.id);\n assert.equal(finalMigration.status, 'succeeded');\n });\n\n it('handles failing execution', async () => {\n let migration = await insertTestBatchedMigration();\n\n const migrationImplementation = makeTestBatchMigration();\n migrationImplementation.setFailingIds([1n, 5010n]);\n const runner = new BatchedMigrationRunner(migration, migrationImplementation);\n await runner.run();\n\n const jobs = await getBatchedMigrationJobs(migration.id);\n const failedJobs = jobs.filter((job) => job.status === 'failed');\n const successfulJobs = jobs.filter((job) => job.status === 'succeeded');\n assert.lengthOf(jobs, 10);\n assert.lengthOf(failedJobs, 2);\n assert.lengthOf(successfulJobs, 8);\n assert.equal(migrationImplementation.executionCount, 10);\n assert.isTrue(jobs.every((job) => job.attempts === 1));\n failedJobs.forEach((job) => {\n const jobData = job.data as any;\n assert.isObject(jobData);\n assert.isObject(jobData.error);\n assert.hasAllKeys(jobData.error, ['name', 'message', 'stack', 'data', 'status']);\n assert.equal(jobData.error.name, 'Error');\n assert.equal(jobData.error.message, 'Execution failure');\n assert.equal(jobData.error.data.start, job.min_value.toString());\n assert.equal(jobData.error.data.end, job.max_value.toString());\n });\n\n const failedMigration = await getBatchedMigration(migration.id);\n assert.equal(failedMigration.status, 'failed');\n\n // Retry the failed jobs; ensure they succeed this time.\n await resetFailedBatchedMigrationJobs(migration.id);\n migration = await updateBatchedMigrationStatus(migration.id, 'running');\n\n migrationImplementation.setFailingIds([]);\n const retryRunner = new BatchedMigrationRunner(migration, migrationImplementation);\n await retryRunner.run();\n\n const finalJobs = await getBatchedMigrationJobs(migration.id);\n const finalFailedJobs = finalJobs.filter((job) => job.status === 'failed');\n const finalSuccessfulJobs = finalJobs.filter((job) => job.status === 'succeeded');\n const retriedJobs = finalJobs.filter((job) => job.attempts === 2);\n assert.lengthOf(finalJobs, 10);\n assert.lengthOf(finalFailedJobs, 0);\n assert.lengthOf(finalSuccessfulJobs, 10);\n assert.lengthOf(retriedJobs, 2);\n assert.isTrue(finalJobs.every((job) => job.data === null));\n\n migration = await getBatchedMigration(migration.id);\n assert.equal(migration.status, 'succeeded');\n\n // The runner should have run only the previously failed jobs, which\n // works out to 2 additional execution.\n assert.equal(migrationImplementation.executionCount, 12);\n });\n});\n"]}
1
+ {"version":3,"file":"batched-migration-runner.test.js","sourceRoot":"","sources":["../../src/batched-migrations/batched-migration-runner.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { afterAll, assert, beforeAll, beforeEach, describe, it } from 'vitest';\n\nimport * as error from '@prairielearn/error';\nimport * as namedLocks from '@prairielearn/named-locks';\nimport { makePostgresTestUtils, queryAsync, queryRow, queryRows } from '@prairielearn/postgres';\n\nimport { SCHEMA_MIGRATIONS_PATH, init } from '../index.js';\n\nimport { BatchedMigrationJobRowSchema } from './batched-migration-job.js';\nimport { BatchedMigrationRunner } from './batched-migration-runner.js';\nimport {\n BatchedMigrationRowSchema,\n insertBatchedMigration,\n makeBatchedMigration,\n updateBatchedMigrationStatus,\n} from './batched-migration.js';\n\nconst postgresTestUtils = makePostgresTestUtils({\n database: 'prairielearn_migrations',\n});\n\nfunction makeTestBatchMigration() {\n let executionCount = 0;\n let failingIds: bigint[] = [];\n\n return makeBatchedMigration({\n async getParameters() {\n return {\n min: 1n,\n max: 10000n,\n batchSize: 1000,\n };\n },\n async execute(start: bigint, end: bigint) {\n executionCount += 1;\n const shouldFail = failingIds.some((id) => id >= start && id <= end);\n if (shouldFail) {\n // Throw an error with some data to make sure it gets persisted. We\n // specifically use BigInt values here to make sure that they are\n // correctly serialized to strings.\n throw new error.AugmentedError('Execution failure', { data: { start, end } });\n }\n },\n setFailingIds(ids: bigint[]) {\n failingIds = ids;\n },\n get executionCount() {\n return executionCount;\n },\n });\n}\n\nasync function getBatchedMigration(migrationId: string) {\n return await queryRow(\n 'SELECT * FROM batched_migrations WHERE id = $id;',\n { id: migrationId },\n BatchedMigrationRowSchema,\n );\n}\n\nasync function getBatchedMigrationJobs(migrationId: string) {\n return await queryRows(\n 'SELECT * FROM batched_migration_jobs WHERE batched_migration_id = $batched_migration_id ORDER BY id ASC;',\n { batched_migration_id: migrationId },\n BatchedMigrationJobRowSchema,\n );\n}\n\nasync function resetFailedBatchedMigrationJobs(migrationId: string) {\n await queryAsync(\n \"UPDATE batched_migration_jobs SET status = 'pending', updated_at = CURRENT_TIMESTAMP WHERE batched_migration_id = $batched_migration_id AND status = 'failed'\",\n {\n batched_migration_id: migrationId,\n },\n );\n}\n\nasync function insertTestBatchedMigration() {\n const migrationImplementation = makeTestBatchMigration();\n const parameters = await migrationImplementation.getParameters();\n const migration = await insertBatchedMigration({\n project: 'test',\n filename: '20230406184103_test_batch_migration.js',\n timestamp: '20230406184103',\n batch_size: parameters.batchSize,\n min_value: parameters.min,\n max_value: parameters.max,\n status: 'running',\n });\n if (!migration) throw new Error('Failed to insert batched migration');\n return migration;\n}\n\ndescribe('BatchedMigrationExecutor', () => {\n beforeAll(async () => {\n await postgresTestUtils.createDatabase();\n await namedLocks.init(postgresTestUtils.getPoolConfig(), (err) => {\n throw err;\n });\n await init([SCHEMA_MIGRATIONS_PATH], 'prairielearn_migrations');\n });\n\n beforeEach(async () => {\n await postgresTestUtils.resetDatabase();\n });\n\n afterAll(async () => {\n await namedLocks.close();\n await postgresTestUtils.dropDatabase();\n });\n\n it('runs one iteration of a batched migration', async () => {\n const migration = await insertTestBatchedMigration();\n\n const migrationImplementation = makeTestBatchMigration();\n const executor = new BatchedMigrationRunner(migration, migrationImplementation);\n await executor.run({ iterations: 1 });\n\n const jobs = await getBatchedMigrationJobs(migration.id);\n assert.lengthOf(jobs, 1);\n\n const finalMigration = await getBatchedMigration(migration.id);\n assert.equal(finalMigration.status, 'running');\n\n assert.equal(migrationImplementation.executionCount, 1);\n });\n\n it('runs an entire batched migration', async () => {\n const migration = await insertTestBatchedMigration();\n\n const migrationImplementation = makeTestBatchMigration();\n const runner = new BatchedMigrationRunner(migration, migrationImplementation);\n await runner.run();\n\n const jobs = await getBatchedMigrationJobs(migration.id);\n assert.lengthOf(jobs, 10);\n assert.equal(jobs[0].min_value, 1n);\n assert.equal(jobs[0].max_value, 1000n);\n assert.equal(jobs.at(-1)?.min_value, 9001n);\n assert.equal(jobs.at(-1)?.max_value, 10000n);\n assert.isTrue(jobs.every((job) => job.started_at !== null));\n assert.isTrue(jobs.every((job) => job.finished_at !== null));\n assert.isTrue(jobs.every((job) => job.status === 'succeeded'));\n assert.isTrue(jobs.every((job) => job.attempts === 1));\n\n const finalMigration = await getBatchedMigration(migration.id);\n assert.equal(finalMigration.status, 'succeeded');\n });\n\n it('handles failing execution', async () => {\n let migration = await insertTestBatchedMigration();\n\n const migrationImplementation = makeTestBatchMigration();\n migrationImplementation.setFailingIds([1n, 5010n]);\n const runner = new BatchedMigrationRunner(migration, migrationImplementation);\n await runner.run();\n\n const jobs = await getBatchedMigrationJobs(migration.id);\n const failedJobs = jobs.filter((job) => job.status === 'failed');\n const successfulJobs = jobs.filter((job) => job.status === 'succeeded');\n assert.lengthOf(jobs, 10);\n assert.lengthOf(failedJobs, 2);\n assert.lengthOf(successfulJobs, 8);\n assert.equal(migrationImplementation.executionCount, 10);\n assert.isTrue(jobs.every((job) => job.attempts === 1));\n failedJobs.forEach((job) => {\n const jobData = job.data as any;\n assert.isObject(jobData);\n assert.isObject(jobData.error);\n assert.hasAllKeys(jobData.error, ['name', 'message', 'stack', 'data', 'status']);\n assert.equal(jobData.error.name, 'Error');\n assert.equal(jobData.error.message, 'Execution failure');\n assert.equal(jobData.error.data.start, job.min_value.toString());\n assert.equal(jobData.error.data.end, job.max_value.toString());\n });\n\n const failedMigration = await getBatchedMigration(migration.id);\n assert.equal(failedMigration.status, 'failed');\n\n // Retry the failed jobs; ensure they succeed this time.\n await resetFailedBatchedMigrationJobs(migration.id);\n migration = await updateBatchedMigrationStatus(migration.id, 'running');\n\n migrationImplementation.setFailingIds([]);\n const retryRunner = new BatchedMigrationRunner(migration, migrationImplementation);\n await retryRunner.run();\n\n const finalJobs = await getBatchedMigrationJobs(migration.id);\n const finalFailedJobs = finalJobs.filter((job) => job.status === 'failed');\n const finalSuccessfulJobs = finalJobs.filter((job) => job.status === 'succeeded');\n const retriedJobs = finalJobs.filter((job) => job.attempts === 2);\n assert.lengthOf(finalJobs, 10);\n assert.lengthOf(finalFailedJobs, 0);\n assert.lengthOf(finalSuccessfulJobs, 10);\n assert.lengthOf(retriedJobs, 2);\n assert.isTrue(finalJobs.every((job) => job.data === null));\n\n migration = await getBatchedMigration(migration.id);\n assert.equal(migration.status, 'succeeded');\n\n // The runner should have run only the previously failed jobs, which\n // works out to 2 additional execution.\n assert.equal(migrationImplementation.executionCount, 12);\n });\n});\n"]}
@@ -1,17 +1,15 @@
1
1
  import path from 'node:path';
2
- import { assert, use as chaiUse } from 'chai';
3
- import chaiAsPromised from 'chai-as-promised';
2
+ import { afterAll, afterEach, assert, beforeAll, describe, expect, it } from 'vitest';
4
3
  import * as namedLocks from '@prairielearn/named-locks';
5
4
  import { makePostgresTestUtils } from '@prairielearn/postgres';
6
5
  import { SCHEMA_MIGRATIONS_PATH, init } from '../index.js';
7
6
  import { selectAllBatchedMigrations } from './batched-migration.js';
8
7
  import { BatchedMigrationsRunner } from './batched-migrations-runner.js';
9
- chaiUse(chaiAsPromised);
10
8
  const postgresTestUtils = makePostgresTestUtils({
11
9
  database: 'prairielearn_migrations',
12
10
  });
13
11
  describe('BatchedMigrationsRunner', () => {
14
- before(async () => {
12
+ beforeAll(async () => {
15
13
  await postgresTestUtils.createDatabase();
16
14
  await namedLocks.init(postgresTestUtils.getPoolConfig(), (err) => {
17
15
  throw err;
@@ -21,7 +19,7 @@ describe('BatchedMigrationsRunner', () => {
21
19
  afterEach(async () => {
22
20
  await postgresTestUtils.resetDatabase();
23
21
  });
24
- after(async () => {
22
+ afterAll(async () => {
25
23
  await namedLocks.close();
26
24
  await postgresTestUtils.dropDatabase();
27
25
  });
@@ -76,9 +74,9 @@ describe('BatchedMigrationsRunner', () => {
76
74
  directories: [path.join(import.meta.dirname, 'fixtures')],
77
75
  });
78
76
  await runner.enqueueBatchedMigration('20230406184107_failing_migration');
79
- await assert.isRejected(runner.finalizeBatchedMigration('20230406184107_failing_migration', {
77
+ await expect(runner.finalizeBatchedMigration('20230406184107_failing_migration', {
80
78
  logProgress: false,
81
- }), "but it is 'failed'");
79
+ })).rejects.toThrow("but it is 'failed'");
82
80
  const migrations = await selectAllBatchedMigrations('test');
83
81
  assert.lengthOf(migrations, 1);
84
82
  assert.equal(migrations[0].timestamp, '20230406184107');
@@ -1 +1 @@
1
- {"version":3,"file":"batched-migrations-runner.test.js","sourceRoot":"","sources":["../../src/batched-migrations/batched-migrations-runner.test.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\n\nimport { assert, use as chaiUse } from 'chai';\nimport chaiAsPromised from 'chai-as-promised';\n\nimport * as namedLocks from '@prairielearn/named-locks';\nimport { makePostgresTestUtils } from '@prairielearn/postgres';\n\nimport { SCHEMA_MIGRATIONS_PATH, init } from '../index.js';\n\nimport { selectAllBatchedMigrations } from './batched-migration.js';\nimport { BatchedMigrationsRunner } from './batched-migrations-runner.js';\n\nchaiUse(chaiAsPromised);\n\nconst postgresTestUtils = makePostgresTestUtils({\n database: 'prairielearn_migrations',\n});\n\ndescribe('BatchedMigrationsRunner', () => {\n before(async () => {\n await postgresTestUtils.createDatabase();\n await namedLocks.init(postgresTestUtils.getPoolConfig(), (err) => {\n throw err;\n });\n await init([SCHEMA_MIGRATIONS_PATH], 'prairielearn_migrations');\n });\n\n afterEach(async () => {\n await postgresTestUtils.resetDatabase();\n });\n\n after(async () => {\n await namedLocks.close();\n await postgresTestUtils.dropDatabase();\n });\n\n it('enqueues migrations', async () => {\n const runner = new BatchedMigrationsRunner({\n project: 'test',\n directories: [path.join(import.meta.dirname, 'fixtures')],\n });\n\n await runner.enqueueBatchedMigration('20230406184103_successful_migration');\n await runner.enqueueBatchedMigration('20230406184107_failing_migration');\n await runner.enqueueBatchedMigration('20230407230446_no_rows_migration');\n\n const migrations = await selectAllBatchedMigrations('test');\n\n assert.lengthOf(migrations, 3);\n assert.equal(migrations[0].timestamp, '20230406184103');\n assert.equal(migrations[0].filename, '20230406184103_successful_migration.ts');\n assert.equal(migrations[0].status, 'pending');\n assert.equal(migrations[1].timestamp, '20230406184107');\n assert.equal(migrations[1].filename, '20230406184107_failing_migration.ts');\n assert.equal(migrations[1].status, 'pending');\n assert.equal(migrations[2].timestamp, '20230407230446');\n assert.equal(migrations[2].filename, '20230407230446_no_rows_migration.ts');\n assert.equal(migrations[2].status, 'succeeded');\n });\n\n it('safely enqueues migrations multiple times', async () => {\n const runner = new BatchedMigrationsRunner({\n project: 'test',\n directories: [path.join(import.meta.dirname, 'fixtures')],\n });\n\n await runner.enqueueBatchedMigration('20230406184103_successful_migration');\n await runner.enqueueBatchedMigration('20230406184103_successful_migration');\n await runner.enqueueBatchedMigration('20230406184103_successful_migration');\n\n const migrations = await selectAllBatchedMigrations('test');\n\n assert.lengthOf(migrations, 1);\n });\n\n it('finalizes a successful migration', async () => {\n const runner = new BatchedMigrationsRunner({\n project: 'test',\n directories: [path.join(import.meta.dirname, 'fixtures')],\n });\n\n await runner.enqueueBatchedMigration('20230406184103_successful_migration');\n await runner.finalizeBatchedMigration('20230406184103_successful_migration', {\n logProgress: false,\n });\n\n const migrations = await selectAllBatchedMigrations('test');\n assert.lengthOf(migrations, 1);\n assert.equal(migrations[0].timestamp, '20230406184103');\n assert.equal(migrations[0].status, 'succeeded');\n });\n\n it('finalizes a failing migration', async () => {\n const runner = new BatchedMigrationsRunner({\n project: 'test',\n directories: [path.join(import.meta.dirname, 'fixtures')],\n });\n\n await runner.enqueueBatchedMigration('20230406184107_failing_migration');\n\n await assert.isRejected(\n runner.finalizeBatchedMigration('20230406184107_failing_migration', {\n logProgress: false,\n }),\n \"but it is 'failed'\",\n );\n\n const migrations = await selectAllBatchedMigrations('test');\n assert.lengthOf(migrations, 1);\n assert.equal(migrations[0].timestamp, '20230406184107');\n assert.equal(migrations[0].status, 'failed');\n });\n});\n"]}
1
+ {"version":3,"file":"batched-migrations-runner.test.js","sourceRoot":"","sources":["../../src/batched-migrations/batched-migrations-runner.test.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\n\nimport { afterAll, afterEach, assert, beforeAll, describe, expect, it } from 'vitest';\n\nimport * as namedLocks from '@prairielearn/named-locks';\nimport { makePostgresTestUtils } from '@prairielearn/postgres';\n\nimport { SCHEMA_MIGRATIONS_PATH, init } from '../index.js';\n\nimport { selectAllBatchedMigrations } from './batched-migration.js';\nimport { BatchedMigrationsRunner } from './batched-migrations-runner.js';\n\nconst postgresTestUtils = makePostgresTestUtils({\n database: 'prairielearn_migrations',\n});\n\ndescribe('BatchedMigrationsRunner', () => {\n beforeAll(async () => {\n await postgresTestUtils.createDatabase();\n await namedLocks.init(postgresTestUtils.getPoolConfig(), (err) => {\n throw err;\n });\n await init([SCHEMA_MIGRATIONS_PATH], 'prairielearn_migrations');\n });\n\n afterEach(async () => {\n await postgresTestUtils.resetDatabase();\n });\n\n afterAll(async () => {\n await namedLocks.close();\n await postgresTestUtils.dropDatabase();\n });\n\n it('enqueues migrations', async () => {\n const runner = new BatchedMigrationsRunner({\n project: 'test',\n directories: [path.join(import.meta.dirname, 'fixtures')],\n });\n\n await runner.enqueueBatchedMigration('20230406184103_successful_migration');\n await runner.enqueueBatchedMigration('20230406184107_failing_migration');\n await runner.enqueueBatchedMigration('20230407230446_no_rows_migration');\n\n const migrations = await selectAllBatchedMigrations('test');\n\n assert.lengthOf(migrations, 3);\n assert.equal(migrations[0].timestamp, '20230406184103');\n assert.equal(migrations[0].filename, '20230406184103_successful_migration.ts');\n assert.equal(migrations[0].status, 'pending');\n assert.equal(migrations[1].timestamp, '20230406184107');\n assert.equal(migrations[1].filename, '20230406184107_failing_migration.ts');\n assert.equal(migrations[1].status, 'pending');\n assert.equal(migrations[2].timestamp, '20230407230446');\n assert.equal(migrations[2].filename, '20230407230446_no_rows_migration.ts');\n assert.equal(migrations[2].status, 'succeeded');\n });\n\n it('safely enqueues migrations multiple times', async () => {\n const runner = new BatchedMigrationsRunner({\n project: 'test',\n directories: [path.join(import.meta.dirname, 'fixtures')],\n });\n\n await runner.enqueueBatchedMigration('20230406184103_successful_migration');\n await runner.enqueueBatchedMigration('20230406184103_successful_migration');\n await runner.enqueueBatchedMigration('20230406184103_successful_migration');\n\n const migrations = await selectAllBatchedMigrations('test');\n\n assert.lengthOf(migrations, 1);\n });\n\n it('finalizes a successful migration', async () => {\n const runner = new BatchedMigrationsRunner({\n project: 'test',\n directories: [path.join(import.meta.dirname, 'fixtures')],\n });\n\n await runner.enqueueBatchedMigration('20230406184103_successful_migration');\n await runner.finalizeBatchedMigration('20230406184103_successful_migration', {\n logProgress: false,\n });\n\n const migrations = await selectAllBatchedMigrations('test');\n assert.lengthOf(migrations, 1);\n assert.equal(migrations[0].timestamp, '20230406184103');\n assert.equal(migrations[0].status, 'succeeded');\n });\n\n it('finalizes a failing migration', async () => {\n const runner = new BatchedMigrationsRunner({\n project: 'test',\n directories: [path.join(import.meta.dirname, 'fixtures')],\n });\n\n await runner.enqueueBatchedMigration('20230406184107_failing_migration');\n\n await expect(\n runner.finalizeBatchedMigration('20230406184107_failing_migration', {\n logProgress: false,\n }),\n ).rejects.toThrow(\"but it is 'failed'\");\n const migrations = await selectAllBatchedMigrations('test');\n assert.lengthOf(migrations, 1);\n assert.equal(migrations[0].timestamp, '20230406184107');\n assert.equal(migrations[0].status, 'failed');\n });\n});\n"]}
@@ -1,10 +1,8 @@
1
1
  import path from 'path';
2
- import { assert, use as chaiUse } from 'chai';
3
- import chaiAsPromised from 'chai-as-promised';
4
2
  import fs from 'fs-extra';
5
3
  import tmp from 'tmp-promise';
4
+ import { assert, describe, expect, it } from 'vitest';
6
5
  import { parseAnnotations, readAndValidateMigrationsFromDirectory, sortMigrationFiles, } from './load-migrations.js';
7
- chaiUse(chaiAsPromised);
8
6
  async function withMigrationFiles(files, fn) {
9
7
  await tmp.withDir(async function (tmpDir) {
10
8
  for (const file of files) {
@@ -17,12 +15,14 @@ describe('load-migrations', () => {
17
15
  describe('readAndValidateMigrationsFromDirectory', () => {
18
16
  it('handles migrations without a timestamp', async () => {
19
17
  await withMigrationFiles(['001_testing.sql'], async (tmpDir) => {
20
- await assert.isRejected(readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']), 'Invalid migration filename: 001_testing.sql');
18
+ const promise = readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']);
19
+ await expect(promise).rejects.toThrow('Invalid migration filename: 001_testing.sql');
21
20
  });
22
21
  });
23
22
  it('handles duplicate timestamps', async () => {
24
23
  await withMigrationFiles(['20220101010101_testing.sql', '20220101010101_testing_again.sql'], async (tmpDir) => {
25
- await assert.isRejected(readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']), 'Duplicate migration timestamp');
24
+ const promise = readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']);
25
+ await expect(promise).rejects.toThrow('Duplicate migration timestamp');
26
26
  });
27
27
  });
28
28
  });
@@ -1 +1 @@
1
- {"version":3,"file":"load-migrations.test.js","sourceRoot":"","sources":["../src/load-migrations.test.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\n\nimport { assert, use as chaiUse } from 'chai';\nimport chaiAsPromised from 'chai-as-promised';\nimport fs from 'fs-extra';\nimport tmp from 'tmp-promise';\n\nimport {\n parseAnnotations,\n readAndValidateMigrationsFromDirectory,\n sortMigrationFiles,\n} from './load-migrations.js';\n\nchaiUse(chaiAsPromised);\n\nasync function withMigrationFiles(files: string[], fn: (tmpDir: string) => Promise<void>) {\n await tmp.withDir(\n async function (tmpDir) {\n for (const file of files) {\n await fs.writeFile(path.join(tmpDir.path, file), '');\n }\n await fn(tmpDir.path);\n },\n { unsafeCleanup: true },\n );\n}\n\ndescribe('load-migrations', () => {\n describe('readAndValidateMigrationsFromDirectory', () => {\n it('handles migrations without a timestamp', async () => {\n await withMigrationFiles(['001_testing.sql'], async (tmpDir) => {\n await assert.isRejected(\n readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']),\n 'Invalid migration filename: 001_testing.sql',\n );\n });\n });\n\n it('handles duplicate timestamps', async () => {\n await withMigrationFiles(\n ['20220101010101_testing.sql', '20220101010101_testing_again.sql'],\n async (tmpDir) => {\n await assert.isRejected(\n readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']),\n 'Duplicate migration timestamp',\n );\n },\n );\n });\n });\n\n describe('sortMigrationFiles', () => {\n it('sorts by timestamp', () => {\n assert.deepEqual(\n sortMigrationFiles([\n {\n directory: 'migrations',\n filename: '20220101010103_testing_3.sql',\n timestamp: '20220101010103',\n },\n {\n directory: 'migrations',\n filename: '20220101010101_testing_1.sql',\n timestamp: '20220101010101',\n },\n {\n directory: 'migrations',\n filename: '20220101010102_testing_2.sql',\n timestamp: '20220101010102',\n },\n ]),\n [\n {\n directory: 'migrations',\n filename: '20220101010101_testing_1.sql',\n timestamp: '20220101010101',\n },\n {\n directory: 'migrations',\n filename: '20220101010102_testing_2.sql',\n timestamp: '20220101010102',\n },\n {\n directory: 'migrations',\n filename: '20220101010103_testing_3.sql',\n timestamp: '20220101010103',\n },\n ],\n );\n });\n });\n\n describe('parseAnnotations', () => {\n it('parses a NO TRANSACTION annotation', () => {\n const annotations = parseAnnotations('-- prairielearn:migrations NO TRANSACTION');\n assert.deepEqual(annotations, new Set(['NO TRANSACTION']));\n });\n\n it('throws an error for an invalid annotation', () => {\n assert.throws(() => {\n parseAnnotations('-- prairielearn:migrations INVALID');\n });\n });\n });\n});\n"]}
1
+ {"version":3,"file":"load-migrations.test.js","sourceRoot":"","sources":["../src/load-migrations.test.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\n\nimport fs from 'fs-extra';\nimport tmp from 'tmp-promise';\nimport { assert, describe, expect, it } from 'vitest';\n\nimport {\n parseAnnotations,\n readAndValidateMigrationsFromDirectory,\n sortMigrationFiles,\n} from './load-migrations.js';\n\nasync function withMigrationFiles(files: string[], fn: (tmpDir: string) => Promise<void>) {\n await tmp.withDir(\n async function (tmpDir) {\n for (const file of files) {\n await fs.writeFile(path.join(tmpDir.path, file), '');\n }\n await fn(tmpDir.path);\n },\n { unsafeCleanup: true },\n );\n}\n\ndescribe('load-migrations', () => {\n describe('readAndValidateMigrationsFromDirectory', () => {\n it('handles migrations without a timestamp', async () => {\n await withMigrationFiles(['001_testing.sql'], async (tmpDir) => {\n const promise = readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']);\n await expect(promise).rejects.toThrow('Invalid migration filename: 001_testing.sql');\n });\n });\n\n it('handles duplicate timestamps', async () => {\n await withMigrationFiles(\n ['20220101010101_testing.sql', '20220101010101_testing_again.sql'],\n async (tmpDir) => {\n const promise = readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']);\n await expect(promise).rejects.toThrow('Duplicate migration timestamp');\n },\n );\n });\n });\n\n describe('sortMigrationFiles', () => {\n it('sorts by timestamp', () => {\n assert.deepEqual(\n sortMigrationFiles([\n {\n directory: 'migrations',\n filename: '20220101010103_testing_3.sql',\n timestamp: '20220101010103',\n },\n {\n directory: 'migrations',\n filename: '20220101010101_testing_1.sql',\n timestamp: '20220101010101',\n },\n {\n directory: 'migrations',\n filename: '20220101010102_testing_2.sql',\n timestamp: '20220101010102',\n },\n ]),\n [\n {\n directory: 'migrations',\n filename: '20220101010101_testing_1.sql',\n timestamp: '20220101010101',\n },\n {\n directory: 'migrations',\n filename: '20220101010102_testing_2.sql',\n timestamp: '20220101010102',\n },\n {\n directory: 'migrations',\n filename: '20220101010103_testing_3.sql',\n timestamp: '20220101010103',\n },\n ],\n );\n });\n });\n\n describe('parseAnnotations', () => {\n it('parses a NO TRANSACTION annotation', () => {\n const annotations = parseAnnotations('-- prairielearn:migrations NO TRANSACTION');\n assert.deepEqual(annotations, new Set(['NO TRANSACTION']));\n });\n\n it('throws an error for an invalid annotation', () => {\n assert.throws(() => {\n parseAnnotations('-- prairielearn:migrations INVALID');\n });\n });\n });\n});\n"]}
@@ -1,5 +1,5 @@
1
1
  import path from 'node:path';
2
- import { assert } from 'chai';
2
+ import { afterAll, assert, beforeAll, describe, it } from 'vitest';
3
3
  import { makePostgresTestUtils, queryAsync } from '@prairielearn/postgres';
4
4
  import { getMigrationsToExecute, initWithLock } from './migrations.js';
5
5
  describe('migrations', () => {
@@ -53,10 +53,10 @@ describe('migrations', () => {
53
53
  const postgresTestUtils = makePostgresTestUtils({
54
54
  database: 'prairielearn_migrations',
55
55
  });
56
- before(async () => {
56
+ beforeAll(async () => {
57
57
  await postgresTestUtils.createDatabase();
58
58
  });
59
- after(async () => {
59
+ afterAll(async () => {
60
60
  await postgresTestUtils.dropDatabase();
61
61
  });
62
62
  it('runs both SQL and JavaScript migrations', async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.test.js","sourceRoot":"","sources":["../../src/migrations/migrations.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE3E,OAAO,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEvE,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACtC,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;YACpD,MAAM,cAAc,GAAG;gBACrB;oBACE,SAAS,EAAE,YAAY;oBACvB,QAAQ,EAAE,iBAAiB;oBAC3B,SAAS,EAAE,gBAAgB;iBAC5B;aACF,CAAC;YACF,MAAM,CAAC,SAAS,CAAC,sBAAsB,CAAC,cAAc,EAAE,EAAE,CAAC,EAAE,cAAc,CAAC,CAAC;QAC/E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;YACpE,MAAM,cAAc,GAAG;gBACrB;oBACE,SAAS,EAAE,YAAY;oBACvB,QAAQ,EAAE,8BAA8B;oBACxC,SAAS,EAAE,gBAAgB;iBAC5B;gBACD;oBACE,SAAS,EAAE,YAAY;oBACvB,QAAQ,EAAE,8BAA8B;oBACxC,SAAS,EAAE,gBAAgB;iBAC5B;gBACD;oBACE,SAAS,EAAE,YAAY;oBACvB,QAAQ,EAAE,8BAA8B;oBACxC,SAAS,EAAE,gBAAgB;iBAC5B;aACF,CAAC;YACF,MAAM,kBAAkB,GAAG;gBACzB;oBACE,SAAS,EAAE,gBAAgB;iBAC5B;gBACD;oBACE,SAAS,EAAE,gBAAgB;iBAC5B;aACF,CAAC;YACF,MAAM,CAAC,SAAS,CAAC,sBAAsB,CAAC,cAAc,EAAE,kBAAkB,CAAC,EAAE;gBAC3E;oBACE,SAAS,EAAE,YAAY;oBACvB,SAAS,EAAE,gBAAgB;oBAC3B,QAAQ,EAAE,8BAA8B;iBACzC;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;YAC9C,QAAQ,EAAE,yBAAyB;SACpC,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,IAAI,EAAE;YAChB,MAAM,iBAAiB,CAAC,cAAc,EAAE,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,iBAAiB,CAAC,YAAY,EAAE,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YAChE,MAAM,YAAY,CAAC,CAAC,YAAY,CAAC,EAAE,yBAAyB,CAAC,CAAC;YAE9D,qEAAqE;YACrE,mBAAmB;YACnB,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;YAC1D,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC/B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import path from 'node:path';\n\nimport { assert } from 'chai';\n\nimport { makePostgresTestUtils, queryAsync } from '@prairielearn/postgres';\n\nimport { getMigrationsToExecute, initWithLock } from './migrations.js';\n\ndescribe('migrations', () => {\n describe('getMigrationsToExecute', () => {\n it('handles the case of no executed migrations', () => {\n const migrationFiles = [\n {\n directory: 'migrations',\n filename: '001_testing.sql',\n timestamp: '20220101010101',\n },\n ];\n assert.deepEqual(getMigrationsToExecute(migrationFiles, []), migrationFiles);\n });\n\n it('handles case where subset of migrations have been executed', () => {\n const migrationFiles = [\n {\n directory: 'migrations',\n filename: '20220101010101_testing_1.sql',\n timestamp: '20220101010101',\n },\n {\n directory: 'migrations',\n filename: '20220101010102_testing_2.sql',\n timestamp: '20220101010102',\n },\n {\n directory: 'migrations',\n filename: '20220101010103_testing_3.sql',\n timestamp: '20220101010103',\n },\n ];\n const executedMigrations = [\n {\n timestamp: '20220101010101',\n },\n {\n timestamp: '20220101010102',\n },\n ];\n assert.deepEqual(getMigrationsToExecute(migrationFiles, executedMigrations), [\n {\n directory: 'migrations',\n timestamp: '20220101010103',\n filename: '20220101010103_testing_3.sql',\n },\n ]);\n });\n });\n\n describe('initWithLock', () => {\n const postgresTestUtils = makePostgresTestUtils({\n database: 'prairielearn_migrations',\n });\n\n before(async () => {\n await postgresTestUtils.createDatabase();\n });\n\n after(async () => {\n await postgresTestUtils.dropDatabase();\n });\n\n it('runs both SQL and JavaScript migrations', async () => {\n const migrationDir = path.join(import.meta.dirname, 'fixtures');\n await initWithLock([migrationDir], 'prairielearn_migrations');\n\n // If both migrations ran successfully, there should be a single user\n // in the database.\n const users = await queryAsync('SELECT * FROM users', {});\n assert.lengthOf(users.rows, 1);\n assert.equal(users.rows[0].name, 'Test User');\n });\n });\n});\n"]}
1
+ {"version":3,"file":"migrations.test.js","sourceRoot":"","sources":["../../src/migrations/migrations.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAEnE,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE3E,OAAO,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEvE,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACtC,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;YACpD,MAAM,cAAc,GAAG;gBACrB;oBACE,SAAS,EAAE,YAAY;oBACvB,QAAQ,EAAE,iBAAiB;oBAC3B,SAAS,EAAE,gBAAgB;iBAC5B;aACF,CAAC;YACF,MAAM,CAAC,SAAS,CAAC,sBAAsB,CAAC,cAAc,EAAE,EAAE,CAAC,EAAE,cAAc,CAAC,CAAC;QAC/E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;YACpE,MAAM,cAAc,GAAG;gBACrB;oBACE,SAAS,EAAE,YAAY;oBACvB,QAAQ,EAAE,8BAA8B;oBACxC,SAAS,EAAE,gBAAgB;iBAC5B;gBACD;oBACE,SAAS,EAAE,YAAY;oBACvB,QAAQ,EAAE,8BAA8B;oBACxC,SAAS,EAAE,gBAAgB;iBAC5B;gBACD;oBACE,SAAS,EAAE,YAAY;oBACvB,QAAQ,EAAE,8BAA8B;oBACxC,SAAS,EAAE,gBAAgB;iBAC5B;aACF,CAAC;YACF,MAAM,kBAAkB,GAAG;gBACzB;oBACE,SAAS,EAAE,gBAAgB;iBAC5B;gBACD;oBACE,SAAS,EAAE,gBAAgB;iBAC5B;aACF,CAAC;YACF,MAAM,CAAC,SAAS,CAAC,sBAAsB,CAAC,cAAc,EAAE,kBAAkB,CAAC,EAAE;gBAC3E;oBACE,SAAS,EAAE,YAAY;oBACvB,SAAS,EAAE,gBAAgB;oBAC3B,QAAQ,EAAE,8BAA8B;iBACzC;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;YAC9C,QAAQ,EAAE,yBAAyB;SACpC,CAAC,CAAC;QAEH,SAAS,CAAC,KAAK,IAAI,EAAE;YACnB,MAAM,iBAAiB,CAAC,cAAc,EAAE,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;YAClB,MAAM,iBAAiB,CAAC,YAAY,EAAE,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YAChE,MAAM,YAAY,CAAC,CAAC,YAAY,CAAC,EAAE,yBAAyB,CAAC,CAAC;YAE9D,qEAAqE;YACrE,mBAAmB;YACnB,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;YAC1D,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC/B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import path from 'node:path';\n\nimport { afterAll, assert, beforeAll, describe, it } from 'vitest';\n\nimport { makePostgresTestUtils, queryAsync } from '@prairielearn/postgres';\n\nimport { getMigrationsToExecute, initWithLock } from './migrations.js';\n\ndescribe('migrations', () => {\n describe('getMigrationsToExecute', () => {\n it('handles the case of no executed migrations', () => {\n const migrationFiles = [\n {\n directory: 'migrations',\n filename: '001_testing.sql',\n timestamp: '20220101010101',\n },\n ];\n assert.deepEqual(getMigrationsToExecute(migrationFiles, []), migrationFiles);\n });\n\n it('handles case where subset of migrations have been executed', () => {\n const migrationFiles = [\n {\n directory: 'migrations',\n filename: '20220101010101_testing_1.sql',\n timestamp: '20220101010101',\n },\n {\n directory: 'migrations',\n filename: '20220101010102_testing_2.sql',\n timestamp: '20220101010102',\n },\n {\n directory: 'migrations',\n filename: '20220101010103_testing_3.sql',\n timestamp: '20220101010103',\n },\n ];\n const executedMigrations = [\n {\n timestamp: '20220101010101',\n },\n {\n timestamp: '20220101010102',\n },\n ];\n assert.deepEqual(getMigrationsToExecute(migrationFiles, executedMigrations), [\n {\n directory: 'migrations',\n timestamp: '20220101010103',\n filename: '20220101010103_testing_3.sql',\n },\n ]);\n });\n });\n\n describe('initWithLock', () => {\n const postgresTestUtils = makePostgresTestUtils({\n database: 'prairielearn_migrations',\n });\n\n beforeAll(async () => {\n await postgresTestUtils.createDatabase();\n });\n\n afterAll(async () => {\n await postgresTestUtils.dropDatabase();\n });\n\n it('runs both SQL and JavaScript migrations', async () => {\n const migrationDir = path.join(import.meta.dirname, 'fixtures');\n await initWithLock([migrationDir], 'prairielearn_migrations');\n\n // If both migrations ran successfully, there should be a single user\n // in the database.\n const users = await queryAsync('SELECT * FROM users', {});\n assert.lengthOf(users.rows, 1);\n assert.equal(users.rows[0].name, 'Test User');\n });\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prairielearn/migrations",
3
- "version": "3.0.16",
3
+ "version": "3.0.17",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,40 +11,26 @@
11
11
  "scripts": {
12
12
  "build": "tsc && tscp",
13
13
  "dev": "tsc --watch --preserveWatchOutput & tscp --watch",
14
- "test": "c8 mocha src/**/*.test.ts"
14
+ "test": "vitest run --coverage"
15
15
  },
16
16
  "dependencies": {
17
- "@prairielearn/error": "^2.0.14",
18
- "@prairielearn/logger": "^2.0.14",
19
- "@prairielearn/named-locks": "^3.0.16",
20
- "@prairielearn/postgres": "^2.1.12",
17
+ "@prairielearn/error": "^2.0.15",
18
+ "@prairielearn/logger": "^2.0.15",
19
+ "@prairielearn/named-locks": "^3.0.17",
20
+ "@prairielearn/postgres": "^2.1.13",
21
21
  "fs-extra": "^11.3.0",
22
22
  "serialize-error": "^12.0.0",
23
- "zod": "^3.24.3"
23
+ "zod": "^3.25.45"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@prairielearn/tsconfig": "^0.0.0",
27
27
  "@types/fs-extra": "^11.0.4",
28
- "@types/mocha": "^10.0.10",
29
- "@types/node": "^20.17.32",
30
- "c8": "^10.1.3",
31
- "chai": "^5.2.0",
32
- "chai-as-promised": "^8.0.1",
33
- "mocha": "^11.1.0",
28
+ "@types/node": "^20.17.57",
29
+ "@vitest/coverage-v8": "^3.1.4",
34
30
  "tmp-promise": "^3.0.3",
35
31
  "tsx": "^4.19.4",
36
32
  "typescript": "^5.8.3",
37
- "typescript-cp": "^0.1.9"
38
- },
39
- "c8": {
40
- "reporter": [
41
- "html",
42
- "text-summary",
43
- "cobertura"
44
- ],
45
- "all": true,
46
- "include": [
47
- "src/**"
48
- ]
33
+ "typescript-cp": "^0.1.9",
34
+ "vitest": "^3.1.4"
49
35
  }
50
36
  }
@@ -1,4 +1,4 @@
1
- import { assert } from 'chai';
1
+ import { afterAll, assert, beforeAll, beforeEach, describe, it } from 'vitest';
2
2
 
3
3
  import * as error from '@prairielearn/error';
4
4
  import * as namedLocks from '@prairielearn/named-locks';
@@ -92,7 +92,7 @@ async function insertTestBatchedMigration() {
92
92
  }
93
93
 
94
94
  describe('BatchedMigrationExecutor', () => {
95
- before(async () => {
95
+ beforeAll(async () => {
96
96
  await postgresTestUtils.createDatabase();
97
97
  await namedLocks.init(postgresTestUtils.getPoolConfig(), (err) => {
98
98
  throw err;
@@ -104,7 +104,7 @@ describe('BatchedMigrationExecutor', () => {
104
104
  await postgresTestUtils.resetDatabase();
105
105
  });
106
106
 
107
- after(async () => {
107
+ afterAll(async () => {
108
108
  await namedLocks.close();
109
109
  await postgresTestUtils.dropDatabase();
110
110
  });
@@ -1,7 +1,6 @@
1
1
  import path from 'node:path';
2
2
 
3
- import { assert, use as chaiUse } from 'chai';
4
- import chaiAsPromised from 'chai-as-promised';
3
+ import { afterAll, afterEach, assert, beforeAll, describe, expect, it } from 'vitest';
5
4
 
6
5
  import * as namedLocks from '@prairielearn/named-locks';
7
6
  import { makePostgresTestUtils } from '@prairielearn/postgres';
@@ -11,14 +10,12 @@ import { SCHEMA_MIGRATIONS_PATH, init } from '../index.js';
11
10
  import { selectAllBatchedMigrations } from './batched-migration.js';
12
11
  import { BatchedMigrationsRunner } from './batched-migrations-runner.js';
13
12
 
14
- chaiUse(chaiAsPromised);
15
-
16
13
  const postgresTestUtils = makePostgresTestUtils({
17
14
  database: 'prairielearn_migrations',
18
15
  });
19
16
 
20
17
  describe('BatchedMigrationsRunner', () => {
21
- before(async () => {
18
+ beforeAll(async () => {
22
19
  await postgresTestUtils.createDatabase();
23
20
  await namedLocks.init(postgresTestUtils.getPoolConfig(), (err) => {
24
21
  throw err;
@@ -30,7 +27,7 @@ describe('BatchedMigrationsRunner', () => {
30
27
  await postgresTestUtils.resetDatabase();
31
28
  });
32
29
 
33
- after(async () => {
30
+ afterAll(async () => {
34
31
  await namedLocks.close();
35
32
  await postgresTestUtils.dropDatabase();
36
33
  });
@@ -99,13 +96,11 @@ describe('BatchedMigrationsRunner', () => {
99
96
 
100
97
  await runner.enqueueBatchedMigration('20230406184107_failing_migration');
101
98
 
102
- await assert.isRejected(
99
+ await expect(
103
100
  runner.finalizeBatchedMigration('20230406184107_failing_migration', {
104
101
  logProgress: false,
105
102
  }),
106
- "but it is 'failed'",
107
- );
108
-
103
+ ).rejects.toThrow("but it is 'failed'");
109
104
  const migrations = await selectAllBatchedMigrations('test');
110
105
  assert.lengthOf(migrations, 1);
111
106
  assert.equal(migrations[0].timestamp, '20230406184107');
@@ -1,9 +1,8 @@
1
1
  import path from 'path';
2
2
 
3
- import { assert, use as chaiUse } from 'chai';
4
- import chaiAsPromised from 'chai-as-promised';
5
3
  import fs from 'fs-extra';
6
4
  import tmp from 'tmp-promise';
5
+ import { assert, describe, expect, it } from 'vitest';
7
6
 
8
7
  import {
9
8
  parseAnnotations,
@@ -11,8 +10,6 @@ import {
11
10
  sortMigrationFiles,
12
11
  } from './load-migrations.js';
13
12
 
14
- chaiUse(chaiAsPromised);
15
-
16
13
  async function withMigrationFiles(files: string[], fn: (tmpDir: string) => Promise<void>) {
17
14
  await tmp.withDir(
18
15
  async function (tmpDir) {
@@ -29,10 +26,8 @@ describe('load-migrations', () => {
29
26
  describe('readAndValidateMigrationsFromDirectory', () => {
30
27
  it('handles migrations without a timestamp', async () => {
31
28
  await withMigrationFiles(['001_testing.sql'], async (tmpDir) => {
32
- await assert.isRejected(
33
- readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']),
34
- 'Invalid migration filename: 001_testing.sql',
35
- );
29
+ const promise = readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']);
30
+ await expect(promise).rejects.toThrow('Invalid migration filename: 001_testing.sql');
36
31
  });
37
32
  });
38
33
 
@@ -40,10 +35,8 @@ describe('load-migrations', () => {
40
35
  await withMigrationFiles(
41
36
  ['20220101010101_testing.sql', '20220101010101_testing_again.sql'],
42
37
  async (tmpDir) => {
43
- await assert.isRejected(
44
- readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']),
45
- 'Duplicate migration timestamp',
46
- );
38
+ const promise = readAndValidateMigrationsFromDirectory(tmpDir, ['.sql']);
39
+ await expect(promise).rejects.toThrow('Duplicate migration timestamp');
47
40
  },
48
41
  );
49
42
  });
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
 
3
- import { assert } from 'chai';
3
+ import { afterAll, assert, beforeAll, describe, it } from 'vitest';
4
4
 
5
5
  import { makePostgresTestUtils, queryAsync } from '@prairielearn/postgres';
6
6
 
@@ -60,11 +60,11 @@ describe('migrations', () => {
60
60
  database: 'prairielearn_migrations',
61
61
  });
62
62
 
63
- before(async () => {
63
+ beforeAll(async () => {
64
64
  await postgresTestUtils.createDatabase();
65
65
  });
66
66
 
67
- after(async () => {
67
+ afterAll(async () => {
68
68
  await postgresTestUtils.dropDatabase();
69
69
  });
70
70
 
package/tsconfig.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "compilerOptions": {
4
4
  "outDir": "./dist",
5
5
  "rootDir": "./src",
6
- "types": ["node", "mocha"],
6
+ "types": ["node"],
7
7
  // Use native ESM.
8
8
  "module": "NodeNext",
9
9
  "moduleResolution": "NodeNext"
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ coverage: {
6
+ reporter: ['html', 'text-summary', 'cobertura'],
7
+ all: true,
8
+ include: ['src/**'],
9
+ },
10
+ },
11
+ });