Files
SuperCharged-Claude-Code-Up…/dexto/packages/core/src/telemetry/http-instrumentation.integration.test.ts
admin b52318eeae feat: Add intelligent auto-router and enhanced integrations
- Add intelligent-router.sh hook for automatic agent routing
- Add AUTO-TRIGGER-SUMMARY.md documentation
- Add FINAL-INTEGRATION-SUMMARY.md documentation
- Complete Prometheus integration (6 commands + 4 tools)
- Complete Dexto integration (12 commands + 5 tools)
- Enhanced Ralph with access to all agents
- Fix /clawd command (removed disable-model-invocation)
- Update hooks.json to v5 with intelligent routing
- 291 total skills now available
- All 21 commands with automatic routing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-28 00:27:56 +04:00

149 lines
5.5 KiB
TypeScript

/**
* Integration test for HTTP instrumentation.
*
* This test verifies that OpenTelemetry's HTTP/fetch instrumentation is working correctly.
* It makes actual HTTP calls and verifies that spans are created for them.
*
* This is critical for ensuring that LLM API calls (which use fetch) are traced.
*
* NOTE: This test sets up OpenTelemetry SDK directly (not via Telemetry class) to verify
* that the specific instrumentations (http + undici) correctly instrument fetch() calls.
* This mirrors the production setup in telemetry.ts.
*/
import { describe, test, expect, afterAll, beforeAll } from 'vitest';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { Resource } from '@opentelemetry/resources';
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';
describe('HTTP Instrumentation', () => {
let serverPort: number;
let memoryExporter: InMemorySpanExporter;
let sdk: NodeSDK;
let server: Awaited<ReturnType<typeof import('http').createServer>>;
beforeAll(async () => {
// Create in-memory exporter
memoryExporter = new InMemorySpanExporter();
// Initialize OpenTelemetry SDK directly with specific instrumentations
// This mirrors the production setup in telemetry.ts
sdk = new NodeSDK({
resource: new Resource({
[ATTR_SERVICE_NAME]: 'http-instrumentation-test',
}),
spanProcessor: new SimpleSpanProcessor(memoryExporter) as any,
instrumentations: [new HttpInstrumentation(), new UndiciInstrumentation()],
});
await sdk.start();
// NOW import http and create the server (after instrumentation is set up)
const http = await import('http');
server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'ok', path: req.url }));
});
await new Promise<void>((resolve) => {
server.listen(0, '127.0.0.1', () => {
const addr = server.address();
if (addr && typeof addr === 'object') {
serverPort = addr.port;
}
resolve();
});
});
});
afterAll(async () => {
// Close server first
if (server) {
await new Promise<void>((resolve, reject) => {
server.close((err) => (err ? reject(err) : resolve()));
});
}
// Then shutdown SDK
if (sdk) {
await sdk.shutdown();
}
});
test('fetch() calls are instrumented and create HTTP spans', async () => {
// Clear any previous spans
memoryExporter.reset();
// Make a fetch call - this should be instrumented by undici instrumentation
// (Node.js 18+ uses undici internally for fetch())
const url = `http://127.0.0.1:${serverPort}/test-fetch-endpoint`;
const response = await fetch(url);
const data = await response.json();
expect(data.message).toBe('ok');
// Give time for async span processing
await new Promise((resolve) => setTimeout(resolve, 500));
// Check that spans were created
const spans = memoryExporter.getFinishedSpans();
// We should have at least one HTTP span
const httpSpans = spans.filter((span) => {
const name = span.name.toLowerCase();
const attrs = span.attributes;
return (
name.includes('get') ||
name.includes('http') ||
name.includes('fetch') ||
attrs['http.method'] === 'GET' ||
attrs['http.request.method'] === 'GET'
);
});
expect(httpSpans.length).toBeGreaterThan(0);
// Verify the span has expected HTTP attributes
const httpSpan = httpSpans[0]!;
const attrs = httpSpan.attributes;
// Should have URL-related attributes
expect(attrs['url.full'] || attrs['http.url'] || attrs['http.target']).toBeDefined();
// Should have method attribute
expect(attrs['http.request.method'] || attrs['http.method']).toBe('GET');
// Should have status code
expect(attrs['http.response.status_code'] || attrs['http.status_code']).toBe(200);
});
test('multiple fetch() calls create multiple spans', async () => {
// Clear any previous spans
memoryExporter.reset();
// Make multiple fetch calls
const urls = [
`http://127.0.0.1:${serverPort}/endpoint-1`,
`http://127.0.0.1:${serverPort}/endpoint-2`,
`http://127.0.0.1:${serverPort}/endpoint-3`,
];
await Promise.all(urls.map((url) => fetch(url)));
// Give time for async span processing
await new Promise((resolve) => setTimeout(resolve, 500));
// Check that spans were created
const spans = memoryExporter.getFinishedSpans();
// Should have at least 3 spans (one for each request)
const httpSpans = spans.filter((span) => {
const attrs = span.attributes;
return attrs['http.request.method'] === 'GET' || attrs['http.method'] === 'GET';
});
expect(httpSpans.length).toBeGreaterThanOrEqual(3);
});
});