mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-11 00:32:43 +08:00
fix: add actionable DingTalk connection diagnostics
This commit is contained in:
parent
3010535409
commit
1b05fa8d32
18 changed files with 1038 additions and 299 deletions
80
test/channels/dingtalk/connection-error.test.mjs
Normal file
80
test/channels/dingtalk/connection-error.test.mjs
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import {
|
||||
describeDingtalkConnectionFailure,
|
||||
dingtalkPublicConnectionError,
|
||||
dingtalkRuntimeStartError,
|
||||
installedDingtalkConnectionDependencies,
|
||||
} from '../../../src/channels/dingtalk/connection-error.mjs';
|
||||
|
||||
const FIXED_REFERENCE = 'DT-CONN-DEADBEEF';
|
||||
|
||||
test('connection diagnostics identify the pnpm mirror proxy dependency failure without leaking values', () => {
|
||||
const cause = new Error(
|
||||
'request for client-id-private failed via https://name:password@proxy.example because clientSecret=secret-private',
|
||||
);
|
||||
cause.name = 'AxiosError';
|
||||
cause.code = 'ECONNRESET';
|
||||
cause.response = { status: 502, data: { code: 'GatewayFailure' } };
|
||||
const error = dingtalkRuntimeStartError('dingtalk-stream-connect-failed', cause);
|
||||
|
||||
const failure = describeDingtalkConnectionFailure(error, {
|
||||
clientId: 'client-id-private',
|
||||
clientSecret: 'secret-private',
|
||||
environment: { HTTPS_PROXY: 'https://name:password@proxy.example' },
|
||||
dependencies: {
|
||||
dingtalkStream: '2.1.4',
|
||||
axios: '1.19.0',
|
||||
httpsProxyAgent: '5.0.1',
|
||||
agentBase: '6.0.0',
|
||||
},
|
||||
nodeVersion: '24.19.0',
|
||||
referenceId: FIXED_REFERENCE,
|
||||
});
|
||||
|
||||
assert.deepEqual(failure.publicError, {
|
||||
code: 'stream-proxy-dependency-incompatible',
|
||||
message: '钉钉 Stream 连接失败:检测到代理依赖 agent-base 6.0.0。',
|
||||
hint: '请将 DSH profile 中的 agent-base@6 固定为 6.0.2 后重新安装依赖,或升级 pnpm 后重新解析 lockfile。',
|
||||
referenceId: FIXED_REFERENCE,
|
||||
});
|
||||
assert.equal(failure.diagnostic.stage, 'dingtalk-stream-connect-failed');
|
||||
assert.deepEqual(failure.diagnostic.proxy, {
|
||||
configured: true,
|
||||
variables: ['HTTPS_PROXY'],
|
||||
});
|
||||
assert.equal(failure.diagnostic.dependencies.agentBase, '6.0.0');
|
||||
assert.equal(failure.diagnostic.errors.at(-1).providerCode, 'GatewayFailure');
|
||||
assert.doesNotMatch(
|
||||
JSON.stringify(failure.diagnostic),
|
||||
/client-id-private|secret-private|name:password/,
|
||||
);
|
||||
});
|
||||
|
||||
test('connection diagnostics classify common stages and keep a public-only RPC projection', () => {
|
||||
const cause = Object.assign(new Error('getaddrinfo ENOTFOUND api.dingtalk.com'), {
|
||||
code: 'ENOTFOUND',
|
||||
});
|
||||
const staged = dingtalkRuntimeStartError('dingtalk-stream-connect-failed', cause);
|
||||
const failure = describeDingtalkConnectionFailure(staged, {
|
||||
environment: {},
|
||||
dependencies: { agentBase: '6.0.2' },
|
||||
referenceId: FIXED_REFERENCE,
|
||||
});
|
||||
|
||||
assert.equal(failure.publicError.code, 'stream-dns-failed');
|
||||
assert.match(failure.publicError.message, /无法解析/);
|
||||
const outward = dingtalkPublicConnectionError(failure.publicError, staged);
|
||||
assert.equal(outward.name, 'DingtalkPublicConnectionError');
|
||||
assert.deepEqual(outward.publicError, failure.publicError);
|
||||
assert.equal(outward.cause, staged);
|
||||
});
|
||||
|
||||
test('installed connection diagnostics follow the DingTalk dependency chain', () => {
|
||||
const versions = installedDingtalkConnectionDependencies();
|
||||
assert.match(versions.dingtalkStream, /^\d+\.\d+\.\d+/);
|
||||
assert.match(versions.axios, /^\d+\.\d+\.\d+/);
|
||||
assert.match(versions.httpsProxyAgent, /^\d+\.\d+\.\d+/);
|
||||
assert.match(versions.agentBase, /^6\./);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue