@@ -37,7 +37,11 @@ const {
37
37
TEST_SUITE ,
38
38
TEST_CODE_OWNERS ,
39
39
TEST_SESSION_NAME ,
40
- TEST_LEVEL_EVENT_TYPES
40
+ TEST_LEVEL_EVENT_TYPES ,
41
+ DI_ERROR_DEBUG_INFO_CAPTURED ,
42
+ DI_DEBUG_ERROR_FILE ,
43
+ DI_DEBUG_ERROR_SNAPSHOT_ID ,
44
+ DI_DEBUG_ERROR_LINE
41
45
} = require ( '../../packages/dd-trace/src/plugins/util/test' )
42
46
const { DD_HOST_CPU_COUNT } = require ( '../../packages/dd-trace/src/plugins/util/env' )
43
47
@@ -86,10 +90,11 @@ versions.forEach(version => {
86
90
87
91
reportMethods . forEach ( ( reportMethod ) => {
88
92
context ( `reporting via ${ reportMethod } ` , ( ) => {
89
- let envVars , isAgentless
93
+ let envVars , isAgentless , logsEndpoint
90
94
beforeEach ( ( ) => {
91
95
isAgentless = reportMethod === 'agentless'
92
96
envVars = isAgentless ? getCiVisAgentlessConfig ( receiver . port ) : getCiVisEvpProxyConfig ( receiver . port )
97
+ logsEndpoint = isAgentless ? '/api/v2/logs' : '/debugger/v1/input'
93
98
} )
94
99
const runModes = [ 'serial' ]
95
100
@@ -1536,6 +1541,191 @@ versions.forEach(version => {
1536
1541
} )
1537
1542
} )
1538
1543
} )
1544
+ // Dynamic instrumentation only supported from >=8.0.0
1545
+ context ( 'dynamic instrumentation' , ( ) => {
1546
+ it ( 'does not activate if DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED is not set' , ( done ) => {
1547
+ const eventsPromise = receiver
1548
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url . endsWith ( '/api/v2/citestcycle' ) , ( payloads ) => {
1549
+ const events = payloads . flatMap ( ( { payload } ) => payload . events )
1550
+
1551
+ const tests = events . filter ( event => event . type === 'test' ) . map ( event => event . content )
1552
+ const retriedTests = tests . filter ( test => test . meta [ TEST_IS_RETRY ] === 'true' )
1553
+
1554
+ assert . equal ( retriedTests . length , 1 )
1555
+ const [ retriedTest ] = retriedTests
1556
+
1557
+ assert . notProperty ( retriedTest . meta , DI_ERROR_DEBUG_INFO_CAPTURED )
1558
+ assert . notProperty ( retriedTest . meta , DI_DEBUG_ERROR_FILE )
1559
+ assert . notProperty ( retriedTest . metrics , DI_DEBUG_ERROR_LINE )
1560
+ assert . notProperty ( retriedTest . meta , DI_DEBUG_ERROR_SNAPSHOT_ID )
1561
+ } )
1562
+ const logsPromise = receiver
1563
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url === logsEndpoint , ( payloads ) => {
1564
+ if ( payloads . length > 0 ) {
1565
+ throw new Error ( 'Unexpected logs' )
1566
+ }
1567
+ } , 5000 )
1568
+
1569
+ childProcess = exec (
1570
+ './node_modules/.bin/cucumber-js ci-visibility/features-di/test-hit-breakpoint.feature --retry 1' ,
1571
+ {
1572
+ cwd,
1573
+ env : envVars ,
1574
+ stdio : 'pipe'
1575
+ }
1576
+ )
1577
+
1578
+ childProcess . on ( 'exit' , ( ) => {
1579
+ Promise . all ( [ eventsPromise , logsPromise ] ) . then ( ( ) => {
1580
+ done ( )
1581
+ } ) . catch ( done )
1582
+ } )
1583
+ } )
1584
+
1585
+ it ( 'runs retries with dynamic instrumentation' , ( done ) => {
1586
+ receiver . setSettings ( {
1587
+ itr_enabled : false ,
1588
+ code_coverage : false ,
1589
+ tests_skipping : false ,
1590
+ early_flake_detection : {
1591
+ enabled : false
1592
+ } ,
1593
+ flaky_test_retries_enabled : false
1594
+ } )
1595
+ let snapshotIdByTest , snapshotIdByLog
1596
+ let spanIdByTest , spanIdByLog , traceIdByTest , traceIdByLog
1597
+
1598
+ const eventsPromise = receiver
1599
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url . endsWith ( '/api/v2/citestcycle' ) , payloads => {
1600
+ const events = payloads . flatMap ( ( { payload } ) => payload . events )
1601
+
1602
+ const tests = events . filter ( event => event . type === 'test' ) . map ( event => event . content )
1603
+
1604
+ const retriedTests = tests . filter ( test => test . meta [ TEST_IS_RETRY ] === 'true' )
1605
+
1606
+ assert . equal ( retriedTests . length , 1 )
1607
+ const [ retriedTest ] = retriedTests
1608
+
1609
+ assert . propertyVal ( retriedTest . meta , DI_ERROR_DEBUG_INFO_CAPTURED , 'true' )
1610
+ assert . propertyVal (
1611
+ retriedTest . meta ,
1612
+ DI_DEBUG_ERROR_FILE ,
1613
+ 'ci-visibility/features-di/support/sum.js'
1614
+ )
1615
+ assert . equal ( retriedTest . metrics [ DI_DEBUG_ERROR_LINE ] , 4 )
1616
+ assert . exists ( retriedTest . meta [ DI_DEBUG_ERROR_SNAPSHOT_ID ] )
1617
+
1618
+ snapshotIdByTest = retriedTest . meta [ DI_DEBUG_ERROR_SNAPSHOT_ID ]
1619
+ spanIdByTest = retriedTest . span_id . toString ( )
1620
+ traceIdByTest = retriedTest . trace_id . toString ( )
1621
+ } )
1622
+
1623
+ const logsPromise = receiver
1624
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url === logsEndpoint , ( payloads ) => {
1625
+ const [ { logMessage : [ diLog ] } ] = payloads
1626
+ assert . deepInclude ( diLog , {
1627
+ ddsource : 'dd_debugger' ,
1628
+ level : 'error'
1629
+ } )
1630
+ assert . equal ( diLog . debugger . snapshot . language , 'javascript' )
1631
+ assert . deepInclude ( diLog . debugger . snapshot . captures . lines [ '4' ] . locals , {
1632
+ a : {
1633
+ type : 'number' ,
1634
+ value : '11'
1635
+ } ,
1636
+ b : {
1637
+ type : 'number' ,
1638
+ value : '3'
1639
+ } ,
1640
+ localVariable : {
1641
+ type : 'number' ,
1642
+ value : '2'
1643
+ }
1644
+ } )
1645
+ spanIdByLog = diLog . dd . span_id
1646
+ traceIdByLog = diLog . dd . trace_id
1647
+ snapshotIdByLog = diLog . debugger . snapshot . id
1648
+ } )
1649
+
1650
+ childProcess = exec (
1651
+ './node_modules/.bin/cucumber-js ci-visibility/features-di/test-hit-breakpoint.feature --retry 1' ,
1652
+ {
1653
+ cwd,
1654
+ env : {
1655
+ ...envVars ,
1656
+ DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED : 'true'
1657
+ } ,
1658
+ stdio : 'pipe'
1659
+ }
1660
+ )
1661
+
1662
+ childProcess . on ( 'exit' , ( ) => {
1663
+ Promise . all ( [ eventsPromise , logsPromise ] ) . then ( ( ) => {
1664
+ assert . equal ( snapshotIdByTest , snapshotIdByLog )
1665
+ assert . equal ( spanIdByTest , spanIdByLog )
1666
+ assert . equal ( traceIdByTest , traceIdByLog )
1667
+ done ( )
1668
+ } ) . catch ( done )
1669
+ } )
1670
+ } )
1671
+
1672
+ it ( 'does not crash if the retry does not hit the breakpoint' , ( done ) => {
1673
+ receiver . setSettings ( {
1674
+ itr_enabled : false ,
1675
+ code_coverage : false ,
1676
+ tests_skipping : false ,
1677
+ early_flake_detection : {
1678
+ enabled : false
1679
+ } ,
1680
+ flaky_test_retries_enabled : false
1681
+ } )
1682
+
1683
+ const eventsPromise = receiver
1684
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url . endsWith ( '/api/v2/citestcycle' ) , ( payloads ) => {
1685
+ const events = payloads . flatMap ( ( { payload } ) => payload . events )
1686
+
1687
+ const tests = events . filter ( event => event . type === 'test' ) . map ( event => event . content )
1688
+ const retriedTests = tests . filter ( test => test . meta [ TEST_IS_RETRY ] === 'true' )
1689
+
1690
+ assert . equal ( retriedTests . length , 1 )
1691
+ const [ retriedTest ] = retriedTests
1692
+
1693
+ assert . propertyVal ( retriedTest . meta , DI_ERROR_DEBUG_INFO_CAPTURED , 'true' )
1694
+ assert . propertyVal (
1695
+ retriedTest . meta ,
1696
+ DI_DEBUG_ERROR_FILE ,
1697
+ 'ci-visibility/features-di/support/sum.js'
1698
+ )
1699
+ assert . equal ( retriedTest . metrics [ DI_DEBUG_ERROR_LINE ] , 4 )
1700
+ assert . exists ( retriedTest . meta [ DI_DEBUG_ERROR_SNAPSHOT_ID ] )
1701
+ } )
1702
+ const logsPromise = receiver
1703
+ . gatherPayloadsMaxTimeout ( ( { url } ) => url . endsWith ( '/api/v2/logs' ) , ( payloads ) => {
1704
+ if ( payloads . length > 0 ) {
1705
+ throw new Error ( 'Unexpected logs' )
1706
+ }
1707
+ } , 5000 )
1708
+
1709
+ childProcess = exec (
1710
+ './node_modules/.bin/cucumber-js ci-visibility/features-di/test-not-hit-breakpoint.feature --retry 1' ,
1711
+ {
1712
+ cwd,
1713
+ env : {
1714
+ ...envVars ,
1715
+ DD_TEST_DYNAMIC_INSTRUMENTATION_ENABLED : 'true'
1716
+ } ,
1717
+ stdio : 'pipe'
1718
+ }
1719
+ )
1720
+
1721
+ childProcess . on ( 'exit' , ( exitCode ) => {
1722
+ Promise . all ( [ eventsPromise , logsPromise ] ) . then ( ( ) => {
1723
+ assert . equal ( exitCode , 0 )
1724
+ done ( )
1725
+ } ) . catch ( done )
1726
+ } )
1727
+ } )
1728
+ } )
1539
1729
}
1540
1730
} )
1541
1731
} )
0 commit comments