mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 04:13:17 +08:00
125 lines
4.5 KiB
JavaScript
125 lines
4.5 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { execFile } from 'node:child_process';
|
|
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
import { createRequire } from 'node:module';
|
|
import { tmpdir } from 'node:os';
|
|
import { dirname, join, resolve } from 'node:path';
|
|
import test from 'node:test';
|
|
import { promisify } from 'node:util';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
import { build } from 'esbuild';
|
|
|
|
import {
|
|
larkSdkHandshakePatch,
|
|
patchLarkSdkHandshakeSource,
|
|
} from '../../../plugin-src/host/lark-sdk-handshake-patch.mjs';
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const execFileAsync = promisify(execFile);
|
|
const testDirectory = dirname(fileURLToPath(import.meta.url));
|
|
|
|
test('Lark SDK patch applies every reviewed lifecycle fix to both vendor builds', async () => {
|
|
const sdkRoot = dirname(require.resolve('@larksuiteoapi/node-sdk/package.json'));
|
|
|
|
for (const flavor of ['es', 'lib']) {
|
|
const sdkPath = resolve(sdkRoot, flavor, 'index.js');
|
|
const source = await readFile(sdkPath, 'utf8');
|
|
const patched = patchLarkSdkHandshakeSource(source, sdkPath);
|
|
|
|
assert.match(patched, /this\.pendingWsInstance = null;/u);
|
|
assert.match(patched, /this\.pendingWsInstance = wsInstance;/u);
|
|
assert.match(patched, /this\.pendingWsInstance === wsInstance/u);
|
|
assert.match(patched, /wsInstance\.removeAllListeners\(['"]open['"]\);/u);
|
|
assert.match(patched, /pendingWsInstance\.terminate\(\);/u);
|
|
assert.match(
|
|
patched,
|
|
/finally \{\s+if \(currentGeneration === this\.reconnectGeneration\) \{\s+this\.isConnecting = false;/u,
|
|
);
|
|
assert.match(
|
|
patched,
|
|
/if \(this\.terminalError\) \{\s+this\.isConnecting = false;[^]*?if \(this\.isConnecting \|\|[^]*?liveWsInstance\.readyState !== WebSocket\.CLOSED/u,
|
|
);
|
|
assert.match(patched, /currentGeneration !== this\.reconnectGeneration \|\| result\.cancelled/u);
|
|
assert.match(patched, /wsInstance\.on\(['"]error['"]/u);
|
|
assert.doesNotMatch(
|
|
patched,
|
|
/handshake timeout[^]*?wsInstance\.removeAllListeners\(\);[^]*?settleOnce\(false\);/u,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('Lark SDK handshake patch fails closed when the reviewed vendor shape changes', () => {
|
|
assert.throws(
|
|
() => patchLarkSdkHandshakeSource('wsInstance.terminate();', 'changed-sdk.js'),
|
|
/expected exactly one reviewed WSClient pending-socket state marker, found 0/u,
|
|
);
|
|
});
|
|
|
|
test('patched real SDK survives handshake timeout and close without uncaught or zombie reconnects', async () => {
|
|
const temporaryDirectory = await mkdtemp(join(tmpdir(), 'dsh-lark-sdk-test-'));
|
|
const outputPath = join(temporaryDirectory, 'scenario.mjs');
|
|
|
|
try {
|
|
await build({
|
|
entryPoints: [resolve(testDirectory, 'fixtures/lark-sdk-close-during-handshake.mjs')],
|
|
bundle: true,
|
|
format: 'esm',
|
|
platform: 'node',
|
|
target: ['node22'],
|
|
mainFields: ['module', 'main'],
|
|
plugins: [larkSdkHandshakePatch],
|
|
outfile: outputPath,
|
|
logLevel: 'silent',
|
|
banner: {
|
|
js: [
|
|
"import { createRequire as __dshCreateRequire } from 'node:module';",
|
|
"import { dirname as __dshDirname } from 'node:path';",
|
|
"import { fileURLToPath as __dshFileURLToPath } from 'node:url';",
|
|
'const require = __dshCreateRequire(import.meta.url);',
|
|
'const __filename = __dshFileURLToPath(import.meta.url);',
|
|
'const __dirname = __dshDirname(__filename);',
|
|
].join('\n'),
|
|
},
|
|
});
|
|
|
|
const { stdout, stderr } = await execFileAsync(process.execPath, [outputPath], {
|
|
timeout: 5_000,
|
|
});
|
|
|
|
assert.deepEqual(JSON.parse(stdout), {
|
|
handshakeTimeoutPulls: 1,
|
|
closeDuringHandshakePulls: 1,
|
|
immediateRestart: {
|
|
callbackCount: 0,
|
|
endpointPulls: 2,
|
|
stateBeforeFinalClose: 'connecting',
|
|
},
|
|
duplicateStart: {
|
|
acceptedConnections: 1,
|
|
activeSocketsAfterClose: 0,
|
|
activeSocketsBeforeClose: 1,
|
|
callbackCount: 0,
|
|
endpointPulls: 1,
|
|
stateBeforeClose: 'connecting',
|
|
},
|
|
connectedRepeatStart: {
|
|
acceptedConnections: 1,
|
|
activeSocketsAfterClose: 0,
|
|
activeSocketsBeforeClose: 1,
|
|
callbackCount: 1,
|
|
endpointPulls: 1,
|
|
stateBeforeClose: 'connected',
|
|
},
|
|
failedRestart: {
|
|
callbackCountBeforeRestart: 1,
|
|
endpointPulls: 2,
|
|
stateAfterRestart: 'connecting',
|
|
},
|
|
});
|
|
assert.equal(stderr, '');
|
|
}
|
|
finally {
|
|
await rm(temporaryDirectory, { recursive: true, force: true });
|
|
}
|
|
});
|