Skip to main content
Glama

Prisma MCP Server

Official
by prisma
Apache 2.0
4
44,213
  • Linux
  • Apple
getTestSuiteInfo.ts11.4 kB
import { ClientEngineType } from '@prisma/internals' import fs from 'fs/promises' import path from 'path' import { matrix } from '../../../../../helpers/blaze/matrix' import { merge } from '../../../../../helpers/blaze/merge' import { MatrixTestHelper } from './defineMatrix' import { AdapterProviders, GeneratorTypes, isDriverAdapterProviderLabel, Providers, RelationModes } from './providers' import type { TestSuiteMeta } from './setupTestSuiteMatrix' import { ClientEngineExecutor, ClientMeta, ClientRuntime, CliMeta } from './types' export type TestSuiteMatrix = { [K in string]: any }[][] export type NamedTestSuiteConfig = { parametersString: string matrixOptions: Record<string, string> & { provider: Providers generatorType?: GeneratorTypes driverAdapter?: `${AdapterProviders}` relationMode?: `${RelationModes}` engineType?: `${ClientEngineType}` clientRuntime?: `${ClientRuntime}` previewFeatures?: string[] clientEngineExecutor?: ClientEngineExecutor } } type MatrixModule = (() => TestSuiteMatrix) | MatrixTestHelper<TestSuiteMatrix> const allProvidersRegexUnion = Object.values(Providers).join('|') const schemaPreviewFeaturesRegex = /previewFeatures\s*=\s*(.*)/ const schemaDefaultGeneratorRegex = /provider\s*=\s*"prisma-client-(j|t)s"/ const schemaProviderRegex = new RegExp(`provider\\s*=\\s*"(?:${allProvidersRegexUnion})"`, 'g') const schemaRelationModeRegex = /relationMode\s*=\s*".*"/ /** * Get the generated test suite name, used for the folder name. * @param suiteMeta * @param suiteConfig * @returns */ export function getTestSuiteFullName(suiteMeta: TestSuiteMeta, suiteConfig: NamedTestSuiteConfig) { let name = `${suiteMeta.testName.replace(/\\|\//g, '.')}` let parametersString = suiteConfig.parametersString const { clientEngineExecutor, clientRuntime } = suiteConfig.matrixOptions if (clientRuntime === 'client' && clientEngineExecutor === 'remote') { parametersString += ', qpe=remote' } name += ` (${parametersString})` // replace illegal chars with empty string return name.replace(/[<>:"\/\\|?*]/g, '') } /** * Get the generated test suite features, used for client generation. * @param suiteConfig * @returns */ export function getTestSuitePreviewFeatures(schema: string): string[] { const match = schema.match(schemaPreviewFeaturesRegex) return match === null ? [] : JSON.parse(match[1]) } /** * Get the generated test suite path, where files will be copied to. */ export function getTestSuiteFolderPath({ suiteMeta, suiteConfig, }: { suiteMeta: TestSuiteMeta suiteConfig: NamedTestSuiteConfig }) { const generatedFolder = path.join(suiteMeta.prismaPath, '..', '.generated') const suiteName = getTestSuiteFullName(suiteMeta, suiteConfig) const suiteFolder = path.join(generatedFolder, suiteName) return suiteFolder } /** * Get the generated test suite schema file path. */ export function getTestSuiteSchemaPath({ suiteMeta, suiteConfig, }: { suiteMeta: TestSuiteMeta suiteConfig: NamedTestSuiteConfig }) { const prismaFolder = getTestSuitePrismaPath({ suiteMeta, suiteConfig }) const schemaPath = path.join(prismaFolder, 'schema.prisma') return schemaPath } /** * Get the generated test suite prisma folder path. */ export function getTestSuitePrismaPath({ suiteMeta, suiteConfig, }: { suiteMeta: TestSuiteMeta suiteConfig: NamedTestSuiteConfig }) { const suiteFolder = getTestSuiteFolderPath({ suiteMeta, suiteConfig }) const prismaPath = path.join(suiteFolder, 'prisma') return prismaPath } /** * Transforms the `_matrix.ts` into the cross-product of config objects. * @param suiteMeta * @returns */ export function getTestSuiteConfigs(suiteMeta: TestSuiteMeta) { const matrixModule = require(suiteMeta._matrixPath).default as MatrixModule let rawMatrix: TestSuiteMatrix let exclude: (config: Record<string, string>) => boolean if (typeof matrixModule === 'function') { rawMatrix = matrixModule() exclude = () => false } else { rawMatrix = matrixModule.matrix() exclude = matrixModule.matrixOptions?.exclude ?? (() => false) } const configs = matrix(rawMatrix) .map((configs) => ({ parametersString: getTestSuiteParametersString(configs), matrixOptions: merge(configs), })) .filter(({ matrixOptions }) => !exclude(matrixOptions)) return configs as NamedTestSuiteConfig[] } /** * Returns "parameters string" part of the suite name * - From each matrix dimension takes first key-value pair. Assumption is that first pair * is what really distinguishes this particular suite and the rest are just additional options, related to that * parameter and do need to be part of the suite name. * - Computes "key1=value1,key2=value2" string from each dimension of the matrix * @param configs * @returns */ function getTestSuiteParametersString(configs: Record<string, string>[]) { return configs .map((config) => { // Note: if the name is too long tests will fail with // `ENAMETOOLONG: name too long` as this is used for the directory name // For `relationMode` tests // we hardcode how it looks like for test results if (config.relationMode !== undefined) { const driverAdapterStr = config.driverAdapter === undefined ? '' : `driverAdapter=${config.driverAdapter},` return `relationMode=${config.relationMode},provider=${config.provider},${driverAdapterStr}onUpdate=${config.onUpdate},onDelete=${config.onDelete},id=${config.id}` } else { const firstKey = Object.keys(config)[0] // ! TODO this can actually produce incorrect tests and break type checks ! \\ Replace with hash return `${firstKey}=${config[firstKey]}` } }) .join(', ') } /** * Inflate the base schema with a test suite config, used for schema generation. */ export function getTestSuiteSchema({ cliMeta, suiteMeta, matrixOptions, }: { cliMeta: CliMeta suiteMeta: TestSuiteMeta matrixOptions: NamedTestSuiteConfig['matrixOptions'] }) { let schema = require(suiteMeta._schemaPath).default(matrixOptions) as string const previewFeatureMatch = schema.match(schemaPreviewFeaturesRegex) const defaultGeneratorMatch = schema.match(schemaDefaultGeneratorRegex) const prismaRelationModeMatch = schema.match(schemaRelationModeRegex) const providerMatch = schema.match(schemaProviderRegex) const previewFeatures = getTestSuitePreviewFeatures(schema) const { engineType, relationMode } = matrixOptions // By default, mini-proxy distinguishes different engine instances using // inline schema hash. In case 2 tests are running in parallel with identical // schema, this can cause all kinds of problems. Adding a unique comment at // the top of schema file forces them to have different hash and fixes this. schema = `// ${JSON.stringify({ test: suiteMeta.testPath, matrixOptions })}\n${schema}` // in some cases we may add more preview features automatically to the schema previewFeatures.push(...cliMeta.previewFeatures) const previewFeaturesStr = `previewFeatures = ${JSON.stringify(previewFeatures)}` // if there's already a preview features block, replace it with the updated one if (previewFeatureMatch !== null) { schema = schema.replace(previewFeatureMatch[0], previewFeaturesStr) } // if there's no preview features, append them to the default generator block if (previewFeatureMatch === null && defaultGeneratorMatch !== null) { const replacement = `${defaultGeneratorMatch[0]}\n${previewFeaturesStr}` schema = schema.replace(defaultGeneratorMatch[0], replacement) } // if an engine type is specified, append it to the default generator block if (engineType !== undefined && defaultGeneratorMatch !== null) { const replacement = `${defaultGeneratorMatch[0]}\nengineType = "${engineType}"` schema = schema.replace(defaultGeneratorMatch[0], replacement) } // for PlanetScale and Vitess, we need to add `relationMode = "prisma"` to the schema if (matrixOptions.relationMode && providerMatch !== null) { const replacement = `${providerMatch![0]}\nrelationMode = "${relationMode}"` if (prismaRelationModeMatch === null) { schema = schema.replace(providerMatch[0], replacement) } } // update the generator block to use the correct generator type if (defaultGeneratorMatch !== null && matrixOptions.generatorType !== undefined) { const replacement = `provider = "${matrixOptions.generatorType}"` schema = schema.replace(defaultGeneratorMatch[0], replacement) } return schema } /** * Get metadata about the original test suite executed by jest. * @returns */ export function getTestSuiteMeta() { const testsDir = path.join(path.dirname(__dirname), '/') const testPath = expect.getState().testPath if (testPath === undefined) { throw new Error(`getTestSuiteMeta can be executed only within jest test`) } const testRootDirName = path.parse(testPath.replace(testsDir, '')).dir const testRoot = path.join(testsDir, testRootDirName) const rootRelativeTestPath = path.relative(testRoot, testPath) const rootRelativeTestDir = path.dirname(rootRelativeTestPath) let testName: string if (rootRelativeTestPath === 'tests.ts') { testName = testRootDirName } else { testName = path.join(testRootDirName, rootRelativeTestDir, path.basename(testPath, '.ts')) } const testFileName = path.basename(testPath) const prismaPath = path.join(testRoot, 'prisma') const _matrixPath = path.join(testRoot, '_matrix') const _schemaPath = path.join(prismaPath, '_schema') const sqlPath = path.join(prismaPath, 'sql') return { testName, testPath, testRoot, rootRelativeTestPath, rootRelativeTestDir, testFileName, prismaPath, sqlPath, _matrixPath, _schemaPath, } } /** * Get `TestCliMeta` from the environment variables created by the test CLI. */ export function getTestSuiteCliMeta(): CliMeta { const dataProxy = Boolean(process.env.TEST_DATA_PROXY) const runtime = process.env.TEST_CLIENT_RUNTIME as ClientRuntime | undefined const engineType = process.env.TEST_ENGINE_TYPE as ClientEngineType | undefined const previewFeatures = process.env.TEST_PREVIEW_FEATURES ?? '' const generatorType = process.env.TEST_GENERATOR_TYPE as GeneratorTypes | undefined const clientEngineExecutor = process.env.TEST_CLIENT_ENGINE_REMOTE_EXECUTOR ? 'remote' : 'local' return { dataProxy, runtime: runtime ?? 'node', engineType: engineType ?? ClientEngineType.Library, previewFeatures: previewFeatures.split(',').filter((feature) => feature !== ''), generatorType, clientEngineExecutor, } } /** * Get `ClientMeta` information to be passed down into the test suite. */ export function getTestSuiteClientMeta({ suiteConfig, }: { suiteConfig: NamedTestSuiteConfig['matrixOptions'] }): ClientMeta { return { ...getTestSuiteCliMeta(), driverAdapter: isDriverAdapterProviderLabel(suiteConfig.driverAdapter), } } export async function testSuiteHasTypedSql(meta: TestSuiteMeta) { return await isDirectory(meta.sqlPath) } async function isDirectory(path: string) { try { const stat = await fs.stat(path) return stat.isDirectory() } catch (e) { if (e.code === 'ENOENT') { return false } throw e } }

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