Skip to content

Commit

Permalink
test(client): D1 tests (prisma#22794)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jolg42 authored Feb 14, 2024
1 parent 857abb9 commit 354aaf0
Show file tree
Hide file tree
Showing 41 changed files with 1,723 additions and 1,421 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ jobs:
fail-fast: false
matrix:
clientRuntime: ['node', 'wasm']
flavor: ['js_pg', 'js_neon', 'js_libsql', 'js_planetscale'] # 'js_d1'
flavor: ['js_pg', 'js_neon', 'js_libsql', 'js_planetscale', 'js_d1']
shard: ['1/6', '2/6', '3/6', '4/6', '5/6', '6/6']
node: [20] #[16, 18, 20]
previewFeatures: ['driverAdapters', 'driverAdapters,relationJoins']
Expand Down
1 change: 0 additions & 1 deletion packages/adapter-d1/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
"sideEffects": false,
"dependencies": {
"@prisma/driver-adapter-utils": "workspace:*",
"async-mutex": "0.4.1",
"@cloudflare/workers-types": "4.20240208.0"
},
"devDependencies": {},
Expand Down
7 changes: 6 additions & 1 deletion packages/adapter-d1/src/conversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ function inferColumnType(value: NonNullable<Value>): ColumnType {
// case 'boolean':
// return ColumnTypeEnum.Boolean
case 'number':
return ColumnTypeEnum.UnknownNumber
// Hack - TODO change this when we have type metadata
if (Number.isInteger(value) && Math.abs(value) < Number.MAX_SAFE_INTEGER) {
return ColumnTypeEnum.Int32
} else {
return ColumnTypeEnum.UnknownNumber
}
case 'object':
return inferObjectType(value)
default:
Expand Down
44 changes: 27 additions & 17 deletions packages/adapter-d1/src/d1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
} from '@prisma/driver-adapter-utils'
import { blue, cyan, red, yellow } from 'kleur/colors'

// import { Mutex } from 'async-mutex'
import { getColumnTypes } from './conversion'

// TODO? Env var works differently in D1 so `debug` does not work.
Expand All @@ -29,8 +28,6 @@ type StdClient = D1Database
class D1Queryable<ClientT extends StdClient> implements Queryable {
readonly provider = 'sqlite'

// [LOCK_TAG] = new Mutex()

constructor(protected readonly client: ClientT) {}

/**
Expand Down Expand Up @@ -94,23 +91,30 @@ class D1Queryable<ClientT extends StdClient> implements Queryable {
const tag = '[js::execute_raw]'
// console.debug(`${tag} %O`, query)

// TODO: rows_written or changes? Only rows_written is documented.
return (await this.performIO(query)).map(({ meta }) => meta.changes ?? 0)
return (await this.performIO(query)).map(({ meta }) => meta.rows_written ?? 0)
}

private async performIO(query: Query): Promise<Result<PerformIOResult>> {
// const release = await this[LOCK_TAG].acquire()
// console.debug({ query })

try {
// Hack for
// Hack for booleans, we must convert them to 0/1.
// ✘ [ERROR] Error in performIO: Error: D1_TYPE_ERROR: Type 'boolean' not supported for value 'true'
query.args = query.args.map((arg) => {
if (arg === true) {
return 1
} else if (arg === false) {
return 0
}
// Temporary unblock for "D1_TYPE_ERROR: Type 'bigint' not supported for value '20'"
// For 0-legacy-ports.query-raw tests
// https://github.com/prisma/team-orm/issues/878
else if (typeof arg === 'bigint') {
return Number(arg)
} else if (arg instanceof Uint8Array) {
return Array.from(arg)
}

return arg
})

Expand All @@ -126,17 +130,23 @@ class D1Queryable<ClientT extends StdClient> implements Queryable {
const error = e as Error
console.error('Error in performIO: %O', error)

const rawCode = error['rawCode'] ?? e.cause?.['rawCode']
if (typeof rawCode === 'number') {
return err({
kind: 'Sqlite',
extendedCode: rawCode,
message: error.message,
})
// We only get the error message, not the error code.
// "name":"Error","message":"D1_ERROR: UNIQUE constraint failed: User.email"
// So we try to match some errors and use the generic error code as a fallback.
// https://www.sqlite.org/rescode.html
// 1 = The SQLITE_ERROR result code is a generic error code that is used when no other more specific error code is available.
let extendedCode = 1
if (error.message.startsWith('D1_ERROR: UNIQUE constraint failed:')) {
extendedCode = 2067
} else if (error.message.startsWith('D1_ERROR: FOREIGN KEY constraint failed')) {
extendedCode = 787
}
throw error
} finally {
// release()

return err({
kind: 'Sqlite',
extendedCode,
message: error.message,
})
}
}
}
Expand Down
24 changes: 0 additions & 24 deletions packages/client/helpers/functional-test/run-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ const args = arg(

async function main(): Promise<number | void> {
let miniProxyProcess: ExecaChildProcess | undefined
let wranglerProcess: ExecaChildProcess | undefined

const jestCliBase = new JestCli(['--config', 'tests/functional/jest.config.js'])
let jestCli = jestCliBase
Expand Down Expand Up @@ -168,26 +167,6 @@ async function main(): Promise<number | void> {
}

jestCli = jestCli.withEnv({ ONLY_TEST_PROVIDER_ADAPTERS: adapterProviders.join(',') })

// Start wrangler dev server with the `wrangler-proxy` for D1 tests
if (adapterProviders.includes(AdapterProviders.JS_D1)) {
wranglerProcess = execa(
'pnpm',
[
'wrangler',
'dev',
'--config=./tests/functional/_utils/wrangler.toml',
'node_modules/wrangler-proxy/dist/worker.js',
],
{
preferLocal: true,
// stdio: 'inherit',
env: {
DEBUG: process.env.DEBUG,
},
},
)
}
}

if (args['--engine-type']) {
Expand Down Expand Up @@ -271,9 +250,6 @@ async function main(): Promise<number | void> {
if (miniProxyProcess) {
miniProxyProcess.kill()
}
if (wranglerProcess) {
wranglerProcess.kill()
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
"extension.d.ts"
],
"devDependencies": {
"@cloudflare/workers-types": "4.20240208.0",
"@codspeed/benchmark.js-plugin": "3.1.0",
"@faker-js/faker": "8.4.1",
"@fast-check/jest": "1.8.0",
Expand Down Expand Up @@ -239,8 +240,7 @@
"tsd": "0.30.4",
"typescript": "5.3.3",
"undici": "5.28.3",
"wrangler": "3.28.1",
"wrangler-proxy": "2.2.7",
"wrangler": "3.28.2",
"zx": "7.2.3"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ declare let Prisma: typeof $.Prisma
// ported from: blog
testMatrix.setupTestSuite(
({ provider, driverAdapter }) => {
const isD1DriverAdapter = driverAdapter === 'js_d1'

beforeAll(async () => {
await prisma.user.create({
data: {
Expand Down Expand Up @@ -51,7 +53,7 @@ testMatrix.setupTestSuite(
postgresql: [{ '?column?': 1 }],
cockroachdb: [{ '?column?': BigInt('1') }],
mysql: [{ '1': BigInt('1') }],
sqlite: [{ '1': BigInt('1') }],
sqlite: [{ '1': isD1DriverAdapter ? 1 : BigInt('1') }],
sqlserver: [{ '': 1 }],
}

Expand All @@ -75,7 +77,7 @@ testMatrix.setupTestSuite(
postgresql: [{ number: 1 }],
cockroachdb: [{ number: BigInt('1') }],
mysql: [{ number: BigInt('1') }],
sqlite: [{ number: BigInt('1') }],
sqlite: [{ number: isD1DriverAdapter ? 1 : BigInt('1') }],
sqlserver: [{ number: 1 }],
}

Expand All @@ -86,11 +88,12 @@ testMatrix.setupTestSuite(
const result: any = await prisma.$queryRaw`
SELECT 1 as "number"
`

const results = {
postgresql: [{ number: 1 }],
cockroachdb: [{ number: BigInt('1') }],
mysql: [{ number: BigInt('1') }],
sqlite: [{ number: BigInt('1') }],
sqlite: [{ number: isD1DriverAdapter ? 1 : BigInt('1') }],
sqlserver: [{ number: 1 }],
}

Expand All @@ -107,7 +110,7 @@ testMatrix.setupTestSuite(
postgresql: [{ '?column?': 1 }],
cockroachdb: [{ '?column?': BigInt('1') }],
mysql: [{ '1': BigInt('1') }],
sqlite: [{ '1': BigInt('1') }],
sqlite: [{ '1': isD1DriverAdapter ? 1 : BigInt('1') }],
sqlserver: [{ '': 1 }],
}

Expand Down
8 changes: 4 additions & 4 deletions packages/client/tests/functional/_example/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,16 @@ testMatrix.setupTestSuite(
})

test('getTestSuiteSchema', () => {
const schemaString = getTestSuiteSchema(
{
const schemaString = getTestSuiteSchema({
cliMeta: {
dataProxy: false,
engineType: 'library',
runtime: 'node',
previewFeatures: [],
},
suiteMeta,
suiteConfig,
)
matrixOptions: suiteConfig,
})

expect(schemaString).toContain('generator')
expect(schemaString).toContain('datasource')
Expand Down
60 changes: 37 additions & 23 deletions packages/client/tests/functional/_utils/getTestSuiteInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,14 @@ export function getTestSuitePreviewFeatures(schema: string): string[] {

/**
* Get the generated test suite path, where files will be copied to.
* @param suiteMeta
* @param suiteConfig
* @returns
*/
export function getTestSuiteFolderPath(suiteMeta: TestSuiteMeta, suiteConfig: NamedTestSuiteConfig) {
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)
Expand All @@ -72,25 +75,31 @@ export function getTestSuiteFolderPath(suiteMeta: TestSuiteMeta, suiteConfig: Na

/**
* Get the generated test suite schema file path.
* @param suiteMeta
* @param suiteConfig
* @returns
*/
export function getTestSuiteSchemaPath(suiteMeta: TestSuiteMeta, suiteConfig: NamedTestSuiteConfig) {
const prismaFolder = getTestSuitePrismaPath(suiteMeta, suiteConfig)
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.
* @param suiteMeta
* @param suiteConfig
* @returns
*/
export function getTestSuitePrismaPath(suiteMeta: TestSuiteMeta, suiteConfig: NamedTestSuiteConfig) {
const suiteFolder = getTestSuiteFolderPath(suiteMeta, suiteConfig)
export function getTestSuitePrismaPath({
suiteMeta,
suiteConfig,
}: {
suiteMeta: TestSuiteMeta
suiteConfig: NamedTestSuiteConfig
}) {
const suiteFolder = getTestSuiteFolderPath({ suiteMeta, suiteConfig })
const prismaPath = path.join(suiteFolder, 'prisma')

return prismaPath
Expand Down Expand Up @@ -155,15 +164,16 @@ function getTestSuiteParametersString(configs: Record<string, string>[]) {

/**
* Inflate the base schema with a test suite config, used for schema generation.
* @param suiteMeta
* @param suiteConfig
* @returns
*/
export function getTestSuiteSchema(
cliMeta: CliMeta,
suiteMeta: TestSuiteMeta,
matrixOptions: NamedTestSuiteConfig['matrixOptions'],
) {
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)
Expand Down Expand Up @@ -270,7 +280,11 @@ export function getTestSuiteCliMeta(): CliMeta {
/**
* Get `ClientMeta` information to be passed down into the test suite.
*/
export function getTestSuiteClientMeta(suiteConfig: NamedTestSuiteConfig['matrixOptions']): ClientMeta {
export function getTestSuiteClientMeta({
suiteConfig,
}: {
suiteConfig: NamedTestSuiteConfig['matrixOptions']
}): ClientMeta {
return {
...getTestSuiteCliMeta(),
driverAdapter: isDriverAdapterProviderLabel(suiteConfig.driverAdapter),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ class ConditionalErrorBuilder<Supplied> implements With<Supplied>, ConditionalEr
return `TODO: add error for relationMode=${relationMode}`
}

// The errors are exactly the same for SQLite and these driverAdapters
if (driverAdapter === 'js_d1' || driverAdapter === 'js_libsql') {
return (
errorBase['sqlite'] ||
`TODO: add error for provider=sqlite (which will be used for libSQL and D1 driver adapters snapshots)`
)
}

return (
errorBase[driverAdapter ?? provider] ||
`TODO: add error for provider=${provider} and driverAdapter=${driverAdapter}`
Expand Down
Loading

0 comments on commit 354aaf0

Please sign in to comment.