mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-11 17:20:53 +08:00
fix(feishu): stabilize post-merge card workflows
This commit is contained in:
parent
9d1dc71794
commit
bd9c58cf9e
19 changed files with 3372 additions and 456 deletions
125
test/channels/feishu/lark-sdk-handshake-patch.test.mjs
Normal file
125
test/channels/feishu/lark-sdk-handshake-patch.test.mjs
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
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 });
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue