Skip to main content
Glama

Prisma MCP Server

Official
by prisma
Apache 2.0
4
44,210
  • Linux
  • Apple
postgresql-views.test.ts17.2 kB
import { loadSchemaContext, pathToPosix, toSchemasContainer } from '@prisma/internals' import path from 'path' import { DbPull } from '../../commands/DbPull' import { Migrate } from '../../Migrate' import { runQueryPostgres, SetupParams, setupPostgres, tearDownPostgres } from '../../utils/setupPostgres' import { describeMatrix, postgresOnly } from '../__helpers__/conditionalTests' import { createDefaultTestContext } from '../__helpers__/context' if (process.env.CI) { jest.setTimeout(300_000) } else { jest.setTimeout(60_000) } const ctx = createDefaultTestContext() describeMatrix(postgresOnly, 'postgresql-views', () => { const connectionString = process.env.TEST_POSTGRES_URI_MIGRATE!.replace( 'tests-migrate', 'tests-migrate-db-pull-postgresql-views', ) type ViewVariant = | 'no-views' // No views in the database | 'no-preview' // No "view" in `previewFeatures` const variantToPath = (variant?: ViewVariant) => { const basePath = 'views-fs' as const return variant === undefined ? basePath : (`${basePath}-${variant}` as const) } const computeSetupParams = (variant?: ViewVariant) => { const fixturePath = path.join('introspection', 'postgresql', variantToPath(variant)) const setupParams: SetupParams = { connectionString, // Note: dirname points to a location with a setup.sql file // which will be executed to prepare the database with the correct tables, views etc. dirname: path.join(__dirname, '..', '..', '__tests__', 'fixtures', fixturePath), } return { setupParams, fixturePath } } const setupPostgresForViewsIO = (variant?: ViewVariant) => { const { setupParams, fixturePath } = computeSetupParams(variant) beforeEach(async () => { await setupPostgres(setupParams) // 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) }) }) return { setupParams, fixturePath } } describe('engine output', () => { describe('no preview feature', () => { const { fixturePath } = setupPostgresForViewsIO('no-preview') it('`views` is null', async () => { ctx.fixture(path.join(fixturePath)) const { engine } = await Migrate.setup({}) const schemaContext = await loadSchemaContext() const introspectionResult = await engine.introspect({ schema: toSchemasContainer(schemaContext.schemaFiles), viewsDirectoryPath: path.join(process.cwd(), 'prisma', 'views'), baseDirectoryPath: ctx.tmpDir, force: false, }) expect(introspectionResult.views).toEqual(null) await engine.stop() }) }) describe('with preview feature and no views defined', () => { const { fixturePath } = setupPostgresForViewsIO('no-views') it('`views` is [] and no views folder is created', async () => { ctx.fixture(path.join(fixturePath)) const { engine } = await Migrate.setup({}) const schemaContext = await loadSchemaContext() const introspectionResult = await engine.introspect({ schema: toSchemasContainer(schemaContext.schemaFiles), viewsDirectoryPath: path.join(process.cwd(), 'prisma', 'views'), baseDirectoryPath: ctx.tmpDir, force: false, }) expect(introspectionResult.views).toEqual([]) await engine.stop() const listWithoutViews = await ctx.fs.listAsync('views') expect(listWithoutViews).toEqual(undefined) }) it('`views` is [] and an empty existing views folder is deleted', async () => { ctx.fixture(path.join(fixturePath)) // Empty dir should be deleted along the views dir await ctx.fs.dirAsync('views/empty-dir') expect(await ctx.fs.listAsync()).toEqual(['node_modules', 'schema.prisma', 'setup.sql', 'views']) expect(await ctx.fs.listAsync('views')).toEqual(['empty-dir']) const { engine } = await Migrate.setup({}) const schemaContext = await loadSchemaContext() const introspectionResult = await engine.introspect({ schema: toSchemasContainer(schemaContext.schemaFiles), viewsDirectoryPath: path.join(process.cwd(), 'views'), baseDirectoryPath: ctx.tmpDir, force: false, }) expect(introspectionResult.views).toEqual([]) await engine.stop() const listWithoutViews = await ctx.fs.listAsync('views') expect(listWithoutViews).toEqual(undefined) // The views folder is deleted expect(await ctx.fs.listAsync()).toEqual(['node_modules', 'schema.prisma', 'setup.sql']) }) it('`views` is [] and a non-empty existing views folder is kept', async () => { ctx.fixture(path.join(fixturePath)) ctx.fs.write('views/README.md', 'Some readme markdown') expect(await ctx.fs.listAsync()).toEqual(['node_modules', 'schema.prisma', 'setup.sql', 'views']) expect(await ctx.fs.listAsync('views')).toEqual(['README.md']) const { engine } = await Migrate.setup({}) const schemaContext = await loadSchemaContext() const introspectionResult = await engine.introspect({ schema: toSchemasContainer(schemaContext.schemaFiles), viewsDirectoryPath: path.join(process.cwd(), 'prisma', 'views'), baseDirectoryPath: ctx.tmpDir, force: false, }) expect(introspectionResult.views).toEqual([]) await engine.stop() const listWithoutViews = await ctx.fs.listAsync('views') expect(listWithoutViews).toEqual(['README.md']) expect(await ctx.fs.listAsync()).toEqual(['node_modules', 'schema.prisma', 'setup.sql', 'views']) }) }) }) describe('with preview feature, views defined and then removed', () => { const { setupParams, fixturePath } = setupPostgresForViewsIO() // TODO: this test is too large in scope, it takes ~6s to run test('re-introspection with views removed', async () => { ctx.fixture(fixturePath) const introspectWithViews = new DbPull() const resultWithViews = introspectWithViews.parse([], await ctx.config()) await expect(resultWithViews).resolves.toMatchInlineSnapshot(`""`) const listWithViews = await ctx.fs.listAsync('views') expect(listWithViews).toMatchInlineSnapshot(` [ "public", "work", ] `) const treeWithViews = await ctx.fs.findAsync({ directories: false, files: true, recursive: true, matching: 'views/**/*', }) expect(treeWithViews).toMatchInlineSnapshot(` [ "views/public/simpleuser.sql", "views/work/workers.sql", ] `) const dropViewsSQL = await ctx.fs.readAsync('./drop_views.sql', 'utf8') // remove any view in the database await runQueryPostgres(setupParams, dropViewsSQL!) const introspectWithoutViews = new DbPull() const resultWithoutViews = introspectWithoutViews.parse([], await ctx.config()) await expect(resultWithoutViews).resolves.toMatchInlineSnapshot(`""`) const listWithoutViews = await ctx.fs.listAsync('views') expect(listWithoutViews).toEqual(undefined) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "Prisma schema loaded from schema.prisma Datasource "db": PostgreSQL database "tests-migrate-db-pull-postgresql-views", schemas "public, work" <location placeholder> - Introspecting based on datasource defined in schema.prisma ✔ Introspected 2 models and wrote them into schema.prisma in XXXms Run prisma generate to generate Prisma Client. Prisma schema loaded from schema.prisma Datasource "db": PostgreSQL database "tests-migrate-db-pull-postgresql-views", schemas "public, work" <location placeholder> - Introspecting based on datasource defined in schema.prisma ✔ Introspected 2 models and wrote them into schema.prisma in XXXms Run prisma generate to generate Prisma Client. " `) }) }) describe('with preview feature and views defined', () => { const { fixturePath } = setupPostgresForViewsIO() // TODO: this test is too large in scope, it takes ~5.2s to run test('basic introspection', async () => { ctx.fixture(fixturePath) const introspect = new DbPull() const result = introspect.parse([], await ctx.config()) await expect(result).resolves.toMatchInlineSnapshot(`""`) const list = await ctx.fs.listAsync('views') expect(list).toMatchInlineSnapshot(` [ "public", "work", ] `) const tree = await ctx.fs.findAsync({ directories: false, files: true, recursive: true, matching: 'views/**/*' }) expect(tree).toMatchInlineSnapshot(` [ "views/public/simpleuser.sql", "views/work/workers.sql", ] `) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "Prisma schema loaded from schema.prisma Datasource "db": PostgreSQL database "tests-migrate-db-pull-postgresql-views", schemas "public, work" <location placeholder> - Introspecting based on datasource defined in schema.prisma ✔ Introspected 2 models and wrote them into schema.prisma in XXXms Run prisma generate to generate Prisma Client. " `) }) const schemaPaths = [ { schemaPath: 'prisma/schema.prisma', viewsPath: 'prisma/views', needsMove: true, needsPathsArg: false, }, { schemaPath: 'custom/schema/dir/schema.prisma', viewsPath: 'custom/schema/dir/views', needsMove: true, needsPathsArg: true, }, { schemaPath: 'non-standard-schema.prisma', viewsPath: 'views', needsMove: true, needsPathsArg: true, }, { schemaPath: 'schema.prisma', viewsPath: 'views', needsMove: false, needsPathsArg: false, }, ] as const for (const { schemaPath, viewsPath, needsMove, needsPathsArg } of schemaPaths) { test(`introspection from ${schemaPath} creates view definition files`, async () => { ctx.fixture(fixturePath) if (needsMove) { await ctx.fs.moveAsync('schema.prisma', schemaPath) } const introspect = new DbPull() const args = needsPathsArg ? ['--schema', `${schemaPath}`] : [] const result = introspect.parse(args, await ctx.config()) await expect(result).resolves.toMatchInlineSnapshot(`""`) // the folders in `views` match the database schema names (public, work) of the views // defined in the `setup.sql` file const list = await ctx.fs.listAsync(viewsPath) expect(list).toMatchInlineSnapshot(` [ "public", "work", ] `) const tree = await ctx.fs.findAsync({ directories: false, files: true, recursive: true, matching: `${viewsPath}/**/*`, }) const polishedTree = tree.map(pathToPosix) expect(polishedTree).toEqual([`${viewsPath}/public/simpleuser.sql`, `${viewsPath}/work/workers.sql`]) const publicSimpleUserView = await ctx.fs.readAsync(`${viewsPath}/public/simpleuser.sql`) expect(publicSimpleUserView).toMatch(/SELECT\s*(su\.)?first_name,\s*(su\.)?last_name\s*FROM\s*someuser su;/) const workWorkersView = await ctx.fs.readAsync(`${viewsPath}/work/workers.sql`) expect(workWorkersView).toMatchInlineSnapshot(` "SELECT su.first_name, su.last_name, c.name AS company_name FROM ( someuser su LEFT JOIN WORK.company c ON ((su.company_id = c.id)) );" `) expect(ctx.normalizedCapturedStdout().replaceAll(schemaPath, '<schema-location>')).toMatchInlineSnapshot(` "Prisma schema loaded from <schema-location> Datasource "db": PostgreSQL database "tests-migrate-db-pull-postgresql-views", schemas "public, work" <location placeholder> - Introspecting based on datasource defined in <schema-location> ✔ Introspected 2 models and wrote them into <schema-location> in XXXms Run prisma generate to generate Prisma Client. " `) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) }) } // TODO: this test is too large in scope, it takes ~5.6s to run. // Also: is it really useful to delete the extraneous files/folders? test('extraneous empty subdirectories should be deleted and top files kept in views directory on introspect', async () => { ctx.fixture(path.join(fixturePath)) await ctx.fs.dirAsync('views') const initialList = await ctx.fs.listAsync('views') expect(initialList).toMatchInlineSnapshot(`[]`) // Empty dir should be deleted await ctx.fs.dirAsync('views/empty-dir') // Any file on the top level should be kept await ctx.fs.fileAsync('views/README') await ctx.fs.fileAsync('views/extraneous-file.sql') const extraneousList = await ctx.fs.listAsync('views') extraneousList?.sort((a, b) => a.localeCompare(b, 'en-US')) expect(extraneousList).toMatchInlineSnapshot(` [ "empty-dir", "extraneous-file.sql", "README", ] `) const introspect = new DbPull() const result = introspect.parse([], await ctx.config()) await expect(result).resolves.toMatchInlineSnapshot(`""`) // the folders in `views` match the database schema names (public, work) of the views // defined in the `setup.sql` file const list = await ctx.fs.listAsync('views') list?.sort((a, b) => a.localeCompare(b, 'en-US')) expect(list).toMatchInlineSnapshot(` [ "extraneous-file.sql", "public", "README", "work", ] `) const tree = await ctx.fs.findAsync({ directories: false, files: true, recursive: true, matching: 'views/**/*' }) tree?.sort((a, b) => a.localeCompare(b, 'en-US')) expect(tree).toMatchInlineSnapshot(` [ "views/extraneous-file.sql", "views/public/simpleuser.sql", "views/README", "views/work/workers.sql", ] `) }) }) describe('no preview', () => { const { fixturePath } = setupPostgresForViewsIO('no-preview') test('basic introspection', async () => { ctx.fixture(path.join(fixturePath)) const introspect = new DbPull() const result = introspect.parse([], await ctx.config()) await expect(result).resolves.toMatchInlineSnapshot(`""`) const list = await ctx.fs.listAsync('views') expect(list).toMatchInlineSnapshot(`undefined`) const tree = await ctx.fs.findAsync({ directories: false, files: true, recursive: true, matching: 'views/**/*' }) expect(tree).toMatchInlineSnapshot(`[]`) expect(ctx.normalizedCapturedStdout()).toMatchInlineSnapshot(` "Prisma schema loaded from schema.prisma Datasource "db": PostgreSQL database "tests-migrate-db-pull-postgresql-views", schemas "public, work" <location placeholder> - Introspecting based on datasource defined in schema.prisma ✔ Introspected 2 models and wrote them into schema.prisma in XXXms Run prisma generate to generate Prisma Client. " `) expect(ctx.mocked['console.error'].mock.calls.join('\n')).toMatchInlineSnapshot(`""`) }) test('introspect with already existing files in "views"', async () => { ctx.fixture(path.join(fixturePath)) await ctx.fs.dirAsync('views/extraneous-dir') await ctx.fs.fileAsync('views/extraneous-file.sql') const extraneousList = await ctx.fs.listAsync('views') expect(extraneousList).toMatchInlineSnapshot(` [ "extraneous-dir", "extraneous-file.sql", ] `) const introspect = new DbPull() const result = introspect.parse([], await ctx.config()) await expect(result).resolves.toMatchInlineSnapshot(`""`) const list = await ctx.fs.listAsync('views') expect(list).toMatchInlineSnapshot(` [ "extraneous-dir", "extraneous-file.sql", ] `) const tree = await ctx.fs.findAsync({ directories: false, files: true, recursive: true, matching: 'views/**/*' }) expect(tree).toMatchInlineSnapshot(` [ "views/extraneous-file.sql", ] `) }) }) })

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