Skip to main content
Glama

Prisma MCP Server

Official
by prisma
Apache 2.0
4
44,192
  • Linux
  • Apple
MigrateDiff.test.ts•42.7 kB
// describeMatrix making eslint unhappy about the test names /* eslint-disable jest/no-identical-title */ import os from 'node:os' import path from 'node:path' import { MigrateDiff } from '../commands/MigrateDiff' import { setupCockroach, tearDownCockroach } from '../utils/setupCockroach' import { setupMSSQL, tearDownMSSQL } from '../utils/setupMSSQL' import { setupMysql, tearDownMysql } from '../utils/setupMysql' import type { SetupParams } from '../utils/setupPostgres' import { setupPostgres, tearDownPostgres } from '../utils/setupPostgres' import { allDriverAdapters, cockroachdbOnly, describeMatrix, mongodbOnly, noDriverAdapters, postgresOnly, sqlServerOnly, } from './__helpers__/conditionalTests' import { createDefaultTestContext } from './__helpers__/context' const ctx = createDefaultTestContext() const isWindows = os.platform() === 'win32' describe('migrate diff', () => { describe('using Prisma Config', () => { it('--from-url is not supported', async () => { ctx.fixture('prisma-config-validation/sqlite-d1') expect.assertions(3) try { await MigrateDiff.new().parse(['--from-url', 'file:./dev.db'], await ctx.config()) } catch (error) { const e = error as Error & { code?: number } expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) expect(e.code).toEqual(undefined) expect(e.message).toMatchInlineSnapshot(` " Passing the --from-url flag to the prisma migrate diff command is not supported when defining an adapter in Prisma config file (e.g., \`prisma.config.ts\`). More information about this limitation: https://pris.ly/d/schema-engine-limitations " `) } }) it('--to-url is not supported', async () => { ctx.fixture('prisma-config-validation/sqlite-d1') expect.assertions(3) try { await MigrateDiff.new().parse(['--from-url', 'file:./dev.db'], await ctx.config()) } catch (error) { const e = error as Error & { code?: number } expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) expect(e.code).toEqual(undefined) expect(e.message).toMatchInlineSnapshot(` " Passing the --from-url flag to the prisma migrate diff command is not supported when defining an adapter in Prisma config file (e.g., \`prisma.config.ts\`). More information about this limitation: https://pris.ly/d/schema-engine-limitations " `) } }) it('--from-schema-datasource is not supported', async () => { ctx.fixture('prisma-config-validation/sqlite-d1') expect.assertions(3) try { await MigrateDiff.new().parse(['--from-schema-datasource', 'schema.prisma'], await ctx.config()) } catch (error) { const e = error as Error & { code?: number } expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) expect(e.code).toEqual(undefined) expect(e.message).toMatchInlineSnapshot(` " Passing the --from-schema-datasource flag to the prisma migrate diff command is not supported when defining an adapter in Prisma config file (e.g., \`prisma.config.ts\`). More information about this limitation: https://pris.ly/d/schema-engine-limitations " `) } }) it('--to-schema-datasource is not supported', async () => { ctx.fixture('prisma-config-validation/sqlite-d1') expect.assertions(3) try { await MigrateDiff.new().parse(['--to-schema-datasource', 'schema.prisma'], await ctx.config()) } catch (error) { const e = error as Error & { code?: number } expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) expect(e.code).toEqual(undefined) expect(e.message).toMatchInlineSnapshot(` " Passing the --to-schema-datasource flag to the prisma migrate diff command is not supported when defining an adapter in Prisma config file (e.g., \`prisma.config.ts\`). More information about this limitation: https://pris.ly/d/schema-engine-limitations " `) } }) it('--shadow-database-url is not supported', async () => { ctx.fixture('prisma-config-validation/sqlite-d1') expect.assertions(3) try { await MigrateDiff.new().parse(['--shadow-database-url', 'file:./dev.shadow.db'], await ctx.config()) } catch (error) { const e = error as Error & { code?: number } expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) expect(e.code).toEqual(undefined) expect(e.message).toMatchInlineSnapshot(` " Passing the --shadow-database-url flag to the prisma migrate diff command is not supported when defining an adapter in Prisma config file (e.g., \`prisma.config.ts\`). More information about this limitation: https://pris.ly/d/schema-engine-limitations " `) } }) it('--from-local-d1 is not supported', async () => { ctx.fixture('prisma-config-validation/sqlite-d1') expect.assertions(3) try { await MigrateDiff.new().parse(['--from-local-d1', 'file:./dev.shadow.db'], await ctx.config()) } catch (error) { const e = error as Error & { code?: number } expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) expect(e.code).toEqual(undefined) expect(e.message).toMatchInlineSnapshot(` " Passing the --from-local-d1 flag to the prisma migrate diff command is not supported when defining an adapter in Prisma config file (e.g., \`prisma.config.ts\`). More information about this limitation: https://pris.ly/d/schema-engine-limitations " `) } }) it('--to-local-d1 is not supported', async () => { ctx.fixture('prisma-config-validation/sqlite-d1') expect.assertions(3) try { await MigrateDiff.new().parse(['--to-local-d1', 'file:./dev.shadow.db'], await ctx.config()) } catch (error) { const e = error as Error & { code?: number } expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) expect(e.code).toEqual(undefined) expect(e.message).toMatchInlineSnapshot(` " Passing the --to-local-d1 flag to the prisma migrate diff command is not supported when defining an adapter in Prisma config file (e.g., \`prisma.config.ts\`). More information about this limitation: https://pris.ly/d/schema-engine-limitations " `) } }) }) describeMatrix(noDriverAdapters, 'D1', () => { it('should succeed when --from-local-d1 and a single local Cloudflare D1 database exists', async () => { ctx.fixture('cloudflare-d1-one-db') const result = await MigrateDiff.new().parse(['--to-empty', '--from-local-d1', '--script'], await ctx.config()) expect(result).toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "-- DropTable PRAGMA foreign_keys=off; DROP TABLE "Post"; PRAGMA foreign_keys=on; -- DropTable PRAGMA foreign_keys=off; DROP TABLE "User"; PRAGMA foreign_keys=on; " `) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) }) it('should succeed when --to-local-d1 and a single local Cloudflare D1 database exists', async () => { ctx.fixture('cloudflare-d1-one-db') const result = await MigrateDiff.new().parse(['--from-empty', '--to-local-d1', '--script'], await ctx.config()) expect(result).toMatchInlineSnapshot(`""`) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) }) it('should succeed when --from-url and a single local Cloudflare D1 database exists', async () => { ctx.fixture('cloudflare-d1-one-db') const url = path.join( ctx.fs.cwd(), '.wrangler', 'state', 'v3', 'd1', 'miniflare-D1DatabaseObject', '5d11bcce386042472d19a6a4f58e40041ebc5932c972e1449cbf404f3e3c4a7a.sqlite', ) const result = await MigrateDiff.new().parse( ['--to-empty', '--from-url', `file:${url}`, '--script'], await ctx.config(), ) expect(result).toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "-- DropTable PRAGMA foreign_keys=off; DROP TABLE "Post"; PRAGMA foreign_keys=on; -- DropTable PRAGMA foreign_keys=off; DROP TABLE "User"; PRAGMA foreign_keys=on; " `) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) }) it('should succeed when --to-url and a single local Cloudflare D1 database exists', async () => { ctx.fixture('cloudflare-d1-one-db') const url = path.join( ctx.fs.cwd(), '.wrangler', 'state', 'v3', 'd1', 'miniflare-D1DatabaseObject', '5d11bcce386042472d19a6a4f58e40041ebc5932c972e1449cbf404f3e3c4a7a.sqlite', ) const result = await MigrateDiff.new().parse( ['--from-empty', '--to-url', `file:${url}`, '--script'], await ctx.config(), ) expect(result).toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "-- CreateTable CREATE TABLE "Post" ( "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "title" TEXT NOT NULL, "authorId" INTEGER NOT NULL, FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE ); -- CreateTable CREATE TABLE "User" ( "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "email" TEXT NOT NULL, "count1" INTEGER NOT NULL, "name" TEXT ); -- CreateIndex CREATE UNIQUE INDEX "User_email_key" ON "User"("email" ASC); " `) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) }) it('should fail when --from-local-d1 and several local Cloudflare D1 databases exist', async () => { ctx.fixture('cloudflare-d1-many-dbs') const result = MigrateDiff.new().parse(['--to-empty', '--from-local-d1', '--script'], await ctx.config()) await expect(result).rejects.toMatchInlineSnapshot( `"Multiple Cloudflare D1 databases found in .wrangler/state/v3/d1/miniflare-D1DatabaseObject. Please manually specify the local D1 database with \`--from-url file:\`, without using the \`--from-local-d1\` flag."`, ) }) it('should fail when --to-local-d1 and several local Cloudflare D1 databases exist', async () => { ctx.fixture('cloudflare-d1-many-dbs') const result = MigrateDiff.new().parse(['--from-empty', '--to-local-d1', '--script'], await ctx.config()) await expect(result).rejects.toMatchInlineSnapshot( `"Multiple Cloudflare D1 databases found in .wrangler/state/v3/d1/miniflare-D1DatabaseObject. Please manually specify the local D1 database with \`--to-url file:\`, without using the \`--to-local-d1\` flag."`, ) }) it('should fail when --from-local-d1 and no local Cloudflare D1 databases exists', async () => { ctx.fixture('cloudflare-d1-no-db') const result = MigrateDiff.new().parse(['--to-empty', '--from-local-d1', '--script'], await ctx.config()) await expect(result).rejects.toMatchInlineSnapshot( `"No Cloudflare D1 databases found in .wrangler/state/v3/d1/miniflare-D1DatabaseObject. Did you run \`wrangler d1 create <DATABASE_NAME>\` and \`wrangler dev\`?"`, ) }) it('should fail when --to-local-d1 and no local Cloudflare D1 databases exists', async () => { ctx.fixture('cloudflare-d1-no-db') const result = MigrateDiff.new().parse(['--from-empty', '--to-local-d1', '--script'], await ctx.config()) await expect(result).rejects.toMatchInlineSnapshot( `"No Cloudflare D1 databases found in .wrangler/state/v3/d1/miniflare-D1DatabaseObject. Did you run \`wrangler d1 create <DATABASE_NAME>\` and \`wrangler dev\`?"`, ) }) it('should fail when --from-local-d1 is used with --shadow-database-url', async () => { ctx.fixture('cloudflare-d1-one-db') const result = MigrateDiff.new().parse( ['--to-empty', '--from-local-d1', '--shadow-database-url', 'file:dev.db', '--script'], await ctx.config(), ) await expect(result).rejects.toThrow( `The flag \`--shadow-database-url\` is not compatible with \`--from-local-d1\` or \`--to-local-d1\`.`, ) }) it('should fail when --to-local-d1 is used with --shadow-database-url', async () => { ctx.fixture('cloudflare-d1-one-db') const result = MigrateDiff.new().parse( ['--from-empty', '--to-local-d1', '--shadow-database-url', 'file:dev.db', '--script'], await ctx.config(), ) await expect(result).rejects.toThrow( `The flag \`--shadow-database-url\` is not compatible with \`--from-local-d1\` or \`--to-local-d1\`.`, ) }) }) describe('generic', () => { it('wrong flag', async () => { const commandInstance = MigrateDiff.new() const spy = jest.spyOn(commandInstance, 'help').mockImplementation(() => 'Help Me') await commandInstance.parse(['--something'], await ctx.config()) expect(spy).toHaveBeenCalledTimes(1) spy.mockRestore() }) it('help flag', async () => { const commandInstance = MigrateDiff.new() const spy = jest.spyOn(commandInstance, 'help').mockImplementation(() => 'Help Me') await commandInstance.parse(['--help'], await ctx.config()) expect(spy).toHaveBeenCalledTimes(1) spy.mockRestore() }) it('should fail if missing --from-... and --to-...', async () => { ctx.fixture('empty') const result = MigrateDiff.new().parse([], await ctx.config()) await expect(result).rejects.toThrow() }) it('should fail if only --from-... is provided', async () => { ctx.fixture('empty') const result = MigrateDiff.new().parse(['--from-empty'], await ctx.config()) await expect(result).rejects.toThrow() }) it('should fail if only --to-... is provided', async () => { ctx.fixture('empty') const result = MigrateDiff.new().parse(['--to-empty'], await ctx.config()) await expect(result).rejects.toThrow() }) it('should fail if more than 1 --from-... is provided', async () => { ctx.fixture('empty') const result = MigrateDiff.new().parse(['--from-empty', '--from-url=file:dev.db'], await ctx.config()) await expect(result).rejects.toThrow() }) it('should fail if more than 1 --to-... is provided', async () => { ctx.fixture('empty') const result = MigrateDiff.new().parse(['--to-empty', '--to-url=file:dev.db'], await ctx.config()) await expect(result).rejects.toThrow() }) it('should fail for empty/empty', async () => { ctx.fixture('empty') expect.assertions(2) try { await MigrateDiff.new().parse(['--from-empty', '--to-empty'], await ctx.config()) } catch (e) { expect(e.code).toEqual(undefined) expect(e.message).toMatchInlineSnapshot(` "Could not determine the connector to use for diffing. " `) } }) describeMatrix(noDriverAdapters, 'non driver adapter', () => { it('should fail if schema does no exists, --from-schema-datasource', async () => { ctx.fixture('empty') expect.assertions(2) try { await MigrateDiff.new().parse( ['--from-schema-datasource=./doesnoexists.prisma', '--to-empty'], await ctx.config(), ) } catch (e) { expect(e.code).toEqual(undefined) expect(e.message).toMatchInlineSnapshot( `"Could not load \`--from-schema-datasource\` from provided path \`doesnoexists.prisma\`: file or directory not found"`, ) } }) }) }) describe('sqlite', () => { it('should diff --from-empty --to-schema-datamodel=./prisma/schema.prisma', async () => { ctx.fixture('schema-only-sqlite') const result = MigrateDiff.new().parse( ['--from-empty', '--to-schema-datamodel=./prisma/schema.prisma'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` " [+] Added tables - Blog " `) }) it('should diff --from-empty --to-schema-datamodel=./prisma/schema (folder)', async () => { ctx.fixture('schema-folder-sqlite') const result = MigrateDiff.new().parse( ['--from-empty', '--to-schema-datamodel=./prisma/schema'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` " [+] Added tables - Blog - User " `) }) it('should diff --from-empty --to-schema-datamodel=./prisma/schema.prisma --script', async () => { ctx.fixture('schema-only-sqlite') const result = MigrateDiff.new().parse( ['--from-empty', '--to-schema-datamodel=./prisma/schema.prisma', '--script'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "-- CreateTable CREATE TABLE "Blog" ( "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "viewCount20" INTEGER NOT NULL ); " `) }) it('should diff --from-schema-datamodel=./prisma/schema.prisma --to-empty', async () => { ctx.fixture('schema-only-sqlite') const result = MigrateDiff.new().parse( ['--from-schema-datamodel=./prisma/schema.prisma', '--to-empty'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` " [-] Removed tables - Blog " `) }) it('should diff --from-schema-datamodel=./prisma/schema (folder) --to-empty', async () => { ctx.fixture('schema-folder-sqlite') const result = MigrateDiff.new().parse( ['--from-schema-datamodel=./prisma/schema', '--to-empty'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` " [-] Removed tables - Blog - User " `) }) it('should diff --from-schema-datamodel=./prisma/schema.prisma --to-empty --script', async () => { ctx.fixture('schema-only-sqlite') const result = MigrateDiff.new().parse( ['--from-schema-datamodel=./prisma/schema.prisma', '--to-empty', '--script'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "-- DropTable PRAGMA foreign_keys=off; DROP TABLE "Blog"; PRAGMA foreign_keys=on; " `) }) it('should diff and write to output path', async () => { ctx.fixture('schema-only-sqlite') const result = MigrateDiff.new().parse( ['--from-schema-datamodel=./prisma/schema.prisma', '--to-empty', '--script', '--output=./output.sql'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(`""`) expect(ctx.fs.read('./output.sql')).toMatchInlineSnapshot(` "-- DropTable PRAGMA foreign_keys=off; DROP TABLE "Blog"; PRAGMA foreign_keys=on; " `) }) it('should diff and write to output path if directory does not exist', async () => { ctx.fixture('schema-only-sqlite') const result = MigrateDiff.new().parse( ['--from-schema-datamodel=./prisma/schema.prisma', '--to-empty', '--script', '--output=./subdir/output.sql'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(`""`) expect(ctx.fs.read('./subdir/output.sql')).toMatchInlineSnapshot(` "-- DropTable PRAGMA foreign_keys=off; DROP TABLE "Blog"; PRAGMA foreign_keys=on; " `) }) it('should fail with EACCES/EPERM when writing to output path which is read only', async () => { ctx.fixture('schema-only-sqlite') // Create a readonly file ctx.fs.write('./readonly.sql', 'test', { mode: 0o444, }) const result = MigrateDiff.new().parse( ['--from-schema-datamodel=./prisma/schema.prisma', '--to-empty', '--script', '--output=./readonly.sql'], await ctx.config(), ) // Example error message: // macOS // [Error: EACCES: permission denied, open '/private/var/folders/qt/13pk8tq5113437vp1xr2l_s40000gn/T/f322d2ba6d947ea7c04312edde54aba3/readonly.sql'] // Windows // EPERM: operation not permitted, open 'C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\61b2f2248cfc996bff236aa42e874653\\readonly.sql' await expect(result).rejects.toThrow(isWindows ? 'EPERM' : 'EACCES') }) describeMatrix(noDriverAdapters, 'non driver adapter', () => { it('should fail --from-empty --to-url=file:doesnotexists.db', async () => { ctx.fixture('schema-only-sqlite') const result = MigrateDiff.new().parse(['--from-empty', '--to-url=file:doesnotexists.db'], await ctx.config()) await expect(result).rejects.toMatchInlineSnapshot(` "P1003 Database \`doesnotexists.db\` does not exist " `) }) it('should fail --from-url=file:doesnotexists.db --to-empty ', async () => { ctx.fixture('schema-only-sqlite') const result = MigrateDiff.new().parse(['--from-url=file:doesnotexists.db', '--to-empty'], await ctx.config()) await expect(result).rejects.toMatchInlineSnapshot(` "P1003 Database \`doesnotexists.db\` does not exist " `) }) it('should fail if directory in path & sqlite file does not exist', async () => { ctx.fixture('schema-only-sqlite') const result = MigrateDiff.new().parse( ['--from-url=file:./something/doesnotexists.db', '--to-empty'], await ctx.config(), ) await expect(result).rejects.toMatchInlineSnapshot(` "P1003 Database \`doesnotexists.db\` does not exist " `) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) }) it('should diff --from-empty --to-url=file:dev.db', async () => { ctx.fixture('introspection/sqlite') const result = MigrateDiff.new().parse(['--from-empty', '--to-url=file:dev.db'], await ctx.config()) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` " [+] Added tables - Post - Profile - User - _Migration [*] Changed the \`Profile\` table [+] Added unique index on columns (userId) [*] Changed the \`User\` table [+] Added unique index on columns (email) " `) }) it('should diff --from-empty --to-url=file:dev.db --script', async () => { ctx.fixture('introspection/sqlite') const result = MigrateDiff.new().parse(['--from-empty', '--to-url=file:dev.db', '--script'], await ctx.config()) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "-- CreateTable CREATE TABLE "Post" ( "authorId" INTEGER NOT NULL, "content" TEXT, "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "published" BOOLEAN NOT NULL DEFAULT false, "title" TEXT NOT NULL, FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE ); -- CreateTable CREATE TABLE "Profile" ( "bio" TEXT, "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "userId" INTEGER NOT NULL, FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE ); -- CreateTable CREATE TABLE "User" ( "email" TEXT NOT NULL, "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "name" TEXT ); -- CreateTable CREATE TABLE "_Migration" ( "revision" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "name" TEXT NOT NULL, "datamodel" TEXT NOT NULL, "status" TEXT NOT NULL, "applied" INTEGER NOT NULL, "rolled_back" INTEGER NOT NULL, "datamodel_steps" TEXT NOT NULL, "database_migration" TEXT NOT NULL, "errors" TEXT NOT NULL, "started_at" DATETIME NOT NULL, "finished_at" DATETIME ); -- CreateIndex CREATE UNIQUE INDEX "Profile.userId" ON "Profile"("userId" ASC); -- CreateIndex CREATE UNIQUE INDEX "User.email" ON "User"("email" ASC); " `) }) it('should pass if no schema file around', async () => { ctx.fixture('empty') // Create empty file, as the file needs to exists ctx.fs.write('dev.db', '') const result = MigrateDiff.new().parse(['--from-url=file:dev.db', '--to-url=file:dev.db'], await ctx.config()) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "No difference detected. " `) }) it('should exit with code 0 when diff is empty with --script', async () => { ctx.fixture('empty') // Create empty file, as the file needs to exists ctx.fs.write('dev.db', '') const result = MigrateDiff.new().parse( ['--from-empty', '--to-url=file:dev.db', '--script', '--exit-code'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "-- This is an empty migration. " `) }) }) describe('--exit-code', () => { it('should exit with code 2 when diff is not empty without --script', async () => { ctx.fixture('schema-only-sqlite') const result = MigrateDiff.new().parse( ['--from-schema-datamodel=./prisma/schema.prisma', '--to-empty', '--exit-code'], await ctx.config(), ) await expect(result).rejects.toMatchInlineSnapshot(`"process.exit: 2"`) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` " [-] Removed tables - Blog " `) expect(ctx.recordedExitCode()).toEqual(2) }) it('should exit with code 2 when diff is not empty with --script', async () => { ctx.fixture('schema-only-sqlite') const result = MigrateDiff.new().parse( ['--from-schema-datamodel=./prisma/schema.prisma', '--to-empty', '--script', '--exit-code'], await ctx.config(), ) await expect(result).rejects.toMatchInlineSnapshot(`"process.exit: 2"`) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "-- DropTable PRAGMA foreign_keys=off; DROP TABLE "Blog"; PRAGMA foreign_keys=on; " `) expect(ctx.recordedExitCode()).toEqual(2) }) }) }) describeMatrix(mongodbOnly, 'mongodb', () => { // it('should diff --from-url=$TEST_MONGO_URI --to-schema-datamodel=./prisma/schema.prisma', async () => { // ctx.fixture('schema-only-mongodb') // const result = MigrateDiff.new().parse([ // '--from-url', // process.env.TEST_MONGO_URI!, // // '--to-empty', // '--to-schema-datamodel=./prisma/schema.prisma', // ]) // await expect(result).resolves.toMatchInlineSnapshot(``) // expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` // [+] Collection \`User\` // `) // }) it('should diff --from-empty --to-schema-datamodel=./prisma/schema.prisma', async () => { ctx.fixture('schema-only-mongodb') const result = MigrateDiff.new().parse( ['--from-empty', '--to-schema-datamodel=./prisma/schema.prisma'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "[+] Collection \`User\` " `) }) it('should diff --from-schema-datamodel=./prisma/schema.prisma --to-empty', async () => { ctx.fixture('schema-only-mongodb') const result = MigrateDiff.new().parse( ['--from-schema-datamodel=./prisma/schema.prisma', '--to-empty'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "No difference detected. " `) }) it('should fail with not supported error with --script', async () => { ctx.fixture('schema-only-mongodb') const result = MigrateDiff.new().parse( ['--from-empty', '--to-schema-datamodel=./prisma/schema.prisma', '--script'], await ctx.config(), ) await expect(result).rejects.toThrowErrorMatchingInlineSnapshot(` "Rendering to a script is not supported on MongoDB. " `) }) }) describeMatrix(cockroachdbOnly, 'cockroachdb', () => { if (!process.env.TEST_SKIP_COCKROACHDB && !process.env.TEST_COCKROACH_URI_MIGRATE) { throw new Error('You must set a value for process.env.TEST_COCKROACH_URI_MIGRATE. See TESTING.md') } const connectionString = process.env.TEST_COCKROACH_URI_MIGRATE?.replace('tests-migrate', 'tests-migrate-diff') const setupParams = { connectionString: connectionString!, dirname: '', } beforeAll(async () => { await tearDownCockroach(setupParams).catch((e) => { console.error(e) }) }) beforeEach(async () => { await setupCockroach(setupParams).catch((e) => { console.error(e) }) // Update env var because it's the one that is used in the schemas tested process.env.TEST_POSTGRES_URI_MIGRATE = connectionString }) afterEach(async () => { await tearDownCockroach(setupParams).catch((e) => { console.error(e) }) }) it('should diff --from-url=connectionString --to-schema-datamodel=./prisma/schema.prisma --script', async () => { ctx.fixture('schema-only-cockroachdb') const result = MigrateDiff.new().parse( ['--from-url', connectionString!, '--to-schema-datamodel=./prisma/schema.prisma', '--script'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "-- CreateTable CREATE TABLE "Blog" ( "id" INT4 NOT NULL, "viewCount20" INT4 NOT NULL, CONSTRAINT "Blog_pkey" PRIMARY KEY ("id") ); " `) }, 10_000) it('should use env var from .env file with --from-schema-datasource', async () => { ctx.fixture('schema-only-cockroachdb') const result = MigrateDiff.new().parse( ['--from-schema-datasource=./prisma/using-dotenv.prisma', '--to-schema-datamodel=./prisma/schema.prisma'], await ctx.config(), ) await expect(result).rejects.toMatchInlineSnapshot(` "P1001 Can't reach database server at \`fromdotenvdoesnotexist:26257\` Please make sure your database server is running at \`fromdotenvdoesnotexist:26257\`. " `) }) it('should fail for 2 different connectors --from-url=connectionString --to-url=file:dev.db --script', async () => { ctx.fixture('introspection/sqlite') const result = MigrateDiff.new().parse( ['--from-url', connectionString!, '--to-url=file:dev.db', '--script'], await ctx.config(), ) await expect(result).rejects.toThrowErrorMatchingInlineSnapshot(` "Error in Schema engine. Reason: [/some/rust/path:0:0] called \`Option::unwrap()\` on a \`None\` value " `) }) }) describeMatrix(postgresOnly, 'postgres', () => { const connectionString = process.env.TEST_POSTGRES_URI_MIGRATE!.replace('tests-migrate', 'tests-migrate-diff') const setupParams: SetupParams = { connectionString, dirname: '', } beforeEach(async () => { await setupPostgres(setupParams).catch((e) => { console.error(e) }) // Update env var because it's the one that is used in the schemas tested process.env.TEST_POSTGRES_URI_MIGRATE = connectionString }) afterEach(async () => { await tearDownPostgres(setupParams).catch((e) => { console.error(e) }) }) it('should diff --from-url=connectionString --to-schema-datamodel=./prisma/schema.prisma --script', async () => { ctx.fixture('schema-only-postgresql') const result = MigrateDiff.new().parse( ['--from-url', connectionString, '--to-schema-datamodel=./prisma/schema.prisma', '--script'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "-- CreateTable CREATE TABLE "Blog" ( "id" INTEGER NOT NULL, "viewCount20" INTEGER NOT NULL, CONSTRAINT "Blog_pkey" PRIMARY KEY ("id") ); " `) }) it('should use env var from .env file with --from-schema-datasource', async () => { ctx.fixture('schema-only-postgresql') const result = MigrateDiff.new().parse( ['--from-schema-datasource=./prisma/using-dotenv.prisma', '--to-schema-datamodel=./prisma/schema.prisma'], await ctx.config(), ) await expect(result).rejects.toMatchInlineSnapshot(` "P1001 Can't reach database server at \`fromdotenvdoesnotexist:5432\` Please make sure your database server is running at \`fromdotenvdoesnotexist:5432\`. " `) }) it('should fail for 2 different connectors --from-url=connectionString --to-url=file:dev.db --script', async () => { ctx.fixture('introspection/sqlite') const result = MigrateDiff.new().parse( ['--from-url', connectionString, '--to-url=file:dev.db', '--script'], await ctx.config(), ) await expect(result).rejects.toThrowErrorMatchingInlineSnapshot(` "Error in Schema engine. Reason: [/some/rust/path:0:0] called \`Option::unwrap()\` on a \`None\` value " `) }) it('should work if directUrl is set as an env var', async () => { ctx.fixture('schema-only-data-proxy') const result = MigrateDiff.new().parse( ['--from-schema-datasource', 'with-directUrl-env.prisma', '--to-empty'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "No difference detected. " `) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) }) it('should exclude external tables from diff', async () => { ctx.fixture('external-tables') const result = MigrateDiff.new().parse( ['--from-empty', '--to-schema-datamodel=./schema.prisma'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) // Note the missing warnings about the User table as it is marked as external and won't be modified expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "No difference detected. " `) }) }) describeMatrix({ providers: { mysql: true }, driverAdapters: allDriverAdapters }, 'mysql', () => { const databaseName = 'tests-migrate-diff' const connectionString = process.env.TEST_MYSQL_URI_MIGRATE!.replace('tests-migrate', databaseName) const setupParams: SetupParams = { connectionString, dirname: '', } beforeAll(async () => { await tearDownMysql(setupParams).catch((e) => { console.error(e) }) }) beforeEach(async () => { await setupMysql(setupParams).catch((e) => { console.error(e) }) // Update env var because it's the one that is used in the schemas tested process.env.TEST_MYSQL_URI_MIGRATE = connectionString }) afterEach(async () => { await tearDownMysql(setupParams).catch((e) => { console.error(e) }) }) it('should diff --from-url=connectionString --to-schema-datamodel=./prisma/schema.prisma --script', async () => { ctx.fixture('schema-only-mysql') const result = MigrateDiff.new().parse( ['--from-url', connectionString, '--to-schema-datamodel=./prisma/schema.prisma', '--script'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "-- CreateTable CREATE TABLE \`Blog\` ( \`id\` INTEGER NOT NULL, \`viewCount20\` INTEGER NOT NULL, PRIMARY KEY (\`id\`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; " `) }) it('should fail for 2 different connectors --from-url=connectionString --to-url=file:dev.db --script', async () => { ctx.fixture('introspection/sqlite') const result = MigrateDiff.new().parse( ['--from-url', connectionString, '--to-url=file:dev.db', '--script'], await ctx.config(), ) await expect(result).rejects.toThrowErrorMatchingInlineSnapshot(` "Error in Schema engine. Reason: [/some/rust/path:0:0] Column native type missing in mysql_renderer::render_column_type() " `) }) }) describeMatrix(sqlServerOnly, 'sqlserver', () => { if (!process.env.TEST_SKIP_MSSQL && !process.env.TEST_MSSQL_JDBC_URI_MIGRATE) { throw new Error('You must set a value for process.env.TEST_MSSQL_JDBC_URI_MIGRATE. See TESTING.md') } const databaseName = 'tests-migrate-diff' const jdbcConnectionString = process.env.TEST_MSSQL_JDBC_URI_MIGRATE?.replace('tests-migrate', databaseName) const setupParams: SetupParams = { connectionString: process.env.TEST_MSSQL_URI!, dirname: '', } beforeAll(async () => { await tearDownMSSQL(setupParams, databaseName).catch((e) => { console.error(e) }) }) beforeEach(async () => { await setupMSSQL(setupParams, databaseName).catch((e) => { console.error(e) }) // Update env var because it's the one that is used in the schemas tested process.env.TEST_MSSQL_JDBC_URI_MIGRATE = process.env.TEST_MSSQL_JDBC_URI_MIGRATE?.replace( 'tests-migrate', databaseName, ) process.env.TEST_MSSQL_SHADOWDB_JDBC_URI_MIGRATE = process.env.TEST_MSSQL_SHADOWDB_JDBC_URI_MIGRATE?.replace( 'tests-migrate-shadowdb', `${databaseName}-shadowdb`, ) }) afterEach(async () => { await tearDownMSSQL(setupParams, databaseName).catch((e) => { console.error(e) }) }) it('should diff --from-url=connectionString --to-schema-datamodel=./prisma/schema.prisma --script', async () => { ctx.fixture('schema-only-sqlserver') const result = MigrateDiff.new().parse( ['--from-url', jdbcConnectionString!, '--to-schema-datamodel=./prisma/schema.prisma', '--script'], await ctx.config(), ) await expect(result).resolves.toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "BEGIN TRY BEGIN TRAN; -- CreateTable CREATE TABLE [dbo].[Blog] ( [id] INT NOT NULL, [viewCount20] INT NOT NULL, CONSTRAINT [Blog_pkey] PRIMARY KEY CLUSTERED ([id]) ); COMMIT TRAN; END TRY BEGIN CATCH IF @@TRANCOUNT > 0 BEGIN ROLLBACK TRAN; END; THROW END CATCH " `) }) it('should fail for 2 different connectors --from-url=jdbcConnectionString --to-url=file:dev.db --script', async () => { ctx.fixture('introspection/sqlite') const result = MigrateDiff.new().parse( ['--from-url', jdbcConnectionString!, '--to-url=file:dev.db', '--script'], await ctx.config(), ) await expect(result).rejects.toThrowErrorMatchingInlineSnapshot(` "Error in Schema engine. Reason: [/some/rust/path:0:0] Missing column native type in mssql_renderer::render_column_type() " `) }) }) })

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/prisma/prisma'

If you have feedback or need assistance with the MCP directory API, please join our Discord server