Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e9c7253
Disable existing fetch tests
chancancode Jan 10, 2025
d605b5a
Install msw
chancancode Jan 10, 2025
ef0b56f
Port enabling/disabling tests
chancancode Jan 10, 2025
3a07a5f
Temporary fix for #5314
chancancode Jan 10, 2025
c00e7f8
Port the first set of actual fetch() test to new msw test infra
chancancode Jan 10, 2025
90de22d
Port trace propagation headers tests
chancancode Jan 13, 2025
c16b0b7
Add tests without global propagator
chancancode Jan 13, 2025
c1bec45
Port test for `clearTimingResources`
chancancode Jan 13, 2025
52d614c
Port CORS tests
chancancode Jan 13, 2025
ad093ba
Port POST requests (measureRequestSize) tests
chancancode Jan 13, 2025
3b20d38
Port secure origin tests
chancancode Jan 13, 2025
72089a5
Port `applyCustomAttributesOnSpan` tests
chancancode Jan 13, 2025
4214f6a
Port `ignoreUrls` tests
chancancode Jan 13, 2025
0344845
Port tests for HTTP errors
chancancode Jan 14, 2025
e97f9b6
Port `PerformanceObserver` available tests
chancancode Jan 14, 2025
8f3dd77
Port `PerformanceObserver` not available tests
chancancode Jan 14, 2025
5e51471
Port `performance.getEntriesByType` not available tests
chancancode Jan 14, 2025
04e844a
Port `ignoreNetworkEvents` tests
chancancode Jan 14, 2025
9f8149c
Cleanup: remove old tests
chancancode Jan 14, 2025
c34f0ee
Get rid of 500ms timeout
chancancode Jan 15, 2025
a155326
Merge branch 'main' into msw-fetch-test
pichlermarc Jan 27, 2025
c543bf6
Revert "Temporary fix for #5314"
chancancode Jan 29, 2025
f6a350d
Manually merge branch 'main' into msw-fetch-test-redux
chancancode Jan 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Port applyCustomAttributesOnSpan tests
  • Loading branch information
chancancode committed Jan 14, 2025
commit 72089a5ac8bba399e0db0c88078b2f781debb7d6
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ describe('fetch', () => {
});
});

