-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
test(node): Add Prisma 8 integration test suite #24681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
22 changes: 22 additions & 0 deletions
22
dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/docker-compose.yml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| version: '3.9' | ||
|
|
||
| services: | ||
| db: | ||
| # Prisma 8 requires PostgreSQL 15 or newer. | ||
| image: postgres:16 | ||
| restart: always | ||
| ports: | ||
| - '5436:5432' | ||
| environment: | ||
| POSTGRES_USER: prisma | ||
| POSTGRES_PASSWORD: prisma | ||
| POSTGRES_DB: tests | ||
| # Dumped from `prisma-cli db init`; the Prisma 8 CLI needs Node 22.18+, so it isn't run at test time. | ||
| volumes: | ||
| - ./prisma/init.sql:/docker-entrypoint-initdb.d/init.sql:ro | ||
| healthcheck: | ||
| test: ['CMD-SHELL', 'pg_isready -U prisma -d tests'] | ||
| interval: 2s | ||
| timeout: 3s | ||
| retries: 30 | ||
| start_period: 5s |
9 changes: 9 additions & 0 deletions
9
dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/instrument.mjs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import * as Sentry from '@sentry/node'; | ||
| import { loggingTransport } from '@sentry-internal/node-integration-tests'; | ||
|
|
||
| Sentry.init({ | ||
| dsn: 'https://public@dsn.ingest.sentry.io/1337', | ||
| release: '1.0', | ||
| tracesSampleRate: 1.0, | ||
| transport: loggingTransport, | ||
| }); |
132 changes: 132 additions & 0 deletions
132
dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/prisma/contract.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| { | ||
| "schemaVersion": "1", | ||
| "targetFamily": "sql", | ||
| "target": "postgres", | ||
| "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", | ||
| "roots": { | ||
| "user": { | ||
| "model": "User", | ||
| "namespace": "public" | ||
| } | ||
| }, | ||
| "domain": { | ||
| "namespaces": { | ||
| "public": { | ||
| "models": { | ||
| "User": { | ||
| "fields": { | ||
| "email": { | ||
| "nullable": false, | ||
| "type": { | ||
| "codecId": "pg/text@1", | ||
| "kind": "scalar" | ||
| } | ||
| }, | ||
| "id": { | ||
| "nullable": false, | ||
| "type": { | ||
| "codecId": "pg/int4@1", | ||
| "kind": "scalar" | ||
| } | ||
| }, | ||
| "name": { | ||
| "nullable": true, | ||
| "type": { | ||
| "codecId": "pg/text@1", | ||
| "kind": "scalar" | ||
| } | ||
| } | ||
| }, | ||
| "relations": {}, | ||
| "storage": { | ||
| "fields": { | ||
| "email": { | ||
| "column": "email" | ||
| }, | ||
| "id": { | ||
| "column": "id" | ||
| }, | ||
| "name": { | ||
| "column": "name" | ||
| } | ||
| }, | ||
| "namespaceId": "public", | ||
| "table": "user" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "storage": { | ||
| "namespaces": { | ||
| "public": { | ||
| "entries": { | ||
| "table": { | ||
| "user": { | ||
| "columns": { | ||
| "email": { | ||
| "codecId": "pg/text@1", | ||
| "nativeType": "text", | ||
| "nullable": false | ||
| }, | ||
| "id": { | ||
| "codecId": "pg/int4@1", | ||
| "default": { | ||
| "expression": "autoincrement()", | ||
| "kind": "function" | ||
| }, | ||
| "nativeType": "int4", | ||
| "nullable": false | ||
| }, | ||
| "name": { | ||
| "codecId": "pg/text@1", | ||
| "nativeType": "text", | ||
| "nullable": true | ||
| } | ||
| }, | ||
| "foreignKeys": [], | ||
| "indexes": [], | ||
| "primaryKey": { | ||
| "columns": ["id"] | ||
| }, | ||
| "uniques": [ | ||
| { | ||
| "columns": ["email"] | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| }, | ||
| "id": "public", | ||
| "kind": "postgres-schema" | ||
| } | ||
| }, | ||
| "storageHash": "f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5" | ||
| }, | ||
| "capabilities": { | ||
| "postgres": { | ||
| "distinctOn": true, | ||
| "jsonAgg": true, | ||
| "lateral": true, | ||
| "limit": true, | ||
| "orderBy": true, | ||
| "returning": true | ||
| }, | ||
| "sql": { | ||
| "checkConstraint": true, | ||
| "defaultInInsert": true, | ||
| "enums": true, | ||
| "lateral": true, | ||
| "returning": true, | ||
| "scalarList": true | ||
| } | ||
| }, | ||
| "extensions": {}, | ||
| "meta": {}, | ||
| "_generated": { | ||
| "warning": "⚠️ GENERATED FILE - DO NOT EDIT", | ||
| "message": "This file is automatically generated by \"prisma contract emit\".", | ||
| "regenerate": "To regenerate, run: prisma contract emit" | ||
| } | ||
| } |
9 changes: 9 additions & 0 deletions
9
dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/prisma/contract.prisma
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // use prisma-next | ||
|
|
||
| // Regenerate `contract.json` with `prisma-cli contract emit` and `init.sql` from `prisma-cli db init` (Node 22.18+). | ||
| // No `DateTime` column: Prisma 8 decodes timestamps into `Temporal`, global only on Node 26. | ||
| model User { | ||
| id Int @id @default(autoincrement()) | ||
| email String @unique | ||
| name String? | ||
| } |
64 changes: 64 additions & 0 deletions
64
dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/prisma/init.sql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| CREATE SCHEMA prisma_contract; | ||
| CREATE TABLE prisma_contract.contract ( | ||
| core_hash text NOT NULL, | ||
| created_at timestamp with time zone DEFAULT now() NOT NULL, | ||
| contract_json jsonb NOT NULL | ||
| ); | ||
| CREATE TABLE prisma_contract.ledger ( | ||
| id bigint NOT NULL, | ||
| created_at timestamp with time zone DEFAULT now() NOT NULL, | ||
| space text NOT NULL, | ||
| migration_name text NOT NULL, | ||
| migration_hash text NOT NULL, | ||
| origin_core_hash text, | ||
| origin_profile_hash text, | ||
| destination_core_hash text NOT NULL, | ||
| destination_profile_hash text, | ||
| operations jsonb NOT NULL | ||
| ); | ||
| CREATE SEQUENCE prisma_contract.ledger_id_seq | ||
| START WITH 1 | ||
| INCREMENT BY 1 | ||
| NO MINVALUE | ||
| NO MAXVALUE | ||
| CACHE 1; | ||
| ALTER SEQUENCE prisma_contract.ledger_id_seq OWNED BY prisma_contract.ledger.id; | ||
| CREATE TABLE prisma_contract.marker ( | ||
| space text DEFAULT 'app'::text NOT NULL, | ||
| core_hash text NOT NULL, | ||
| profile_hash text NOT NULL, | ||
| contract_json jsonb, | ||
| canonical_version integer, | ||
| updated_at timestamp with time zone DEFAULT now() NOT NULL, | ||
| app_tag text, | ||
| meta jsonb DEFAULT '{}'::jsonb NOT NULL, | ||
| invariants text[] DEFAULT '{}'::text[] NOT NULL | ||
| ); | ||
| CREATE TABLE public."user" ( | ||
| email text NOT NULL, | ||
| id integer NOT NULL, | ||
| name text | ||
| ); | ||
| CREATE SEQUENCE public.user_id_seq | ||
| AS integer | ||
| START WITH 1 | ||
| INCREMENT BY 1 | ||
| NO MINVALUE | ||
| NO MAXVALUE | ||
| CACHE 1; | ||
| ALTER SEQUENCE public.user_id_seq OWNED BY public."user".id; | ||
| ALTER TABLE ONLY prisma_contract.ledger ALTER COLUMN id SET DEFAULT nextval('prisma_contract.ledger_id_seq'::regclass); | ||
| ALTER TABLE ONLY public."user" ALTER COLUMN id SET DEFAULT nextval('public.user_id_seq'::regclass); | ||
| INSERT INTO prisma_contract.contract VALUES ('f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', '2026-09-09 09:15:28.369559+00', '{"meta": {}, "roots": {"user": {"model": "User", "namespace": "public"}}, "domain": {"namespaces": {"public": {"models": {"User": {"fields": {"id": {"type": {"kind": "scalar", "codecId": "pg/int4@1"}, "nullable": false}, "name": {"type": {"kind": "scalar", "codecId": "pg/text@1"}, "nullable": true}, "email": {"type": {"kind": "scalar", "codecId": "pg/text@1"}, "nullable": false}}, "storage": {"table": "user", "fields": {"id": {"column": "id"}, "name": {"column": "name"}, "email": {"column": "email"}}, "namespaceId": "public"}, "relations": {}}}}}}, "target": "postgres", "storage": {"namespaces": {"public": {"id": "public", "entries": {"table": {"user": {"columns": {"id": {"codecId": "pg/int4@1", "default": {"kind": "function", "expression": "autoincrement()"}, "nullable": false, "nativeType": "int4"}, "name": {"codecId": "pg/text@1", "nullable": true, "nativeType": "text"}, "email": {"codecId": "pg/text@1", "nullable": false, "nativeType": "text"}}, "indexes": [], "uniques": [{"columns": ["email"]}], "primaryKey": {"columns": ["id"]}, "foreignKeys": []}}}}}, "storageHash": "f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5"}, "extensions": {}, "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "capabilities": {"sql": {"enums": true, "lateral": true, "returning": true, "scalarList": true, "checkConstraint": true, "defaultInInsert": true}, "postgres": {"limit": true, "jsonAgg": true, "lateral": true, "orderBy": true, "returning": true, "distinctOn": true}}, "targetFamily": "sql"}'); | ||
| INSERT INTO prisma_contract.ledger VALUES (1, '2026-09-09 09:15:28.369559+00', 'app', '', 'f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', '', NULL, 'f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', NULL, '[{"id": "table.user", "label": "Create table \"user\"", "target": {"id": "postgres", "details": {"name": "user", "schema": "public", "objectType": "table"}}, "execute": [{"sql": "CREATE TABLE \"public\".\"user\" (\n \"email\" text NOT NULL,\n \"id\" SERIAL NOT NULL,\n \"name\" text,\n PRIMARY KEY (\"id\")\n)", "params": [], "description": "create table \"user\""}], "summary": "Creates table \"user\"", "precheck": [{"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", "params": ["\"public\".\"user\""], "description": "ensure table \"user\" does not exist"}], "postcheck": [{"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"", "params": ["\"public\".\"user\""], "description": "verify table \"user\" exists"}], "operationClass": "additive"}, {"id": "unique.user.user_email_key", "label": "Add unique constraint on \"user\" (email)", "target": {"id": "postgres", "details": {"name": "user_email_key", "table": "user", "schema": "public", "objectType": "unique"}}, "execute": [{"sql": "ALTER TABLE \"public\".\"user\" ADD CONSTRAINT \"user_email_key\" UNIQUE (\"email\")", "description": "add unique constraint \"user_email_key\""}], "precheck": [{"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", "params": ["user_email_key", "public", "\"public\".\"user\""], "description": "ensure constraint \"user_email_key\" does not exist"}], "postcheck": [{"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", "params": ["user_email_key", "public", "\"public\".\"user\""], "description": "verify constraint \"user_email_key\" exists"}], "operationClass": "additive"}]'); | ||
| INSERT INTO prisma_contract.marker VALUES ('app', 'f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', '3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2', NULL, NULL, '2026-09-09 09:15:28.369559+00', NULL, '{}', '{}'); | ||
| ALTER TABLE ONLY prisma_contract.contract | ||
| ADD CONSTRAINT contract_pkey PRIMARY KEY (core_hash); | ||
| ALTER TABLE ONLY prisma_contract.ledger | ||
| ADD CONSTRAINT ledger_pkey PRIMARY KEY (id); | ||
| ALTER TABLE ONLY prisma_contract.marker | ||
| ADD CONSTRAINT marker_pkey PRIMARY KEY (space); | ||
| ALTER TABLE ONLY public."user" | ||
| ADD CONSTRAINT user_email_key UNIQUE (email); | ||
| ALTER TABLE ONLY public."user" | ||
| ADD CONSTRAINT user_pkey PRIMARY KEY (id); |
36 changes: 36 additions & 0 deletions
36
dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/scenario.mjs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| // ESM-only package: the CJS variant gets the namespace from `require(esm)`, so the factory is read off `default`. | ||
| import * as prismaPostgres from '@prisma/orm-postgres/runtime'; | ||
| import * as Sentry from '@sentry/node'; | ||
| import { randomBytes } from 'crypto'; | ||
| import contractJson from './prisma/contract.json' with { type: 'json' }; | ||
|
|
||
| const url = 'postgresql://prisma:prisma@localhost:5436/tests'; | ||
|
|
||
| async function run() { | ||
| const db = prismaPostgres.default({ contractJson, url }); | ||
|
|
||
| await Sentry.startSpan( | ||
| { | ||
| name: 'Test Transaction', | ||
| op: 'transaction', | ||
| }, | ||
| async () => { | ||
| // With `require(esm)` the module-injection notice arrives on a later tick, so a call in the same | ||
| // tick as the require would run before the SDK subscribes to the channels. | ||
| await new Promise(resolve => setImmediate(resolve)); | ||
|
|
||
| await db.orm.public.User.create({ | ||
| name: 'Tilda', | ||
| email: `tilda_${randomBytes(4).toString('hex')}@sentry.io`, | ||
| }); | ||
|
|
||
| await db.orm.public.User.all(); | ||
|
|
||
| await db.orm.public.User.where(user => user.email.like('%sentry.io')).delete(); | ||
| }, | ||
| ); | ||
|
|
||
| await db.close(); | ||
| } | ||
|
|
||
| run(); | ||
62 changes: 62 additions & 0 deletions
62
dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { afterAll, describe, expect } from 'vitest'; | ||
| import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner'; | ||
|
|
||
| afterAll(() => { | ||
| cleanupChildProcesses(); | ||
| }); | ||
|
|
||
| // Prisma 8 ("Prisma Next") has no tracing surface (its scorecard lists OpenTelemetry spans as not in 8.0), so | ||
| // `prismaIntegration` is inert and only the `pg` spans from `postgresIntegration` remain. Pinned here so a | ||
| // Prisma release that adds a tracing surface shows up. The runtime runs on every Node version in the matrix; | ||
| // only the Prisma 8 CLI needs Node 22.18+, which is why the generated files are committed. | ||
| describe('Prisma ORM v8 Tests', () => { | ||
| describeWithDockerCompose('Prisma ORM v8', { workingDirectory: [__dirname] }, () => { | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| createEsmAndCjsTests( | ||
| __dirname, | ||
| 'scenario.mjs', | ||
| 'instrument.mjs', | ||
| (createRunner, test) => { | ||
| test('should instrument PostgreSQL queries from Prisma ORM via pg', { timeout: 75_000 }, async () => { | ||
| await createRunner() | ||
| .unordered() | ||
| .expect({ | ||
| span: container => { | ||
| const segment = container.items.find(item => item.is_segment); | ||
| expect(segment?.name).toBe('Test Transaction'); | ||
|
|
||
| const querySpans = container.items.filter( | ||
| item => | ||
| item.attributes['sentry.origin']?.value === 'auto.db.postgres' && | ||
| item.attributes['db.query.text']?.value, | ||
| ); | ||
| expect(querySpans.map(span => span.name)).toEqual( | ||
| expect.arrayContaining([ | ||
| 'INSERT "public"."user"', | ||
| 'SELECT "public"."user"', | ||
| 'DELETE "public"."user"', | ||
| ]), | ||
| ); | ||
| querySpans.forEach(span => { | ||
| expect(span.attributes['sentry.op']?.value).toBe('db'); | ||
| expect(span.attributes['db.system.name']?.value).toBe('postgresql'); | ||
| expect(span.parent_span_id).toBe(segment?.span_id); | ||
| }); | ||
|
|
||
| expect( | ||
| container.items.filter(item => item.attributes['sentry.origin']?.value === 'auto.db.prisma'), | ||
| ).toEqual([]); | ||
| }, | ||
| }) | ||
| .start() | ||
| .completed(); | ||
| }); | ||
| }, | ||
| { | ||
| additionalDependencies: { | ||
| '@prisma/orm-postgres': '8.0.0-rc.8', | ||
| }, | ||
| copyPaths: ['prisma'], | ||
| }, | ||
| ); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m/q: Isn't that an issue users would face too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, in one narrow case. A CJS app on Node 22 to 24.12 loads this ESM-only package through
require(esm), which Node routes through the async module hooks. The hook thread transforms the file and posts the "module injected" notice over a message port, so the main thread only processes it on a later tick. An ORM call made in the very same tick as therequiretherefore runs before the SDK subscribes to the channels and gets no operation span; anything after the first await is covered. Real apps make their first query after startup, so I left it as a documented edge (also in the PR body of #24682). It is inherent to the async-hook path for any ESM-only dependency required from CJS, not Prisma specific; from Node 24.13 / 25.1 the sync hooks make it disappear. Happy to look at subscribing eagerly for that path in a follow-up if we think it matters.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should add that in the troubleshooting section in the docs, if that really occurs
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh sorry @JPeer264 my clanker went off limits and responded here