import { makeEventTransaction, makeSpan, makeTrace, makeTransaction, } from 'sentry/views/performance/newTraceDetails/traceModels/traceTreeTestUtils'; import {isMissingInstrumentationNode} from './../traceGuards'; import {TraceTree} from './traceTree'; const start = new Date('2024-02-29T00:00:00Z').getTime() / 1e3; const traceMetadata = {replay: null, meta: null}; const singleTransactionTrace = makeTrace({ transactions: [ makeTransaction({ start_timestamp: start, timestamp: start + 2, children: [], }), ], orphan_errors: [], }); const missingInstrumentationSpans = [ makeSpan({ op: 'db', description: 'redis', start_timestamp: start, timestamp: start + 1, }), makeSpan({ op: 'db', description: 'redis', start_timestamp: start + 2, timestamp: start + 4, }), ]; const childrenMissingInstrumentationSpans = [ makeSpan({ op: 'db', description: 'redis', span_id: 'redis', start_timestamp: start, timestamp: start + 1, }), makeSpan({ op: 'http', description: 'request', span_id: 'other redis', parent_span_id: 'redis', start_timestamp: start + 2, timestamp: start + 4, }), ]; describe('missing instrumentation', () => { it('adds missing instrumentation between sibling spans', () => { const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata); TraceTree.FromSpans( tree.root.children[0].children[0], missingInstrumentationSpans, makeEventTransaction() ); TraceTree.DetectMissingInstrumentation(tree.root); expect(tree.build().serialize()).toMatchSnapshot(); }); it('adds missing instrumentation between children spans', () => { const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata); TraceTree.FromSpans( tree.root.children[0].children[0], childrenMissingInstrumentationSpans, makeEventTransaction() ); TraceTree.DetectMissingInstrumentation(tree.root); expect(tree.build().serialize()).toMatchSnapshot(); }); it('adds missing instrumentation between two spans that share a common root', () => { const tree = TraceTree.FromTrace( makeTrace({ transactions: [ makeTransaction({ span_id: 'parent-transaction', }), ], }), traceMetadata ); TraceTree.FromSpans( tree.root.children[0].children[0], [ makeSpan({ op: 'http', description: 'request', span_id: '0000', parent_span_id: 'parent-transaction', start_timestamp: start, timestamp: start + 2, }), makeSpan({ op: 'db', description: 'redis', span_id: '0001', parent_span_id: '0000', start_timestamp: start, timestamp: start + 2, }), makeSpan({ op: 'cache', description: 'redis', span_id: '0002', parent_span_id: 'parent-transaction', start_timestamp: start + 3, timestamp: start + 4, }), ], makeEventTransaction() ); TraceTree.DetectMissingInstrumentation(tree.root); expect(tree.build().serialize()).toMatchSnapshot(); }); it('removes missing instrumentation nodes', () => { const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata); TraceTree.FromSpans( tree.root.children[0].children[0], missingInstrumentationSpans, makeEventTransaction() ); const snapshot = tree.build().serialize(); TraceTree.DetectMissingInstrumentation(tree.root); // Assert that missing instrumentation nodes exist expect( TraceTree.Find(tree.root, c => isMissingInstrumentationNode(c)) ).not.toBeNull(); // Remove it and assert that the tree is back to the original state TraceTree.RemoveMissingInstrumentationNodes(tree.root); expect(tree.build().serialize()).toEqual(snapshot); expect(tree.build().serialize()).toMatchSnapshot(); }); it('does not add missing instrumentation for browser SDKs', () => { const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata); TraceTree.FromSpans( tree.root.children[0].children[0], missingInstrumentationSpans, makeEventTransaction({sdk: {name: 'sentry.javascript.browser', version: '1.0.0'}}) ); TraceTree.DetectMissingInstrumentation(tree.root); expect(TraceTree.Find(tree.root, c => isMissingInstrumentationNode(c))).toBeNull(); expect(tree.build().serialize()).toMatchSnapshot(); }); it.each([ ['children', childrenMissingInstrumentationSpans], ['siblings', missingInstrumentationSpans], ])('idempotent - %s', (_type, setup) => { const tree = TraceTree.FromTrace(singleTransactionTrace, traceMetadata); TraceTree.FromSpans(tree.root.children[0].children[0], setup, makeEventTransaction()); TraceTree.DetectMissingInstrumentation(tree.root); const initial = tree.build().serialize(); expect(tree.build().serialize()).toMatchSnapshot(); expect(tree.build().serialize()).toEqual(initial); }); });