Skip to content

Commit 18df157

Browse files
committed
tests: added tests to check for QUICStreams cleaning up properly
* Fixes #66 [ci skip]
1 parent ea81c26 commit 18df157

File tree

1 file changed

+274
-1
lines changed

1 file changed

+274
-1
lines changed

tests/QUICStream.test.ts

Lines changed: 274 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ describe(QUICStream.name, () => {
2727
randomBytes: testsUtils.randomBytes,
2828
};
2929
let socketCleanMethods: ReturnType<typeof testsUtils.socketCleanupFactory>;
30+
let createQUICStreamMock: jest.SpyInstance;
3031

3132
const testReason = Symbol('TestReason');
3233
const testCodeToReason = (type, code) => {
@@ -46,8 +47,10 @@ describe(QUICStream.name, () => {
4647
beforeEach(async () => {
4748
key = await testsUtils.generateKeyHMAC();
4849
socketCleanMethods = testsUtils.socketCleanupFactory();
50+
createQUICStreamMock = jest.spyOn(QUICStream, 'createQUICStream');
4951
});
5052
afterEach(async () => {
53+
createQUICStreamMock.mockRestore();
5154
await socketCleanMethods.stopSockets();
5255
});
5356

@@ -1235,7 +1238,6 @@ describe(QUICStream.name, () => {
12351238
await client.destroy({ force: true });
12361239
await server.stop({ force: true });
12371240
});
1238-
12391241
test('streams are allowed to end when client is destroyed with force: false', async () => {
12401242
const message = Buffer.from('The Quick Brown Fox Jumped Over The Lazy Dog');
12411243
const numStreams = 10;
@@ -1576,4 +1578,275 @@ describe(QUICStream.name, () => {
15761578
waitResolveP();
15771579
await waitP;
15781580
});
1581+
test('quicStream properly cleans up after cancelling without data sent', async () => {
1582+
const cancelReason = Symbol('CancelReason');
1583+
const connectionEventProm =
1584+
utils.promise<events.EventQUICServerConnection>();
1585+
const tlsConfig1 = await generateTLSConfig(defaultType);
1586+
const tlsConfig2 = await generateTLSConfig(defaultType);
1587+
const reasonConverters = testsUtils.createReasonConverters();
1588+
const server = new QUICServer({
1589+
crypto: {
1590+
key,
1591+
ops: serverCrypto,
1592+
},
1593+
logger: logger.getChild(QUICServer.name),
1594+
config: {
1595+
key: tlsConfig1.leafKeyPairPEM.privateKey,
1596+
cert: tlsConfig1.leafCertPEM,
1597+
verifyPeer: true,
1598+
ca: tlsConfig2.caCertPEM,
1599+
},
1600+
...reasonConverters,
1601+
});
1602+
socketCleanMethods.extractSocket(server);
1603+
server.addEventListener(
1604+
events.EventQUICServerConnection.name,
1605+
(e: events.EventQUICServerConnection) => connectionEventProm.resolveP(e),
1606+
);
1607+
await server.start({
1608+
host: localhost,
1609+
});
1610+
const client = await QUICClient.createQUICClient({
1611+
host: localhost,
1612+
port: server.port,
1613+
localHost: localhost,
1614+
crypto: {
1615+
ops: clientCrypto,
1616+
},
1617+
logger: logger.getChild(QUICClient.name),
1618+
config: {
1619+
verifyPeer: false,
1620+
key: tlsConfig2.leafKeyPairPEM.privateKey,
1621+
cert: tlsConfig2.leafCertPEM,
1622+
},
1623+
...reasonConverters,
1624+
});
1625+
socketCleanMethods.extractSocket(client);
1626+
const serverConnection = (await connectionEventProm.p).detail;
1627+
1628+
// Do the test
1629+
const { p: serverStreamP, resolveP: serverStreamResolveP } =
1630+
utils.promise<QUICStream>();
1631+
serverConnection.addEventListener(
1632+
events.EventQUICConnectionStream.name,
1633+
(event: events.EventQUICConnectionStream) => {
1634+
serverStreamResolveP(event.detail);
1635+
},
1636+
);
1637+
// Let's make a new stream.
1638+
const clientStream = client.connection.newStream();
1639+
const writer = clientStream.writable.getWriter();
1640+
// Await writer.write(message);
1641+
writer.releaseLock();
1642+
clientStream.cancel(cancelReason);
1643+
await expect(clientStream.readable.getReader().read()).rejects.toBe(
1644+
cancelReason,
1645+
);
1646+
await expect(clientStream.writable.getWriter().write()).rejects.toBe(
1647+
cancelReason,
1648+
);
1649+
1650+
// Let's check that the server side ended
1651+
const serverStream = await serverStreamP;
1652+
const serverReadProm = (async () => {
1653+
for await (const _ of serverStream.readable) {
1654+
// Just consume until stream throws
1655+
}
1656+
})();
1657+
await expect(serverReadProm).rejects.toBe(cancelReason);
1658+
const serverWriter = serverStream.writable.getWriter();
1659+
// Should throw
1660+
await expect(serverWriter.write(Buffer.from('hello'))).rejects.toBe(
1661+
cancelReason,
1662+
);
1663+
1664+
// And client stream should've cleaned up
1665+
await testsUtils.sleep(100);
1666+
// Only two streams should've been created
1667+
expect(createQUICStreamMock).toHaveBeenCalledTimes(2);
1668+
1669+
expect(clientStream[destroyed]).toBeTrue();
1670+
await client.destroy({ force: true });
1671+
await server.stop({ force: true });
1672+
});
1673+
test('quicStream properly cleans up after cancelling with data sent', async () => {
1674+
const cancelReason = Symbol('CancelReason');
1675+
const connectionEventProm =
1676+
utils.promise<events.EventQUICServerConnection>();
1677+
const tlsConfig1 = await generateTLSConfig(defaultType);
1678+
const tlsConfig2 = await generateTLSConfig(defaultType);
1679+
const reasonConverters = testsUtils.createReasonConverters();
1680+
const server = new QUICServer({
1681+
crypto: {
1682+
key,
1683+
ops: serverCrypto,
1684+
},
1685+
logger: logger.getChild(QUICServer.name),
1686+
config: {
1687+
key: tlsConfig1.leafKeyPairPEM.privateKey,
1688+
cert: tlsConfig1.leafCertPEM,
1689+
verifyPeer: true,
1690+
ca: tlsConfig2.caCertPEM,
1691+
},
1692+
...reasonConverters,
1693+
});
1694+
socketCleanMethods.extractSocket(server);
1695+
server.addEventListener(
1696+
events.EventQUICServerConnection.name,
1697+
(e: events.EventQUICServerConnection) => connectionEventProm.resolveP(e),
1698+
);
1699+
await server.start({
1700+
host: localhost,
1701+
});
1702+
const client = await QUICClient.createQUICClient({
1703+
host: localhost,
1704+
port: server.port,
1705+
localHost: localhost,
1706+
crypto: {
1707+
ops: clientCrypto,
1708+
},
1709+
logger: logger.getChild(QUICClient.name),
1710+
config: {
1711+
verifyPeer: false,
1712+
key: tlsConfig2.leafKeyPairPEM.privateKey,
1713+
cert: tlsConfig2.leafCertPEM,
1714+
},
1715+
...reasonConverters,
1716+
});
1717+
socketCleanMethods.extractSocket(client);
1718+
const serverConnection = (await connectionEventProm.p).detail;
1719+
1720+
// Do the test
1721+
const { p: serverStreamP, resolveP: serverStreamResolveP } =
1722+
utils.promise<QUICStream>();
1723+
serverConnection.addEventListener(
1724+
events.EventQUICConnectionStream.name,
1725+
(event: events.EventQUICConnectionStream) => {
1726+
serverStreamResolveP(event.detail);
1727+
},
1728+
);
1729+
// Let's make a new stream.
1730+
const message = Buffer.from('Hello!');
1731+
const clientStream = client.connection.newStream();
1732+
const writer = clientStream.writable.getWriter();
1733+
await writer.write(message);
1734+
writer.releaseLock();
1735+
clientStream.cancel(cancelReason);
1736+
await expect(clientStream.readable.getReader().read()).rejects.toBe(
1737+
cancelReason,
1738+
);
1739+
await expect(clientStream.writable.getWriter().write()).rejects.toBe(
1740+
cancelReason,
1741+
);
1742+
1743+
// Let's check that the server side ended
1744+
const serverStream = await serverStreamP;
1745+
const serverReadProm = (async () => {
1746+
for await (const _ of serverStream.readable) {
1747+
// Just consume until stream throws
1748+
}
1749+
})();
1750+
await expect(serverReadProm).rejects.toBe(cancelReason);
1751+
const serverWriter = serverStream.writable.getWriter();
1752+
// Should throw
1753+
await expect(serverWriter.write(Buffer.from('hello'))).rejects.toBe(
1754+
cancelReason,
1755+
);
1756+
1757+
// And client stream should've cleaned up
1758+
await testsUtils.sleep(100);
1759+
// Only two streams should've been created
1760+
expect(createQUICStreamMock).toHaveBeenCalledTimes(2);
1761+
1762+
expect(clientStream[destroyed]).toBeTrue();
1763+
await client.destroy({ force: true });
1764+
await server.stop({ force: true });
1765+
});
1766+
test('quicStream properly cleans up after graceful end with data sent', async () => {
1767+
const connectionEventProm =
1768+
utils.promise<events.EventQUICServerConnection>();
1769+
const tlsConfig1 = await generateTLSConfig(defaultType);
1770+
const tlsConfig2 = await generateTLSConfig(defaultType);
1771+
const reasonConverters = testsUtils.createReasonConverters();
1772+
const server = new QUICServer({
1773+
crypto: {
1774+
key,
1775+
ops: serverCrypto,
1776+
},
1777+
logger: logger.getChild(QUICServer.name),
1778+
config: {
1779+
key: tlsConfig1.leafKeyPairPEM.privateKey,
1780+
cert: tlsConfig1.leafCertPEM,
1781+
verifyPeer: true,
1782+
ca: tlsConfig2.caCertPEM,
1783+
},
1784+
...reasonConverters,
1785+
});
1786+
socketCleanMethods.extractSocket(server);
1787+
server.addEventListener(
1788+
events.EventQUICServerConnection.name,
1789+
(e: events.EventQUICServerConnection) => connectionEventProm.resolveP(e),
1790+
);
1791+
await server.start({
1792+
host: localhost,
1793+
});
1794+
const client = await QUICClient.createQUICClient({
1795+
host: localhost,
1796+
port: server.port,
1797+
localHost: localhost,
1798+
crypto: {
1799+
ops: clientCrypto,
1800+
},
1801+
logger: logger.getChild(QUICClient.name),
1802+
config: {
1803+
verifyPeer: false,
1804+
key: tlsConfig2.leafKeyPairPEM.privateKey,
1805+
cert: tlsConfig2.leafCertPEM,
1806+
},
1807+
...reasonConverters,
1808+
});
1809+
socketCleanMethods.extractSocket(client);
1810+
const serverConnection = (await connectionEventProm.p).detail;
1811+
1812+
// Do the test
1813+
const { p: serverStreamP, resolveP: serverStreamResolveP } =
1814+
utils.promise<QUICStream>();
1815+
serverConnection.addEventListener(
1816+
events.EventQUICConnectionStream.name,
1817+
(event: events.EventQUICConnectionStream) => {
1818+
serverStreamResolveP(event.detail);
1819+
},
1820+
);
1821+
// Let's make a new stream.
1822+
const message = Buffer.from('Hello!');
1823+
const clientStream = client.connection.newStream();
1824+
const writer = clientStream.writable.getWriter();
1825+
await writer.write(message);
1826+
await writer.close();
1827+
1828+
// Let's check that the server side ended
1829+
const serverStream = await serverStreamP;
1830+
const serverReadP = (async () => {
1831+
const writer = serverStream.writable.getWriter();
1832+
await writer.write(message);
1833+
await writer.close();
1834+
for await (const _ of serverStream.readable) {
1835+
// Just consume until finish
1836+
}
1837+
})();
1838+
for await (const _ of clientStream.readable) {
1839+
// Just consume until finish
1840+
}
1841+
await serverReadP;
1842+
1843+
// And client stream should've cleaned up
1844+
await testsUtils.sleep(100);
1845+
// Only two streams should've been created
1846+
expect(createQUICStreamMock).toHaveBeenCalledTimes(2);
1847+
1848+
expect(clientStream[destroyed]).toBeTrue();
1849+
await client.destroy({ force: true });
1850+
await server.stop({ force: true });
1851+
});
15791852
});

0 commit comments

Comments
 (0)