test(e2e): Add a Mistral E2E app covering both instrumentation paths - #24378
Conversation
size-limit report 📦
|
4920054 to
c90fff4
Compare
c90fff4 to
2fc5bc8
Compare
| const chatSpan = spans.find(isChatSpan); | ||
|
|
||
| expect(chatSpan).toBeDefined(); | ||
| expectCommonChatAttributes(chatSpan!); |
There was a problem hiding this comment.
Pipe path skips uniqueness check
Low Severity
The pipeThrough case picks the first gen_ai.chat span with find and never checks that only one was emitted. Duplicate spans on this drain path would still satisfy the later attribute and nesting checks, so the suite would not catch a double-instrumentation regression.
Triggered by project rule: PR Review Guidelines for Cursor Bot
Reviewed by Cursor Bugbot for commit 2fc5bc8. Configure here.
| debug: !!process.env.DEBUG, | ||
| tunnel: 'http://localhost:3031/', | ||
| tracesSampleRate: 1, | ||
| traceLifecycle: 'stream', |
There was a problem hiding this comment.
l:
| traceLifecycle: 'stream', |
| tracesSampleRate: 1, | ||
| traceLifecycle: 'stream', | ||
| enableRuntimeChannelInjection: isDev, | ||
| integrations: [Sentry.spanStreamingIntegration()], |
There was a problem hiding this comment.
l: I think this can be removed too? This should be used only in browsers AFAIK
| integrations: [Sentry.spanStreamingIntegration()], |
Follows `node-eve` and `node-mastra`: the Mistral SDK talks to a real endpoint
through `E2E_OPENROUTER_API_KEY` rather than a mock, and the app is marked
`sentryTest.optional` so it lands on the job where that secret is set.
The suite runs twice the way `node-mastra` splits dev and prod, so both
instrumentation paths are covered by the same assertions:
production - `dist/app.cjs`, whose Mistral, dataloader and express copies were
transformed at build time by `sentryEsbuildPlugin`
development - unbundled ESM behind the runtime `--import` hook
What it covers:
- gen_ai spans for streaming and non-streaming calls, and the shapes of
`gen_ai.response.text` and `gen_ai.output.messages`
- a rejected model id reaches Sentry as an error, the gen_ai span is marked
errored and records nothing from a response, and the error shares its trace
- a manual span nests under the request span, and the gen_ai span directly under
the manual one, for both streaming and not
- streams drained through `tee()` and `pipeThrough()`, which take their reader
from internal slots and so produced no span before the accompanying fix
- dataloader spans in the same trace as gen_ai spans, which also covers a
CommonJS and an ESM-only module through the same transform in one process
Assertions avoid anything a live model decides. Token counts are checked as
positive numbers and response text for shape, while origin, provider, operation
name, the `chat {model}` naming rule, the stream flags and every parent/child
relationship are exact.
`Sentry.init` registers the runtime hook unless `enableRuntimeChannelInjection`
is false, which the bundled mode sets, so the production run also asserts the
injected channel names are present in the built file. Without that a passing
production run would not distinguish build-time instrumentation from a silent
runtime fallback.
OpenRouter serves an OpenAI-compatible `/v1/chat/completions`, which is what
`chat.complete` and `chat.stream` post to, and the SDK's response schemas accept
it: `usage` carries a `catchall` and `finish_reason` is an open enum.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2fc5bc8 to
7919617
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 7919617. Configure here.
| import { APP, attr, expectCommonChatAttributes, isChatSpan } from './utils'; | ||
|
|
||
| test('emits a gen_ai.chat span for a non-streaming call', async ({ baseURL, request }) => { | ||
| const spansPromise = collectStreamedSpansUntilSegment(APP, 'GET /chat'); |
There was a problem hiding this comment.
Shared routes use non-unique waiters
Medium Severity
Several tests wait only on GET /chat or GET /chat-stream, with no per-request id. collectStreamedSpans can treat a late envelope from an earlier hit of the same route as the trace under test, especially on the development retries. The /chat-error tests already uniquify with the model id; these routes do not.
Additional Locations (2)
Triggered by project rule: PR Review Guidelines for Cursor Bot
Reviewed by Cursor Bugbot for commit 7919617. Configure here.


Stacked on #24243. Adds an E2E test app for the Mistral integration.
The suite runs twice against the same Express app, selected by TEST_ENV. development serves unbundled ESM through the runtime --import hook; production serves an esbuild bundle transformed by sentryEsbuildPlugin. Every assertion runs against both paths, which matters most for a provider that ships ESM-only.
What it covers: