feat: wecom-client 新增网页授权能力(授权 URL / code 换身份 / 外部联系人显示名)
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import {
|
||||
buildWecomOAuthUrl,
|
||||
clearWecomAccessTokenCacheForTests,
|
||||
createGroupMsgTemplate,
|
||||
exchangeWecomOAuthCode,
|
||||
fetchWecomExternalContactName,
|
||||
listCustomerGroupChats,
|
||||
listExternalContacts,
|
||||
resolveWecomConfig,
|
||||
@@ -605,3 +608,166 @@ test("createGroupMsgTemplate rejects missing sender or empty chat list", async (
|
||||
/客户群列表为空/,
|
||||
);
|
||||
});
|
||||
|
||||
function fakeWecomOAuth(options = {}) {
|
||||
const calls = [];
|
||||
const fetchImpl = async (input, init = {}) => {
|
||||
const url = new URL(String(input));
|
||||
calls.push({ url, init });
|
||||
if (url.pathname.endsWith("/cgi-bin/gettoken")) {
|
||||
return Response.json({
|
||||
errcode: 0,
|
||||
errmsg: "ok",
|
||||
access_token: "token-test",
|
||||
expires_in: 7200,
|
||||
});
|
||||
}
|
||||
if (url.pathname.endsWith("/cgi-bin/auth/getuserinfo")) {
|
||||
return Response.json({
|
||||
errcode: options.oauthErrcode ?? 0,
|
||||
errmsg: options.oauthErrmsg ?? "ok",
|
||||
...(options.userId ? { userid: options.userId } : {}),
|
||||
...(options.externalUserId ? { external_userid: options.externalUserId } : {}),
|
||||
});
|
||||
}
|
||||
if (url.pathname.endsWith("/cgi-bin/externalcontact/get")) {
|
||||
const eid = url.searchParams.get("external_userid");
|
||||
if (options.contactNames && options.contactNames[eid]) {
|
||||
return Response.json({
|
||||
errcode: 0,
|
||||
errmsg: "ok",
|
||||
external_contact: { external_userid: eid, name: options.contactNames[eid] },
|
||||
});
|
||||
}
|
||||
return Response.json({ errcode: 60111, errmsg: "not found" });
|
||||
}
|
||||
return new Response("not found", { status: 404 });
|
||||
};
|
||||
return { calls, fetchImpl };
|
||||
}
|
||||
|
||||
test("buildWecomOAuthUrl assembles authorize URL with encoded params", () => {
|
||||
const url = new URL(
|
||||
buildWecomOAuthUrl({
|
||||
corpId: "corp-test",
|
||||
agentId: "1000001",
|
||||
redirectUri: "https://portal.example.com/koc/?task=abc",
|
||||
state: "t=abc",
|
||||
}),
|
||||
);
|
||||
assert.equal(url.origin, "https://open.weixin.qq.com");
|
||||
assert.equal(url.pathname, "/connect/oauth2/authorize");
|
||||
assert.equal(url.hash, "#wechat_redirect");
|
||||
assert.equal(url.searchParams.get("appid"), "corp-test");
|
||||
assert.equal(url.searchParams.get("agentid"), "1000001");
|
||||
assert.equal(url.searchParams.get("response_type"), "code");
|
||||
assert.equal(url.searchParams.get("scope"), "snsapi_base");
|
||||
assert.equal(url.searchParams.get("state"), "t=abc");
|
||||
assert.equal(
|
||||
url.searchParams.get("redirect_uri"),
|
||||
"https://portal.example.com/koc/?task=abc",
|
||||
);
|
||||
});
|
||||
|
||||
test("exchangeWecomOAuthCode returns external identity and posts code", async () => {
|
||||
const { calls, fetchImpl } = fakeWecomOAuth({ externalUserId: "wmExt123" });
|
||||
const identity = await exchangeWecomOAuthCode(
|
||||
resolveWecomConfig(baseBindings),
|
||||
"code-abc",
|
||||
fetchImpl,
|
||||
);
|
||||
assert.deepEqual(identity, { externalUserId: "wmExt123" });
|
||||
const call = calls.find((c) =>
|
||||
c.url.pathname.endsWith("/cgi-bin/auth/getuserinfo"),
|
||||
);
|
||||
assert.ok(call, "getuserinfo was called");
|
||||
assert.equal(call.url.searchParams.get("access_token"), "token-test");
|
||||
assert.equal(JSON.parse(String(call.init.body)).code, "code-abc");
|
||||
});
|
||||
|
||||
test("exchangeWecomOAuthCode returns internal member userid", async () => {
|
||||
const { fetchImpl } = fakeWecomOAuth({ userId: "zhangsan" });
|
||||
const identity = await exchangeWecomOAuthCode(
|
||||
resolveWecomConfig(baseBindings),
|
||||
"code-abc",
|
||||
fetchImpl,
|
||||
);
|
||||
assert.deepEqual(identity, { userId: "zhangsan" });
|
||||
});
|
||||
|
||||
test("exchangeWecomOAuthCode throws 400 on invalid code (errcode 40029)", async () => {
|
||||
const { fetchImpl } = fakeWecomOAuth({
|
||||
oauthErrcode: 40029,
|
||||
oauthErrmsg: "invalid code",
|
||||
});
|
||||
await assert.rejects(
|
||||
() =>
|
||||
exchangeWecomOAuthCode(
|
||||
resolveWecomConfig(baseBindings),
|
||||
"code-expired",
|
||||
fetchImpl,
|
||||
),
|
||||
(error) => {
|
||||
assert.ok(error instanceof WecomClientError);
|
||||
assert.equal(error.status, 400);
|
||||
return true;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("exchangeWecomOAuthCode throws when provider returns no identity", async () => {
|
||||
const { fetchImpl } = fakeWecomOAuth();
|
||||
await assert.rejects(
|
||||
() =>
|
||||
exchangeWecomOAuthCode(
|
||||
resolveWecomConfig(baseBindings),
|
||||
"code-abc",
|
||||
fetchImpl,
|
||||
),
|
||||
(error) => {
|
||||
assert.ok(error instanceof WecomClientError);
|
||||
assert.equal(error.status, 400);
|
||||
return true;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("exchangeWecomOAuthCode reuses cached access_token", async () => {
|
||||
const { calls, fetchImpl } = fakeWecomOAuth({ externalUserId: "wmExt123" });
|
||||
const config = resolveWecomConfig(baseBindings);
|
||||
await exchangeWecomOAuthCode(config, "code-1", fetchImpl);
|
||||
await exchangeWecomOAuthCode(config, "code-2", fetchImpl);
|
||||
assert.equal(
|
||||
calls.filter((c) => c.url.pathname.endsWith("/cgi-bin/gettoken")).length,
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
test("fetchWecomExternalContactName resolves name and falls back to null", async () => {
|
||||
const okImpl = fakeWecomOAuth({ contactNames: { wmExt123: "张三" } });
|
||||
assert.equal(
|
||||
await fetchWecomExternalContactName(
|
||||
resolveWecomConfig(baseBindings),
|
||||
"wmExt123",
|
||||
okImpl.fetchImpl,
|
||||
),
|
||||
"张三",
|
||||
);
|
||||
const failImpl = fakeWecomOAuth();
|
||||
assert.equal(
|
||||
await fetchWecomExternalContactName(
|
||||
resolveWecomConfig(baseBindings),
|
||||
"wmMissing",
|
||||
failImpl.fetchImpl,
|
||||
),
|
||||
null,
|
||||
);
|
||||
assert.equal(
|
||||
await fetchWecomExternalContactName(
|
||||
resolveWecomConfig({ ...baseBindings, WECOM_SECRET: "" }),
|
||||
"wmExt123",
|
||||
failImpl.fetchImpl,
|
||||
),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user