|
| 1 | +import { fork } from 'child_process'; |
1 | 2 | import { |
2 | 3 | MongoClient, |
3 | 4 | } from '../../mongodb'; |
| 5 | +import { on, once } from 'node:events'; |
| 6 | +import { readFile, unlink, writeFile } from 'node:fs/promises'; |
| 7 | +import { TestConfiguration } from '../../tools/runner/config'; |
| 8 | +import { expect } from 'chai'; |
| 9 | +import { StringOrPlaceholder } from '../../tools/unified-spec-runner/schema'; |
4 | 10 |
|
5 | 11 | /* |
6 | | -async function runWithProcessAndCheck(_fn) { |
7 | | - start process |
8 | | - Run fn in process |
9 | | - Assert no resources |
10 | | - Close process |
11 | | -} |
| 12 | +export async function testScriptFactory( |
| 13 | + name: string, |
| 14 | + uri: string, |
| 15 | + iterations: number, |
| 16 | + func: Function |
| 17 | +) { |
| 18 | + let resourceScript = await readFile(RESOURCE_SCRIPT_PATH, { encoding: 'utf8' }); |
| 19 | +
|
| 20 | + resourceScript = resourceScript.replace('DRIVER_SOURCE_PATH', DRIVER_SRC_PATH); |
| 21 | + resourceScript = resourceScript.replace('FUNCTION_STRING', `(${func.toString()})`); |
| 22 | + resourceScript = resourceScript.replace('NAME_STRING', JSON.stringify(name)); |
| 23 | + resourceScript = resourceScript.replace('URI_STRING', JSON.stringify(uri)); |
| 24 | + resourceScript = resourceScript.replace('ITERATIONS_STRING', `${iterations}`); |
| 25 | +
|
| 26 | + return resourceScript; |
| 27 | +} |
| 28 | +
|
| 29 | +export async function runScriptAndReturnResourceInfo( |
| 30 | + name: string, |
| 31 | + config: TestConfiguration, |
| 32 | + func: Function |
| 33 | +) { |
| 34 | +
|
| 35 | + const pathName = `scripts/${name}.cjs`; |
| 36 | + const scriptContent = await testScriptFactory(name, config.url(), func); |
| 37 | + await writeFile(name, func.toString(), { encoding: 'utf8' }); |
| 38 | +
|
| 39 | + const processDiedController = new AbortController(); |
| 40 | + const script = fork(name, { execArgv: ['--expose-gc'] }); |
| 41 | +
|
| 42 | + // Interrupt our awaiting of messages if the process crashed |
| 43 | + script.once('close', exitCode => { |
| 44 | + if (exitCode !== 0) { |
| 45 | + processDiedController.abort(new Error(`process exited with: ${exitCode}`)); |
| 46 | + } |
| 47 | + }); |
| 48 | +
|
| 49 | + const willClose = once(script, 'close'); |
| 50 | +
|
| 51 | + // make sure the process ended |
| 52 | + const [exitCode] = await willClose; |
| 53 | + expect(exitCode, 'process should have exited with zero').to.equal(0); |
| 54 | +
|
| 55 | + return process.report.getReport().libuv; |
| 56 | +} |
12 | 57 | */ |
13 | 58 |
|
14 | | -describe.only('client.close() Resource Management Integration tests', () => { |
| 59 | +describe.only('client.close() Integration', () => { |
15 | 60 | let client: MongoClient; |
| 61 | + let config: TestConfiguration; |
16 | 62 | beforeEach(function () { |
| 63 | + config = this.configuration; |
17 | 64 | client = this.configuration.newClient(); |
18 | 65 | }); |
19 | 66 |
|
20 | 67 | describe('File System', () => { |
21 | | - context('when client is closed', () => { |
22 | | - context('after client is connected', () => { |
23 | | - it('the TLS file access is cleaned up', () => { |
| 68 | + describe('when client is connected and reading a TLS long file', () => { |
| 69 | + it('the file read is interrupted by client.close', () => { |
24 | 70 |
|
25 | | - }); |
26 | 71 | }); |
27 | | - context('after client is created ', () => { |
28 | | - // our docker env detection uses fs.access which will not be aborted until after it runs |
29 | | - // fs.access does not support abort signals |
30 | | - it('the .docker file access is cleaned up', () => { |
31 | | - |
32 | | - }); |
| 72 | + }); |
| 73 | + describe('when client is created and reading a long docker file', () => { |
| 74 | + // our docker env detection uses fs.access which will not be aborted until after it runs |
| 75 | + // fs.access does not support abort signals |
| 76 | + it('the file read is not interrupted by client.close', () => { |
33 | 77 | }); |
| 78 | + }); |
34 | 79 |
|
35 | | - context('when FLE is enabled', () => { |
36 | | - context('after client has made a KMS request', () => { |
37 | | - it('the TLS file access is cleaned up', () => { |
| 80 | + describe('when FLE is enabled and the client has made a KMS request that is reading a long TLS file', () => { |
| 81 | + it('the file read is interrupted by client.close', () => { |
38 | 82 |
|
39 | | - }); |
40 | | - }); |
41 | 83 | }); |
42 | 84 | }); |
43 | 85 | }); |
44 | 86 |
|
45 | 87 | describe('Connection Creation and Socket Lifetime', () => { |
46 | | - context('when client is closed', () => { |
47 | | - context('after client is connected', () => { |
48 | | - it('the socket is cleaned up', () => { |
| 88 | + describe('after client is connected', () => { |
| 89 | + it('no sockets remain after client.close', () => { |
49 | 90 |
|
50 | | - }); |
51 | 91 | }); |
| 92 | + it('no server-side connection threads remain after client.close', () => { |
52 | 93 |
|
53 | | - context('after a connection is checked out', () => { |
54 | | - it('the socket is cleaned up', () => { |
| 94 | + }); |
| 95 | + }); |
| 96 | + |
| 97 | + describe('after a connection is checked out', () => { |
| 98 | + it('no sockets remain after client.close', () => { |
| 99 | + |
| 100 | + }); |
| 101 | + it('no server-side connection threads remain after client.close', () => { |
55 | 102 |
|
56 | | - }); |
57 | 103 | }); |
| 104 | + }); |
58 | 105 |
|
59 | | - context('after a minPoolSize has been set on the ConnectionPool', () => { |
60 | | - it('the socket is cleaned up', () => { |
| 106 | + describe('after a minPoolSize has been set on the ConnectionPool', () => { |
| 107 | + it('no sockets remain after client.close', () => { |
61 | 108 |
|
62 | | - }); |
63 | 109 | }); |
| 110 | + it('no server-side connection threads remain after client.close', () => { |
| 111 | + |
| 112 | + }); |
| 113 | + }); |
| 114 | + |
| 115 | + describe('when connection monitoring is turned on', () => { |
| 116 | + it('no sockets remain after client.close', () => { |
64 | 117 |
|
65 | | - context('when connection monitoring is turned on', () => { |
66 | | - it('the socket is cleaned up', () => { |
| 118 | + }); |
| 119 | + it('no server-side connection threads remain after client.close', () => { |
67 | 120 |
|
68 | | - }); |
69 | 121 | }); |
| 122 | + }); |
70 | 123 |
|
71 | | - context('when rtt monitoring is turned on', () => { |
72 | | - it('the socket is cleaned up', () => { |
| 124 | + describe('when rtt monitoring is turned on', () => { |
| 125 | + it('no sockets remain after client.close', () => { |
73 | 126 |
|
74 | | - }); |
75 | 127 | }); |
| 128 | + it('no server-side connection threads remain after client.close', () => { |
| 129 | + |
| 130 | + }); |
| 131 | + }); |
76 | 132 |
|
77 | | - context('when FLE is enabled', () => { |
78 | | - context('after client has made a KMS request', () => { |
79 | | - it('the socket is cleaned up', () => { |
| 133 | + describe('when FLE is enabled and the client has made a KMS request', () => { |
| 134 | + it('no sockets remain after client.close', () => { |
| 135 | + |
| 136 | + }); |
| 137 | + it('no server-side connection threads remain after client.close', () => { |
80 | 138 |
|
81 | | - }); |
82 | | - }); |
83 | 139 | }); |
84 | 140 | }); |
85 | 141 | }); |
86 | 142 |
|
87 | 143 | describe('Timers', () => { |
88 | | - context('when client is closed', () => { |
89 | | - context('after SRVPoller is explicitly created', () => { |
90 | | - it('timers are cleaned up', () => { |
| 144 | + describe('after SRVPoller is explicitly created', () => { |
| 145 | + it('timers are cleaned up by client.close()', () => { |
91 | 146 |
|
92 | | - }); |
93 | 147 | }); |
| 148 | + }); |
94 | 149 |
|
95 | | - // SRVPoller is implicitly created after an SRV string's topology transitions to sharded |
96 | | - context('after SRVPoller is implicitly created', () => { |
97 | | - it('timers are cleaned up', () => { |
| 150 | + // SRVPoller is implicitly created after an SRV string's topology transitions to sharded |
| 151 | + describe('after SRVPoller is implicitly created', () => { |
| 152 | + it('timers are cleaned up by client.close()', () => { |
98 | 153 |
|
99 | | - }); |
100 | 154 | }); |
| 155 | + }); |
101 | 156 |
|
102 | | - context('after new connection pool is created', () => { |
103 | | - it('minPoolSize timer is cleaned up', () => { |
| 157 | + describe('after new connection pool is created', () => { |
| 158 | + it('minPoolSize timer is cleaned up by client.close()', () => { |
104 | 159 |
|
105 | | - }); |
106 | 160 | }); |
| 161 | + }); |
107 | 162 |
|
108 | | - context('after a new monitor is made', () => { |
109 | | - it('monitor interval timer is cleaned up', () => { |
| 163 | + describe('after a new monitor is made', () => { |
| 164 | + it('monitor interval timer is cleaned up by client.close()', () => { |
110 | 165 |
|
111 | | - }); |
112 | 166 | }); |
| 167 | + }); |
113 | 168 |
|
114 | | - context('after a heartbeat fails', () => { |
115 | | - it('monitor interval timer is cleaned up', () => { |
| 169 | + describe('after a heartbeat fails', () => { |
| 170 | + it('monitor interval timer is cleaned up by client.close()', () => { |
116 | 171 |
|
117 | | - }); |
118 | 172 | }); |
| 173 | + }); |
119 | 174 |
|
120 | | - context('after helloReply has a topologyVersion defined fails', () => { |
121 | | - it('rtt pinger timer is cleaned up', () => { |
| 175 | + describe('after helloReply has a topologyVersion defined fails', () => { |
| 176 | + it('rtt pinger timer is cleaned up by client.close()', () => { |
122 | 177 |
|
123 | | - }); |
124 | 178 | }); |
125 | 179 | }); |
126 | 180 | }); |
127 | 181 |
|
128 | 182 | describe('Cursor Clean-up', () => { |
129 | | - context('when client is closed', () => { |
130 | | - context('after cursors are created', () => { |
131 | | - it('closes all active cursors', () => { |
| 183 | + describe('after cursors are created', () => { |
| 184 | + it('all active server-side cursors are closed by client.close()', () => { |
| 185 | + |
| 186 | + }); |
| 187 | + }); |
| 188 | + }); |
| 189 | + |
| 190 | + describe('Sessions', () => { |
| 191 | + describe('after a clientSession is created', () => { |
| 192 | + it('the server-side ServerSession is cleaned up by client.close()', () => { |
132 | 193 |
|
133 | | - }); |
134 | 194 | }); |
135 | 195 | }); |
136 | 196 | }); |
|
0 commit comments