@@ -24,7 +24,11 @@ const {
24
24
TEST_NAME ,
25
25
TEST_EARLY_FLAKE_ENABLED ,
26
26
TEST_EARLY_FLAKE_ABORT_REASON ,
27
- TEST_SUITE
27
+ TEST_SUITE ,
28
+ DI_ERROR_DEBUG_INFO_CAPTURED ,
29
+ DI_DEBUG_ERROR_FILE ,
30
+ DI_DEBUG_ERROR_LINE ,
31
+ DI_DEBUG_ERROR_SNAPSHOT_ID
28
32
} = require ( '../../packages/dd-trace/src/plugins/util/test' )
29
33
const { DD_HOST_CPU_COUNT } = require ( '../../packages/dd-trace/src/plugins/util/env' )
30
34
@@ -896,5 +900,204 @@ versions.forEach((version) => {
896
900
} )
897
901
} )
898
902
} )
903
+
904
+ // dynamic instrumentation only supported from >=2.0.0
905
+ if ( version === 'latest' ) {
906
+ context ( 'dynamic instrumentation' , ( ) => {
907
+ it ( 'does not activate it if DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED is not set' , ( done ) => {
908
+ receiver . setSettings ( {
909
+ itr_enabled : false ,
910
+ code_coverage : false ,
911
+ tests_skipping : false ,
912
+ flaky_test_retries_enabled : false
913
+ } )
914
+
915
+ const eventsPromise = receiver
916
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url . endsWith ( '/api/v2/citestcycle' ) , ( payloads ) => {
917
+ const events = payloads . flatMap ( ( { payload } ) => payload . events )
918
+
919
+ const tests = events . filter ( event => event . type === 'test' ) . map ( event => event . content )
920
+ const retriedTests = tests . filter ( test => test . meta [ TEST_IS_RETRY ] === 'true' )
921
+
922
+ assert . equal ( retriedTests . length , 1 )
923
+ const [ retriedTest ] = retriedTests
924
+
925
+ assert . notProperty ( retriedTest . meta , DI_ERROR_DEBUG_INFO_CAPTURED )
926
+ assert . notProperty ( retriedTest . meta , DI_DEBUG_ERROR_FILE )
927
+ assert . notProperty ( retriedTest . metrics , DI_DEBUG_ERROR_LINE )
928
+ assert . notProperty ( retriedTest . meta , DI_DEBUG_ERROR_SNAPSHOT_ID )
929
+ } )
930
+
931
+ const logsPromise = receiver
932
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url . endsWith ( '/api/v2/logs' ) , ( payloads ) => {
933
+ if ( payloads . length > 0 ) {
934
+ throw new Error ( 'Unexpected logs' )
935
+ }
936
+ } , 5000 )
937
+
938
+ childProcess = exec (
939
+ './node_modules/.bin/vitest run --retry=1' ,
940
+ {
941
+ cwd,
942
+ env : {
943
+ ...getCiVisAgentlessConfig ( receiver . port ) ,
944
+ TEST_DIR : 'ci-visibility/vitest-tests/dynamic-instrumentation*' ,
945
+ NODE_OPTIONS : '--import dd-trace/register.js -r dd-trace/ci/init'
946
+ } ,
947
+ stdio : 'pipe'
948
+ }
949
+ )
950
+
951
+ childProcess . on ( 'exit' , ( ) => {
952
+ Promise . all ( [ eventsPromise , logsPromise ] ) . then ( ( ) => {
953
+ done ( )
954
+ } ) . catch ( done )
955
+ } )
956
+ } )
957
+
958
+ it ( 'runs retries with dynamic instrumentation' , ( done ) => {
959
+ receiver . setSettings ( {
960
+ itr_enabled : false ,
961
+ code_coverage : false ,
962
+ tests_skipping : false ,
963
+ flaky_test_retries_enabled : false ,
964
+ early_flake_detection : {
965
+ enabled : false
966
+ }
967
+ // di_enabled: true // TODO
968
+ } )
969
+
970
+ let snapshotIdByTest , snapshotIdByLog
971
+ let spanIdByTest , spanIdByLog , traceIdByTest , traceIdByLog
972
+
973
+ const eventsPromise = receiver
974
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url . endsWith ( '/api/v2/citestcycle' ) , ( payloads ) => {
975
+ const events = payloads . flatMap ( ( { payload } ) => payload . events )
976
+
977
+ const tests = events . filter ( event => event . type === 'test' ) . map ( event => event . content )
978
+ const retriedTests = tests . filter ( test => test . meta [ TEST_IS_RETRY ] === 'true' )
979
+
980
+ assert . equal ( retriedTests . length , 1 )
981
+ const [ retriedTest ] = retriedTests
982
+
983
+ assert . propertyVal ( retriedTest . meta , DI_ERROR_DEBUG_INFO_CAPTURED , 'true' )
984
+ assert . propertyVal (
985
+ retriedTest . meta ,
986
+ DI_DEBUG_ERROR_FILE ,
987
+ 'ci-visibility/vitest-tests/bad-sum.mjs'
988
+ )
989
+ assert . equal ( retriedTest . metrics [ DI_DEBUG_ERROR_LINE ] , 4 )
990
+ assert . exists ( retriedTest . meta [ DI_DEBUG_ERROR_SNAPSHOT_ID ] )
991
+
992
+ snapshotIdByTest = retriedTest . meta [ DI_DEBUG_ERROR_SNAPSHOT_ID ]
993
+ spanIdByTest = retriedTest . span_id . toString ( )
994
+ traceIdByTest = retriedTest . trace_id . toString ( )
995
+
996
+ const notRetriedTest = tests . find ( test => test . meta [ TEST_NAME ] . includes ( 'is not retried' ) )
997
+
998
+ assert . notProperty ( notRetriedTest . meta , DI_ERROR_DEBUG_INFO_CAPTURED )
999
+ } )
1000
+
1001
+ const logsPromise = receiver
1002
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url . endsWith ( '/api/v2/logs' ) , ( payloads ) => {
1003
+ const [ { logMessage : [ diLog ] } ] = payloads
1004
+ assert . deepInclude ( diLog , {
1005
+ ddsource : 'dd_debugger' ,
1006
+ level : 'error'
1007
+ } )
1008
+ assert . equal ( diLog . debugger . snapshot . language , 'javascript' )
1009
+ assert . deepInclude ( diLog . debugger . snapshot . captures . lines [ '4' ] . locals , {
1010
+ a : {
1011
+ type : 'number' ,
1012
+ value : '11'
1013
+ } ,
1014
+ b : {
1015
+ type : 'number' ,
1016
+ value : '2'
1017
+ } ,
1018
+ localVar : {
1019
+ type : 'number' ,
1020
+ value : '10'
1021
+ }
1022
+ } )
1023
+ spanIdByLog = diLog . dd . span_id
1024
+ traceIdByLog = diLog . dd . trace_id
1025
+ snapshotIdByLog = diLog . debugger . snapshot . id
1026
+ } , 5000 )
1027
+
1028
+ childProcess = exec (
1029
+ './node_modules/.bin/vitest run --retry=1' ,
1030
+ {
1031
+ cwd,
1032
+ env : {
1033
+ ...getCiVisAgentlessConfig ( receiver . port ) ,
1034
+ TEST_DIR : 'ci-visibility/vitest-tests/dynamic-instrumentation*' ,
1035
+ NODE_OPTIONS : '--import dd-trace/register.js -r dd-trace/ci/init' ,
1036
+ DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED : '1'
1037
+ } ,
1038
+ stdio : 'pipe'
1039
+ }
1040
+ )
1041
+
1042
+ childProcess . on ( 'exit' , ( ) => {
1043
+ Promise . all ( [ eventsPromise , logsPromise ] ) . then ( ( ) => {
1044
+ assert . equal ( snapshotIdByTest , snapshotIdByLog )
1045
+ assert . equal ( spanIdByTest , spanIdByLog )
1046
+ assert . equal ( traceIdByTest , traceIdByLog )
1047
+ done ( )
1048
+ } ) . catch ( done )
1049
+ } )
1050
+ } )
1051
+
1052
+ it ( 'does not crash if the retry does not hit the breakpoint' , ( done ) => {
1053
+ const eventsPromise = receiver
1054
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url . endsWith ( '/api/v2/citestcycle' ) , ( payloads ) => {
1055
+ const events = payloads . flatMap ( ( { payload } ) => payload . events )
1056
+
1057
+ const tests = events . filter ( event => event . type === 'test' ) . map ( event => event . content )
1058
+ const retriedTests = tests . filter ( test => test . meta [ TEST_IS_RETRY ] === 'true' )
1059
+
1060
+ assert . equal ( retriedTests . length , 1 )
1061
+ const [ retriedTest ] = retriedTests
1062
+
1063
+ assert . propertyVal ( retriedTest . meta , DI_ERROR_DEBUG_INFO_CAPTURED , 'true' )
1064
+ assert . propertyVal (
1065
+ retriedTest . meta ,
1066
+ DI_DEBUG_ERROR_FILE ,
1067
+ 'ci-visibility/vitest-tests/bad-sum.mjs'
1068
+ )
1069
+ assert . equal ( retriedTest . metrics [ DI_DEBUG_ERROR_LINE ] , 4 )
1070
+ assert . exists ( retriedTest . meta [ DI_DEBUG_ERROR_SNAPSHOT_ID ] )
1071
+ } )
1072
+
1073
+ const logsPromise = receiver
1074
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url . endsWith ( '/api/v2/logs' ) , ( payloads ) => {
1075
+ if ( payloads . length > 0 ) {
1076
+ throw new Error ( 'Unexpected logs' )
1077
+ }
1078
+ } , 5000 )
1079
+
1080
+ childProcess = exec (
1081
+ './node_modules/.bin/vitest run --retry=1' ,
1082
+ {
1083
+ cwd,
1084
+ env : {
1085
+ ...getCiVisAgentlessConfig ( receiver . port ) ,
1086
+ TEST_DIR : 'ci-visibility/vitest-tests/breakpoint-not-hit*' ,
1087
+ NODE_OPTIONS : '--import dd-trace/register.js -r dd-trace/ci/init' ,
1088
+ DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED : '1'
1089
+ } ,
1090
+ stdio : 'pipe'
1091
+ }
1092
+ )
1093
+
1094
+ childProcess . on ( 'exit' , ( ) => {
1095
+ Promise . all ( [ eventsPromise , logsPromise ] ) . then ( ( ) => {
1096
+ done ( )
1097
+ } ) . catch ( done )
1098
+ } )
1099
+ } )
1100
+ } )
1101
+ }
899
1102
} )
900
1103
} )
0 commit comments