Skip to content

Commit db8a21d

Browse files
committed
tests: nuxt tests and upgrade node version
1 parent 978953c commit db8a21d

File tree

5 files changed

+275
-3
lines changed

5 files changed

+275
-3
lines changed

dev-packages/e2e-tests/test-applications/nuxt-3/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
]
3434
},
3535
"volta": {
36-
"extends": "../../package.json"
36+
"extends": "../../package.json",
37+
"node": "22.20.0"
3738
}
3839
}

dev-packages/e2e-tests/test-applications/nuxt-4/nuxt.config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ export default defineNuxtConfig({
1313
},
1414

1515
modules: ['@pinia/nuxt', '@sentry/nuxt/module'],
16-
16+
nitro: {
17+
experimental: {
18+
database: true,
19+
},
20+
},
1721
runtimeConfig: {
1822
public: {
1923
sentry: {

dev-packages/e2e-tests/test-applications/nuxt-4/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"@sentry-internal/test-utils": "link:../../../test-utils"
2626
},
2727
"volta": {
28-
"extends": "../../package.json"
28+
"extends": "../../package.json",
29+
"node": "22.20.0"
2930
},
3031
"sentryTest": {
3132
"optionalVariants": [
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { useDatabase, defineEventHandler, getQuery } from '#imports';
2+
3+
export default defineEventHandler(async event => {
4+
const db = useDatabase();
5+
const query = getQuery(event);
6+
const method = query.method as string;
7+
8+
switch (method) {
9+
case 'prepare-get': {
10+
await db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)');
11+
await db.exec(`INSERT OR REPLACE INTO users (id, name, email) VALUES (1, 'Test User', '[email protected]')`);
12+
const stmt = db.prepare('SELECT * FROM users WHERE id = ?');
13+
const result = await stmt.get(1);
14+
return { success: true, result };
15+
}
16+
17+
case 'prepare-all': {
18+
await db.exec('CREATE TABLE IF NOT EXISTS products (id INTEGER PRIMARY KEY, name TEXT, price REAL)');
19+
await db.exec(`INSERT OR REPLACE INTO products (id, name, price) VALUES
20+
(1, 'Product A', 10.99),
21+
(2, 'Product B', 20.50),
22+
(3, 'Product C', 15.25)`);
23+
const stmt = db.prepare('SELECT * FROM products WHERE price > ?');
24+
const results = await stmt.all(10);
25+
return { success: true, count: results.length, results };
26+
}
27+
28+
case 'prepare-run': {
29+
await db.exec('CREATE TABLE IF NOT EXISTS orders (id INTEGER PRIMARY KEY, customer TEXT, amount REAL)');
30+
const stmt = db.prepare('INSERT INTO orders (customer, amount) VALUES (?, ?)');
31+
const result = await stmt.run('John Doe', 99.99);
32+
return { success: true, result };
33+
}
34+
35+
case 'prepare-bind': {
36+
await db.exec('CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, category TEXT, value INTEGER)');
37+
await db.exec(`INSERT OR REPLACE INTO items (id, category, value) VALUES
38+
(1, 'electronics', 100),
39+
(2, 'books', 50),
40+
(3, 'electronics', 200)`);
41+
const stmt = db.prepare('SELECT * FROM items WHERE category = ?');
42+
const boundStmt = stmt.bind('electronics');
43+
const results = await boundStmt.all();
44+
return { success: true, count: results.length, results };
45+
}
46+
47+
case 'sql': {
48+
await db.exec('CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY, content TEXT, created_at TEXT)');
49+
const timestamp = new Date().toISOString();
50+
const results = await db.sql`INSERT INTO messages (content, created_at) VALUES (${'Hello World'}, ${timestamp})`;
51+
return { success: true, results };
52+
}
53+
54+
case 'exec': {
55+
await db.exec('CREATE TABLE IF NOT EXISTS logs (id INTEGER PRIMARY KEY, message TEXT, level TEXT)');
56+
const result = await db.exec(`INSERT INTO logs (message, level) VALUES ('Test log', 'INFO')`);
57+
return { success: true, result };
58+
}
59+
60+
case 'error': {
61+
const stmt = db.prepare('SELECT * FROM nonexistent_table WHERE invalid_column = ?');
62+
await stmt.get(1);
63+
return { success: false, message: 'Should have thrown an error' };
64+
}
65+
66+
default:
67+
return { error: 'Unknown method' };
68+
}
69+
});
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
3+
4+
test.describe('database integration', () => {
5+
test('captures db.prepare().get() span', async ({ request }) => {
6+
const transactionPromise = waitForTransaction('nuxt-4', transactionEvent => {
7+
return transactionEvent.transaction === 'GET /api/db-test';
8+
});
9+
10+
await request.get('/api/db-test?method=prepare-get');
11+
12+
const transaction = await transactionPromise;
13+
14+
const dbSpan = transaction.spans?.find(span => span.op === 'db.query' && span.description?.includes('SELECT'));
15+
16+
expect(dbSpan).toBeDefined();
17+
expect(dbSpan?.op).toBe('db.query');
18+
expect(dbSpan?.description).toBe('SELECT * FROM users WHERE id = ?');
19+
expect(dbSpan?.data?.['db.system.name']).toBe('sqlite');
20+
expect(dbSpan?.data?.['db.query.text']).toBe('SELECT * FROM users WHERE id = ?');
21+
expect(dbSpan?.data?.['sentry.origin']).toBe('auto.db.nuxt');
22+
});
23+
24+
test('captures db.prepare().all() span', async ({ request }) => {
25+
const transactionPromise = waitForTransaction('nuxt-4', transactionEvent => {
26+
return transactionEvent.transaction === 'GET /api/db-test';
27+
});
28+
29+
await request.get('/api/db-test?method=prepare-all');
30+
31+
const transaction = await transactionPromise;
32+
33+
const dbSpan = transaction.spans?.find(
34+
span => span.op === 'db.query' && span.description?.includes('SELECT * FROM products'),
35+
);
36+
37+
expect(dbSpan).toBeDefined();
38+
expect(dbSpan?.op).toBe('db.query');
39+
expect(dbSpan?.description).toBe('SELECT * FROM products WHERE price > ?');
40+
expect(dbSpan?.data?.['db.system.name']).toBe('sqlite');
41+
expect(dbSpan?.data?.['db.query.text']).toBe('SELECT * FROM products WHERE price > ?');
42+
expect(dbSpan?.data?.['sentry.origin']).toBe('auto.db.nuxt');
43+
});
44+
45+
test('captures db.prepare().run() span', async ({ request }) => {
46+
const transactionPromise = waitForTransaction('nuxt-4', transactionEvent => {
47+
return transactionEvent.transaction === 'GET /api/db-test';
48+
});
49+
50+
await request.get('/api/db-test?method=prepare-run');
51+
52+
const transaction = await transactionPromise;
53+
54+
const dbSpan = transaction.spans?.find(
55+
span => span.op === 'db.query' && span.description?.includes('INSERT INTO orders'),
56+
);
57+
58+
expect(dbSpan).toBeDefined();
59+
expect(dbSpan?.op).toBe('db.query');
60+
expect(dbSpan?.description).toBe('INSERT INTO orders (customer, amount) VALUES (?, ?)');
61+
expect(dbSpan?.data?.['db.system.name']).toBe('sqlite');
62+
expect(dbSpan?.data?.['db.query.text']).toBe('INSERT INTO orders (customer, amount) VALUES (?, ?)');
63+
expect(dbSpan?.data?.['sentry.origin']).toBe('auto.db.nuxt');
64+
});
65+
66+
test('captures db.prepare().bind().all() span', async ({ request }) => {
67+
const transactionPromise = waitForTransaction('nuxt-4', transactionEvent => {
68+
return transactionEvent.transaction === 'GET /api/db-test';
69+
});
70+
71+
await request.get('/api/db-test?method=prepare-bind');
72+
73+
const transaction = await transactionPromise;
74+
75+
const dbSpan = transaction.spans?.find(
76+
span => span.op === 'db.query' && span.description?.includes('SELECT * FROM items'),
77+
);
78+
79+
expect(dbSpan).toBeDefined();
80+
expect(dbSpan?.op).toBe('db.query');
81+
expect(dbSpan?.description).toBe('SELECT * FROM items WHERE category = ?');
82+
expect(dbSpan?.data?.['db.system.name']).toBe('sqlite');
83+
expect(dbSpan?.data?.['db.query.text']).toBe('SELECT * FROM items WHERE category = ?');
84+
expect(dbSpan?.data?.['sentry.origin']).toBe('auto.db.nuxt');
85+
});
86+
87+
test('captures db.sql template tag span', async ({ request }) => {
88+
const transactionPromise = waitForTransaction('nuxt-4', transactionEvent => {
89+
return transactionEvent.transaction === 'GET /api/db-test';
90+
});
91+
92+
await request.get('/api/db-test?method=sql');
93+
94+
const transaction = await transactionPromise;
95+
96+
const dbSpan = transaction.spans?.find(
97+
span => span.op === 'db.query' && span.description?.includes('INSERT INTO messages'),
98+
);
99+
100+
expect(dbSpan).toBeDefined();
101+
expect(dbSpan?.op).toBe('db.query');
102+
expect(dbSpan?.description).toContain('INSERT INTO messages');
103+
expect(dbSpan?.data?.['db.system.name']).toBe('sqlite');
104+
expect(dbSpan?.data?.['db.query.text']).toContain('INSERT INTO messages');
105+
expect(dbSpan?.data?.['sentry.origin']).toBe('auto.db.nuxt');
106+
});
107+
108+
test('captures db.exec() span', async ({ request }) => {
109+
const transactionPromise = waitForTransaction('nuxt-4', transactionEvent => {
110+
return transactionEvent.transaction === 'GET /api/db-test';
111+
});
112+
113+
await request.get('/api/db-test?method=exec');
114+
115+
const transaction = await transactionPromise;
116+
117+
const dbSpan = transaction.spans?.find(
118+
span => span.op === 'db.query' && span.description?.includes('INSERT INTO logs'),
119+
);
120+
121+
expect(dbSpan).toBeDefined();
122+
expect(dbSpan?.op).toBe('db.query');
123+
expect(dbSpan?.description).toBe(`INSERT INTO logs (message, level) VALUES ('Test log', 'INFO')`);
124+
expect(dbSpan?.data?.['db.system.name']).toBe('sqlite');
125+
expect(dbSpan?.data?.['db.query.text']).toBe(`INSERT INTO logs (message, level) VALUES ('Test log', 'INFO')`);
126+
expect(dbSpan?.data?.['sentry.origin']).toBe('auto.db.nuxt');
127+
});
128+
129+
test('captures database error and marks span as failed', async ({ request }) => {
130+
const errorPromise = waitForError('nuxt-4', errorEvent => {
131+
return !!errorEvent?.exception?.values?.[0]?.value?.includes('no such table');
132+
});
133+
134+
const transactionPromise = waitForTransaction('nuxt-4', transactionEvent => {
135+
return transactionEvent.transaction === 'GET /api/db-test';
136+
});
137+
138+
await request.get('/api/db-test?method=error').catch(() => {
139+
// Expected to fail
140+
});
141+
142+
const [error, transaction] = await Promise.all([errorPromise, transactionPromise]);
143+
144+
expect(error).toBeDefined();
145+
expect(error.exception?.values?.[0]?.value).toContain('no such table');
146+
expect(error.exception?.values?.[0]?.mechanism).toEqual({
147+
handled: false,
148+
type: 'auto.db.nuxt',
149+
});
150+
151+
const dbSpan = transaction.spans?.find(
152+
span => span.op === 'db.query' && span.description?.includes('SELECT * FROM nonexistent_table'),
153+
);
154+
155+
expect(dbSpan).toBeDefined();
156+
expect(dbSpan?.op).toBe('db.query');
157+
expect(dbSpan?.description).toBe('SELECT * FROM nonexistent_table WHERE invalid_column = ?');
158+
expect(dbSpan?.data?.['db.system.name']).toBe('sqlite');
159+
expect(dbSpan?.data?.['db.query.text']).toBe('SELECT * FROM nonexistent_table WHERE invalid_column = ?');
160+
expect(dbSpan?.data?.['sentry.origin']).toBe('auto.db.nuxt');
161+
expect(dbSpan?.status).toBe('internal_error');
162+
});
163+
164+
test('captures breadcrumb for db.exec() queries', async ({ request }) => {
165+
const transactionPromise = waitForTransaction('nuxt-4', transactionEvent => {
166+
return transactionEvent.transaction === 'GET /api/db-test';
167+
});
168+
169+
await request.get('/api/db-test?method=exec');
170+
171+
const transaction = await transactionPromise;
172+
173+
const dbBreadcrumb = transaction.breadcrumbs?.find(
174+
breadcrumb => breadcrumb.category === 'query' && breadcrumb.message?.includes('INSERT INTO logs'),
175+
);
176+
177+
expect(dbBreadcrumb).toBeDefined();
178+
expect(dbBreadcrumb?.category).toBe('query');
179+
expect(dbBreadcrumb?.message).toBe(`INSERT INTO logs (message, level) VALUES ('Test log', 'INFO')`);
180+
expect(dbBreadcrumb?.data?.['db.query.text']).toBe(`INSERT INTO logs (message, level) VALUES ('Test log', 'INFO')`);
181+
});
182+
183+
test('multiple database operations in single request create multiple spans', async ({ request }) => {
184+
const transactionPromise = waitForTransaction('nuxt-4', transactionEvent => {
185+
return transactionEvent.transaction === 'GET /api/db-test';
186+
});
187+
188+
await request.get('/api/db-test?method=prepare-get');
189+
190+
const transaction = await transactionPromise;
191+
192+
const dbSpans = transaction.spans?.filter(span => span.op === 'db.query');
193+
194+
expect(dbSpans).toBeDefined();
195+
expect(dbSpans!.length).toBeGreaterThanOrEqual(1);
196+
});
197+
});

0 commit comments

Comments
 (0)