@@ -19,23 +19,48 @@ import {WindowWithOcwGlobals} from '../src/types';
1919
2020const windowWithOcwGlobals = window as WindowWithOcwGlobals ;
2121
22+ /**
23+ * Converts bytes in a hexadecimal encoded string to base64 encoded string.
24+ * This is needed because the JSON proto formatting that the OC agent expects
25+ * (via grpc-gateway) formats trace and span IDs in base64. This helper function
26+ * allows matching trace/span IDs that are sent in the XHR body to the agent.
27+ */
28+ function hexToBase64 ( hexString : string ) : string {
29+ const match = hexString . match ( / \w { 2 } / g) ;
30+ if ( ! match ) return '' ;
31+ return window . btoa (
32+ match
33+ . map (
34+ ( hexByteChars ) =>
35+ // tslint:disable-next-line:ban Needed to parse hexadecimal.
36+ String . fromCharCode ( parseInt ( hexByteChars , 16 ) ) )
37+ . join ( '' ) ) ;
38+ }
39+
2240describe ( 'exportRootSpanAfterLoadEvent' , ( ) => {
2341 let realOcwAgent : string | undefined ;
42+ let realTraceparent : string | undefined ;
43+ let sendSpy : jasmine . Spy ;
2444 beforeEach ( ( ) => {
2545 jasmine . clock ( ) . install ( ) ;
2646 spyOn ( XMLHttpRequest . prototype , 'open' ) ;
27- spyOn ( XMLHttpRequest . prototype , 'send' ) ;
47+ sendSpy = spyOn ( XMLHttpRequest . prototype , 'send' ) ;
2848 spyOn ( XMLHttpRequest . prototype , 'setRequestHeader' ) ;
2949 realOcwAgent = windowWithOcwGlobals . ocwAgent ;
50+ realTraceparent = windowWithOcwGlobals . traceparent ;
3051 } ) ;
3152 afterEach ( ( ) => {
3253 jasmine . clock ( ) . uninstall ( ) ;
3354 windowWithOcwGlobals . ocwAgent = realOcwAgent ;
55+ windowWithOcwGlobals . traceparent = realTraceparent ;
3456 } ) ;
3557
3658 it ( 'exports spans to agent if agent is configured' , ( ) => {
3759 windowWithOcwGlobals . ocwAgent = 'http://agent' ;
60+ windowWithOcwGlobals . traceparent = undefined ;
61+
3862 exportRootSpanAfterLoadEvent ( ) ;
63+
3964 jasmine . clock ( ) . tick ( 300000 ) ;
4065 expect ( XMLHttpRequest . prototype . open )
4166 . toHaveBeenCalledWith ( 'POST' , 'http://agent/v1/trace' ) ;
@@ -44,7 +69,40 @@ describe('exportRootSpanAfterLoadEvent', () => {
4469
4570 it ( 'does not export if agent not configured' , ( ) => {
4671 windowWithOcwGlobals . ocwAgent = undefined ;
72+ windowWithOcwGlobals . traceparent = undefined ;
73+
4774 exportRootSpanAfterLoadEvent ( ) ;
75+
76+ jasmine . clock ( ) . tick ( 300000 ) ;
77+ expect ( XMLHttpRequest . prototype . open ) . not . toHaveBeenCalled ( ) ;
78+ expect ( XMLHttpRequest . prototype . send ) . not . toHaveBeenCalled ( ) ;
79+ } ) ;
80+
81+ it ( 'uses trace and span ID from window.traceparent if specified' , ( ) => {
82+ windowWithOcwGlobals . ocwAgent = 'http://agent' ;
83+ const traceId = '0af7651916cd43dd8448eb211c80319c' ;
84+ const spanId = 'b7ad6b7169203331' ;
85+ windowWithOcwGlobals . traceparent = `00-${ traceId } -${ spanId } -01` ;
86+
87+ exportRootSpanAfterLoadEvent ( ) ;
88+
89+ jasmine . clock ( ) . tick ( 300000 ) ;
90+ expect ( XMLHttpRequest . prototype . open )
91+ . toHaveBeenCalledWith ( 'POST' , 'http://agent/v1/trace' ) ;
92+ expect ( XMLHttpRequest . prototype . send ) . toHaveBeenCalledTimes ( 1 ) ;
93+ // Check that trace and span ID from `window.traceparent` are in body sent.
94+ const sendBody = sendSpy . calls . argsFor ( 0 ) [ 0 ] ;
95+ expect ( sendBody ) . toContain ( hexToBase64 ( traceId ) ) ;
96+ expect ( sendBody ) . toContain ( hexToBase64 ( traceId ) ) ;
97+ } ) ;
98+
99+ it ( 'does not export spans if traceparent sampling hint not set' , ( ) => {
100+ windowWithOcwGlobals . ocwAgent = 'http://agent' ;
101+ windowWithOcwGlobals . traceparent =
102+ '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-00' ;
103+
104+ exportRootSpanAfterLoadEvent ( ) ;
105+
48106 jasmine . clock ( ) . tick ( 300000 ) ;
49107 expect ( XMLHttpRequest . prototype . open ) . not . toHaveBeenCalled ( ) ;
50108 expect ( XMLHttpRequest . prototype . send ) . not . toHaveBeenCalled ( ) ;
0 commit comments