@@ -1525,6 +1525,133 @@ describe('ListWatchCache', () => {
15251525
15261526 mockAgent . assertNoPendingInterceptors ( ) ;
15271527 } ) ;
1528+
1529+ it ( 'should apply exponential backoff on repeated reconnects' , async ( ) => {
1530+ const fakeWatch = mock . mock ( Watch ) ;
1531+ const listObj = {
1532+ metadata : { resourceVersion : '12345' } as V1ListMeta ,
1533+ items : [ ] as V1Namespace [ ] ,
1534+ } as V1NamespaceList ;
1535+
1536+ const listFn : ListPromise < V1Namespace > = ( ) => Promise . resolve ( listObj ) ;
1537+
1538+ let watchCalls = 0 ;
1539+ const delayValues : number [ ] = [ ] ;
1540+ const promise = new Promise ( ( resolve ) => {
1541+ mock . when (
1542+ fakeWatch . watch ( mock . anything ( ) , mock . anything ( ) , mock . anything ( ) , mock . anything ( ) ) ,
1543+ ) . thenCall ( ( ) => {
1544+ watchCalls ++ ;
1545+ resolve ( new AbortController ( ) ) ;
1546+ return Promise . resolve ( new AbortController ( ) ) ;
1547+ } ) ;
1548+ } ) ;
1549+
1550+ const cache = new ListWatch ( '/some/path' , mock . instance ( fakeWatch ) , listFn ) ;
1551+ ( cache as any ) . delayFn = ( ms : number ) => {
1552+ delayValues . push ( ms ) ;
1553+ return Promise . resolve ( ) ;
1554+ } ;
1555+ await promise ;
1556+ strictEqual ( watchCalls , 1 ) ;
1557+
1558+ const [ , , , doneHandler ] = mock . capture ( fakeWatch . watch ) . last ( ) ;
1559+
1560+ await doneHandler ( null ) ;
1561+ strictEqual ( watchCalls , 2 ) ;
1562+ deepStrictEqual ( delayValues , [ ] ) ;
1563+
1564+ await doneHandler ( null ) ;
1565+ strictEqual ( watchCalls , 3 ) ;
1566+ deepStrictEqual ( delayValues , [ 1000 ] ) ;
1567+
1568+ await doneHandler ( null ) ;
1569+ strictEqual ( watchCalls , 4 ) ;
1570+ deepStrictEqual ( delayValues , [ 1000 , 2000 ] ) ;
1571+
1572+ await doneHandler ( null ) ;
1573+ strictEqual ( watchCalls , 5 ) ;
1574+ deepStrictEqual ( delayValues , [ 1000 , 2000 , 4000 ] ) ;
1575+ } ) ;
1576+
1577+ it ( 'should reset backoff after receiving a watch event' , async ( ) => {
1578+ const fakeWatch = mock . mock ( Watch ) ;
1579+ const listObj = {
1580+ metadata : { resourceVersion : '12345' } as V1ListMeta ,
1581+ items : [ ] as V1Namespace [ ] ,
1582+ } as V1NamespaceList ;
1583+
1584+ const listFn : ListPromise < V1Namespace > = ( ) => Promise . resolve ( listObj ) ;
1585+
1586+ const delayValues : number [ ] = [ ] ;
1587+ const promise = new Promise ( ( resolve ) => {
1588+ mock . when (
1589+ fakeWatch . watch ( mock . anything ( ) , mock . anything ( ) , mock . anything ( ) , mock . anything ( ) ) ,
1590+ ) . thenCall ( ( ) => {
1591+ resolve ( new AbortController ( ) ) ;
1592+ return Promise . resolve ( new AbortController ( ) ) ;
1593+ } ) ;
1594+ } ) ;
1595+
1596+ const cache = new ListWatch ( '/some/path' , mock . instance ( fakeWatch ) , listFn ) ;
1597+ ( cache as any ) . delayFn = ( ms : number ) => {
1598+ delayValues . push ( ms ) ;
1599+ return Promise . resolve ( ) ;
1600+ } ;
1601+ await promise ;
1602+
1603+ const [ , , watchHandler , doneHandler ] = mock . capture ( fakeWatch . watch ) . last ( ) ;
1604+
1605+ await doneHandler ( null ) ;
1606+ await doneHandler ( null ) ;
1607+ deepStrictEqual ( delayValues , [ 1000 ] ) ;
1608+
1609+ watchHandler ( 'ADDED' , {
1610+ metadata : { name : 'reset' , namespace : 'default' , resourceVersion : '99' } as V1ObjectMeta ,
1611+ } as V1Namespace ) ;
1612+
1613+ delayValues . length = 0 ;
1614+ await doneHandler ( null ) ;
1615+ deepStrictEqual ( delayValues , [ ] ) ;
1616+
1617+ await doneHandler ( null ) ;
1618+ deepStrictEqual ( delayValues , [ 1000 ] ) ;
1619+ } ) ;
1620+
1621+ it ( 'should reconnect with backoff on TimeoutError' , async ( ) => {
1622+ const fakeWatch = mock . mock ( Watch ) ;
1623+ const listObj = {
1624+ metadata : { resourceVersion : '12345' } as V1ListMeta ,
1625+ items : [ ] as V1Namespace [ ] ,
1626+ } as V1NamespaceList ;
1627+
1628+ const listFn : ListPromise < V1Namespace > = ( ) => Promise . resolve ( listObj ) ;
1629+
1630+ let watchCalls = 0 ;
1631+ let errorEmitted = false ;
1632+ const promise = new Promise ( ( resolve ) => {
1633+ mock . when (
1634+ fakeWatch . watch ( mock . anything ( ) , mock . anything ( ) , mock . anything ( ) , mock . anything ( ) ) ,
1635+ ) . thenCall ( ( ) => {
1636+ watchCalls ++ ;
1637+ resolve ( new AbortController ( ) ) ;
1638+ return Promise . resolve ( new AbortController ( ) ) ;
1639+ } ) ;
1640+ } ) ;
1641+
1642+ const cache = new ListWatch ( '/some/path' , mock . instance ( fakeWatch ) , listFn ) ;
1643+ ( cache as any ) . delayFn = ( ) => Promise . resolve ( ) ;
1644+ await promise ;
1645+ cache . on ( 'error' , ( ) => ( errorEmitted = true ) ) ;
1646+ strictEqual ( watchCalls , 1 ) ;
1647+
1648+ const [ , , , doneHandler ] = mock . capture ( fakeWatch . watch ) . last ( ) ;
1649+
1650+ const timeoutError = new DOMException ( 'The operation was aborted due to timeout' , 'TimeoutError' ) ;
1651+ await doneHandler ( timeoutError ) ;
1652+ strictEqual ( watchCalls , 2 ) ;
1653+ strictEqual ( errorEmitted , false ) ;
1654+ } ) ;
15281655} ) ;
15291656
15301657describe ( 'delete items' , ( ) => {
0 commit comments