Spaces:
Build error
Build error
| import { Test } from '@nestjs/testing'; | |
| import { getDataSourceToken } from '@nestjs/typeorm'; | |
| import { InstanceCommandGenerationService } from 'src/database/commands/instance-command-generation.service'; | |
| import { type TwentyAllVersion } from 'src/engine/core-modules/upgrade/constants/twenty-all-versions.constant'; | |
| const VERSION_A = '10.20.0' as TwentyAllVersion; | |
| const VERSION_B = '30.40.0' as TwentyAllVersion; | |
| const FIXED_TIMESTAMP = 1775000000000; | |
| const buildMockDataSource = ( | |
| upQueries: { query: string; parameters?: unknown[] }[], | |
| downQueries: { query: string; parameters?: unknown[] }[], | |
| ) => ({ | |
| driver: { | |
| createSchemaBuilder: () => ({ | |
| log: jest.fn().mockResolvedValue({ upQueries, downQueries }), | |
| }), | |
| }, | |
| }); | |
| describe('InstanceCommandGenerationService', () => { | |
| const buildService = async ( | |
| upQueries: { query: string; parameters?: unknown[] }[] = [], | |
| downQueries: { query: string; parameters?: unknown[] }[] = [], | |
| ) => { | |
| const module = await Test.createTestingModule({ | |
| providers: [ | |
| InstanceCommandGenerationService, | |
| { | |
| provide: getDataSourceToken(), | |
| useValue: buildMockDataSource(upQueries, downQueries), | |
| }, | |
| ], | |
| }).compile(); | |
| return module.get(InstanceCommandGenerationService); | |
| }; | |
| it('should return null when no schema changes are detected', async () => { | |
| const service = await buildService(); | |
| const result = await service.generateInstanceCommand({ | |
| migrationName: 'no-changes', | |
| version: VERSION_A, | |
| timestamp: FIXED_TIMESTAMP, | |
| }); | |
| expect(result).toBeNull(); | |
| }); | |
| it('should generate a migration with a single up/down query', async () => { | |
| const service = await buildService( | |
| [{ query: 'ALTER TABLE "core"."user" ADD "foo" varchar' }], | |
| [{ query: 'ALTER TABLE "core"."user" DROP COLUMN "foo"' }], | |
| ); | |
| const result = await service.generateInstanceCommand({ | |
| migrationName: 'add-foo-column', | |
| version: VERSION_A, | |
| timestamp: FIXED_TIMESTAMP, | |
| }); | |
| expect(result).toMatchSnapshot(); | |
| }); | |
| it('should generate a migration with multiple queries', async () => { | |
| const service = await buildService( | |
| [ | |
| { | |
| query: | |
| 'CREATE TABLE "core"."task" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" varchar NOT NULL)', | |
| }, | |
| { | |
| query: | |
| 'ALTER TABLE "core"."task" ADD CONSTRAINT "PK_task" PRIMARY KEY ("id")', | |
| }, | |
| ], | |
| [ | |
| { query: 'ALTER TABLE "core"."task" DROP CONSTRAINT "PK_task"' }, | |
| { query: 'DROP TABLE "core"."task"' }, | |
| ], | |
| ); | |
| const result = await service.generateInstanceCommand({ | |
| migrationName: 'create-task-table', | |
| version: VERSION_A, | |
| timestamp: FIXED_TIMESTAMP, | |
| }); | |
| expect(result).toMatchSnapshot(); | |
| }); | |
| it('should generate a migration with query parameters', async () => { | |
| const service = await buildService( | |
| [ | |
| { | |
| query: | |
| 'INSERT INTO "core"."setting" ("key", "value") VALUES ($1, $2)', | |
| parameters: ['theme', 'dark'], | |
| }, | |
| ], | |
| [ | |
| { | |
| query: 'DELETE FROM "core"."setting" WHERE "key" = $1', | |
| parameters: ['theme'], | |
| }, | |
| ], | |
| ); | |
| const result = await service.generateInstanceCommand({ | |
| migrationName: 'seed-setting', | |
| version: VERSION_A, | |
| timestamp: FIXED_TIMESTAMP, | |
| }); | |
| expect(result).toMatchSnapshot(); | |
| }); | |
| it('should escape single quotes in SQL queries', async () => { | |
| const service = await buildService( | |
| [{ query: 'UPDATE "core"."config" SET "value" = \'it\'\'s done\'' }], | |
| [{ query: 'UPDATE "core"."config" SET "value" = \'original\'' }], | |
| ); | |
| const result = await service.generateInstanceCommand({ | |
| migrationName: 'update-config', | |
| version: VERSION_A, | |
| timestamp: FIXED_TIMESTAMP, | |
| }); | |
| expect(result).toMatchSnapshot(); | |
| }); | |
| it('should escape backslashes in SQL queries', async () => { | |
| const service = await buildService( | |
| [ | |
| { | |
| query: 'UPDATE "core"."config" SET "value" = E\'path\\\\to\\\\file\'', | |
| }, | |
| ], | |
| [{ query: 'UPDATE "core"."config" SET "value" = NULL' }], | |
| ); | |
| const result = await service.generateInstanceCommand({ | |
| migrationName: 'update-path', | |
| version: VERSION_A, | |
| timestamp: FIXED_TIMESTAMP, | |
| }); | |
| expect(result).toMatchSnapshot(); | |
| }); | |
| it('should use default migration name in class and file names', async () => { | |
| const service = await buildService( | |
| [{ query: 'ALTER TABLE "core"."user" ADD "bar" integer' }], | |
| [{ query: 'ALTER TABLE "core"."user" DROP COLUMN "bar"' }], | |
| ); | |
| const result = await service.generateInstanceCommand({ | |
| migrationName: 'auto-generated', | |
| version: VERSION_A, | |
| timestamp: FIXED_TIMESTAMP, | |
| }); | |
| expect(result).toMatchSnapshot(); | |
| }); | |
| it('should encode version correctly in file and class names', async () => { | |
| const service = await buildService( | |
| [{ query: 'SELECT 1' }], | |
| [{ query: 'SELECT 1' }], | |
| ); | |
| const result = await service.generateInstanceCommand({ | |
| migrationName: 'test', | |
| version: VERSION_B, | |
| timestamp: FIXED_TIMESTAMP, | |
| }); | |
| expect(result).toMatchSnapshot(); | |
| }); | |
| it('should return null for slow type when no schema changes are detected', async () => { | |
| const service = await buildService(); | |
| const result = await service.generateInstanceCommand({ | |
| migrationName: 'no-changes', | |
| version: VERSION_A, | |
| timestamp: FIXED_TIMESTAMP, | |
| type: 'slow', | |
| }); | |
| expect(result).toBeNull(); | |
| }); | |
| it('should generate a slow instance command with populated up/down', async () => { | |
| const service = await buildService( | |
| [ | |
| { | |
| query: 'ALTER TABLE "core"."user" ALTER COLUMN "email" SET NOT NULL', | |
| }, | |
| ], | |
| [ | |
| { | |
| query: 'ALTER TABLE "core"."user" ALTER COLUMN "email" DROP NOT NULL', | |
| }, | |
| ], | |
| ); | |
| const result = await service.generateInstanceCommand({ | |
| migrationName: 'make-column-not-nullable', | |
| version: VERSION_A, | |
| timestamp: FIXED_TIMESTAMP, | |
| type: 'slow', | |
| }); | |
| expect(result).toMatchSnapshot(); | |
| }); | |
| it('should use correct file naming for slow instance commands', async () => { | |
| const service = await buildService( | |
| [{ query: 'SELECT 1' }], | |
| [{ query: 'SELECT 1' }], | |
| ); | |
| const result = await service.generateInstanceCommand({ | |
| migrationName: 'backfill-data', | |
| version: VERSION_B, | |
| timestamp: FIXED_TIMESTAMP, | |
| type: 'slow', | |
| }); | |
| const versionSlug = VERSION_B.split('.').slice(0, 2).join('-'); | |
| expect(result?.fileName).toBe( | |
| `${versionSlug}-instance-command-slow-${FIXED_TIMESTAMP}-backfill-data.ts`, | |
| ); | |
| expect(result?.className).toBe('BackfillDataSlowInstanceCommand'); | |
| }); | |
| }); | |