describe('applyCustomAttributesOnSpan option', () => {
xdescribe('applyCustomAttributesOnSpan option', () => {
const prepare = async (
url: string,
applyCustomAttributesOnSpan: FetchCustomAttributeFunction
Expand All @@ -898,7 +898,7 @@ describe('fetch', () => {
clearData();
});

it('applies attributes when the request is successful', async () => {
xit('applies attributes when the request is successful', async () => {
await prepare(url, span => {
span.setAttribute(CUSTOM_ATTRIBUTE_KEY, 'custom value');
});
Expand All @@ -908,7 +908,7 @@ describe('fetch', () => {
assert.ok(attributes[CUSTOM_ATTRIBUTE_KEY] === 'custom value');
});

it('applies custom attributes when the request fails', async () => {
xit('applies custom attributes when the request fails', async () => {
await prepare(badUrl, span => {
span.setAttribute(CUSTOM_ATTRIBUTE_KEY, 'custom value');
});
Expand All @@ -918,7 +918,7 @@ describe('fetch', () => {
assert.ok(attributes[CUSTOM_ATTRIBUTE_KEY] === 'custom value');
});

it('has request and response objects in callback arguments', async () => {
xit('has request and response objects in callback arguments', async () => {
let request: any;
let response: any;
const applyCustomAttributes: FetchCustomAttributeFunction = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ import {
} from '@opentelemetry/sdk-trace-web';
import * as assert from 'assert';
import * as sinon from 'sinon';
import { FetchInstrumentation, FetchInstrumentationConfig } from '../src';
import {
FetchCustomAttributeFunction,
FetchInstrumentation,
FetchInstrumentationConfig,
} from '../src';
import { AttributeNames } from '../src/enums/AttributeNames';
import {
SEMATTRS_HTTP_HOST,
Expand Down Expand Up @@ -1109,5 +1113,164 @@ describe('fetch', () => {
]);
});
});

describe('`applyCustomAttributesOnSpan` hook', () => {
const tracedFetch = async ({
handlers = [
msw.http.get('/api/project-headers.json', ({ request }) => {
const headers = new Headers();

for (const [key, value] of request.headers) {
headers.set(`x-request-${key}`, value);
}

return msw.HttpResponse.json({ ok: true }, { headers });
}),
msw.http.get('/api/fail.json', () => {
return msw.HttpResponse.json({ fail: true }, { status: 500 });
}),
],
callback = () => fetch('/api/project-headers.json'),
config,
}: {
handlers?: msw.RequestHandler[];
callback?: () => Promise<Response>;
config: FetchInstrumentationConfig &
Required<
Pick<FetchInstrumentationConfig, 'applyCustomAttributesOnSpan'>
>;
}): Promise<{ rootSpan: api.Span; response: Response }> => {
let response: Response | undefined;

await startWorker(...handlers);

// The current implementation doesn't call this hook until the body has
// been fully read, this ensures that timing is met before returning to
// the test so we don't have to deal with it in every test. Plus it
// checks that the hook is definitely called which is important here.
const appliedCustomAttributes = new Promise<void>(resolve => {
const originalHook = config.applyCustomAttributesOnSpan;

const applyCustomAttributesOnSpan = (
...args: Parameters<FetchCustomAttributeFunction>
) => {
resolve();
originalHook(...args);
};

config = { ...config, applyCustomAttributesOnSpan };
});

const rootSpan = await trace(async () => {
response = await callback();
}, config);

await appliedCustomAttributes;

assert.ok(response instanceof Response);
assert.strictEqual(exportedSpans.length, 1);

return { rootSpan, response };
};

it('can apply arbitrary attributes to the span indiscriminantly', async () => {
await tracedFetch({
config: {
applyCustomAttributesOnSpan: span => {
span.setAttribute('custom.foo', 'bar');
},
},
});

const span: tracing.ReadableSpan = exportedSpans[0];
assert.strictEqual(span.attributes['custom.foo'], 'bar');
});

describe('successful request', () => {
it('has access to the request and response objects', async () => {
await tracedFetch({
callback: () =>
fetch(
new Request('/api/project-headers.json', {
headers: new Headers({
foo: 'bar',
}),
})
),
config: {
applyCustomAttributesOnSpan: (span, request, response) => {
assert.ok(request.headers instanceof Headers);
assert.ok(response instanceof Response);
assert.ok(response.headers instanceof Headers);

assert.strictEqual(
request.headers.get('foo'),
response.headers.get('x-request-foo')
);

span.setAttribute(
'custom.foo',
response.headers.get('x-request-foo')!
);

/*
Note: this confirms that nothing *in the instrumentation code*
consumed the response body; it doesn't guarantee that the response
object passed to the `applyCustomAttributes` hook will always have
a consumable body – in fact, this is typically *not* the case:

```js
// user code:
let response = await fetch("foo");
let json = await response.json(); // <- user code consumes the body on `response`
// ...

{
// ...this is called sometime later...
applyCustomAttributes(span, request, response) {
// too late!
response.bodyUsed // => true
}
}
```

See https://github.com/open-telemetry/opentelemetry-js/pull/5281
*/
assert.strictEqual(response.bodyUsed, false);
},
},
});

const span: tracing.ReadableSpan = exportedSpans[0];
assert.strictEqual(span.attributes['custom.foo'], 'bar');
});

// https://github.com/open-telemetry/opentelemetry-js/pull/5281
it('will not be able to access the response body if already consumed by the application', async () => {
await tracedFetch({
callback: async () => {
const response = await fetch(
new Request('/api/project-headers.json')
);

// body consumed here by the application
await response.json();

return response;
},
config: {
applyCustomAttributesOnSpan: (span, _request, response) => {
assert.ok(response instanceof Response);

span.setAttribute('custom.body-used', response.bodyUsed);
},
},
});

const span: tracing.ReadableSpan = exportedSpans[0];
assert.strictEqual(span.attributes['custom.body-used'], true);
});
});
});
});
});