@openclaw/zalouser 2026.3.13 → 2026.5.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/api.ts +9 -0
- package/channel-plugin-api.ts +3 -0
- package/contract-api.ts +2 -0
- package/doctor-contract-api.ts +1 -0
- package/index.ts +29 -24
- package/openclaw.plugin.json +288 -1
- package/package.json +38 -11
- package/runtime-api.ts +67 -0
- package/secret-contract-api.ts +4 -0
- package/setup-entry.ts +9 -0
- package/setup-plugin-api.ts +2 -0
- package/src/accounts.runtime.ts +1 -0
- package/src/accounts.test-mocks.ts +7 -3
- package/src/accounts.test.ts +53 -1
- package/src/accounts.ts +38 -24
- package/src/channel-api.ts +20 -0
- package/src/channel.adapters.ts +390 -0
- package/src/channel.directory.test.ts +47 -40
- package/src/channel.runtime.ts +12 -0
- package/src/channel.sendpayload.test.ts +41 -23
- package/src/channel.setup.test.ts +33 -0
- package/src/channel.setup.ts +12 -0
- package/src/channel.test.ts +231 -20
- package/src/channel.ts +176 -685
- package/src/config-schema.ts +5 -5
- package/src/directory.ts +54 -0
- package/src/doctor-contract.ts +156 -0
- package/src/doctor.test.ts +77 -0
- package/src/doctor.ts +37 -0
- package/src/group-policy.test.ts +4 -4
- package/src/group-policy.ts +4 -2
- package/src/monitor.account-scope.test.ts +2 -1
- package/src/monitor.group-gating.test.ts +162 -8
- package/src/monitor.ts +233 -173
- package/src/probe.ts +3 -2
- package/src/qr-temp-file.ts +1 -1
- package/src/reaction.ts +5 -2
- package/src/runtime.ts +6 -3
- package/src/security-audit.test.ts +80 -0
- package/src/security-audit.ts +71 -0
- package/src/send.test.ts +2 -2
- package/src/send.ts +3 -3
- package/src/session-route.ts +121 -0
- package/src/setup-core.ts +33 -0
- package/src/setup-surface.test.ts +363 -0
- package/src/setup-surface.ts +470 -0
- package/src/setup-test-helpers.ts +42 -0
- package/src/shared.ts +92 -0
- package/src/status-issues.test.ts +1 -13
- package/src/status-issues.ts +8 -2
- package/src/test-helpers.ts +1 -1
- package/src/text-styles.test.ts +1 -1
- package/src/text-styles.ts +5 -2
- package/src/tool.test.ts +66 -3
- package/src/tool.ts +76 -14
- package/src/types.ts +3 -3
- package/src/zalo-js.credentials.test.ts +465 -0
- package/src/zalo-js.test-mocks.ts +89 -0
- package/src/zalo-js.ts +491 -274
- package/src/zca-client.test.ts +24 -0
- package/src/zca-client.ts +24 -58
- package/src/zca-constants.ts +55 -0
- package/test-api.ts +21 -0
- package/tsconfig.json +16 -0
- package/CHANGELOG.md +0 -107
- package/src/onboarding.ts +0 -340
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
installChannelOutboundPayloadContractSuite,
|
|
3
|
+
primeChannelOutboundSendMock,
|
|
4
|
+
type OutboundPayloadHarnessParams,
|
|
5
|
+
} from "openclaw/plugin-sdk/channel-contract-testing";
|
|
2
6
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
7
|
import "./accounts.test-mocks.js";
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
primeSendMock,
|
|
7
|
-
} from "../../../src/test-utils/send-payload-contract.js";
|
|
8
|
+
import "./zalo-js.test-mocks.js";
|
|
9
|
+
import type { ReplyPayload } from "../runtime-api.js";
|
|
8
10
|
import { zalouserPlugin } from "./channel.js";
|
|
9
11
|
import { setZalouserRuntime } from "./runtime.js";
|
|
12
|
+
import * as sendModule from "./send.js";
|
|
10
13
|
|
|
11
14
|
vi.mock("./send.js", () => ({
|
|
12
15
|
sendMessageZalouser: vi.fn().mockResolvedValue({ ok: true, messageId: "zlu-1" }),
|
|
@@ -25,7 +28,7 @@ function baseCtx(payload: ReplyPayload) {
|
|
|
25
28
|
describe("zalouserPlugin outbound sendPayload", () => {
|
|
26
29
|
let mockedSend: ReturnType<typeof vi.mocked<(typeof import("./send.js"))["sendMessageZalouser"]>>;
|
|
27
30
|
|
|
28
|
-
beforeEach(
|
|
31
|
+
beforeEach(() => {
|
|
29
32
|
setZalouserRuntime({
|
|
30
33
|
channel: {
|
|
31
34
|
text: {
|
|
@@ -34,10 +37,8 @@ describe("zalouserPlugin outbound sendPayload", () => {
|
|
|
34
37
|
},
|
|
35
38
|
},
|
|
36
39
|
} as never);
|
|
37
|
-
|
|
38
|
-
mockedSend
|
|
39
|
-
mockedSend.mockClear();
|
|
40
|
-
mockedSend.mockResolvedValue({ ok: true, messageId: "zlu-1" });
|
|
40
|
+
mockedSend = vi.mocked(sendModule.sendMessageZalouser);
|
|
41
|
+
primeChannelOutboundSendMock(mockedSend, { ok: true, messageId: "zlu-1" });
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
it("group target delegates with isGroup=true and stripped threadId", async () => {
|
|
@@ -110,27 +111,45 @@ describe("zalouserPlugin outbound sendPayload", () => {
|
|
|
110
111
|
);
|
|
111
112
|
expect(result).toMatchObject({ channel: "zalouser", messageId: "zlu-code" });
|
|
112
113
|
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("zalouserPlugin outbound payload contract", () => {
|
|
117
|
+
function createZalouserHarness(params: OutboundPayloadHarnessParams) {
|
|
118
|
+
const mockedSend = vi.mocked(sendModule.sendMessageZalouser);
|
|
119
|
+
setZalouserRuntime({
|
|
120
|
+
channel: {
|
|
121
|
+
text: {
|
|
122
|
+
resolveChunkMode: vi.fn(() => "length"),
|
|
123
|
+
resolveTextChunkLimit: vi.fn(() => 1200),
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
} as never);
|
|
127
|
+
primeChannelOutboundSendMock(mockedSend, { ok: true, messageId: "zlu-1" }, params.sendResults);
|
|
128
|
+
const ctx = {
|
|
129
|
+
cfg: {},
|
|
130
|
+
to: "user:987654321",
|
|
131
|
+
text: "",
|
|
132
|
+
payload: params.payload,
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
run: async () => await zalouserPlugin.outbound!.sendPayload!(ctx),
|
|
136
|
+
sendMock: mockedSend,
|
|
137
|
+
to: "987654321",
|
|
138
|
+
};
|
|
139
|
+
}
|
|
113
140
|
|
|
114
|
-
|
|
141
|
+
installChannelOutboundPayloadContractSuite({
|
|
115
142
|
channel: "zalouser",
|
|
116
143
|
chunking: { mode: "passthrough", longTextLength: 3000 },
|
|
117
|
-
createHarness:
|
|
118
|
-
primeSendMock(mockedSend, { ok: true, messageId: "zlu-1" }, sendResults);
|
|
119
|
-
return {
|
|
120
|
-
run: async () => await zalouserPlugin.outbound!.sendPayload!(baseCtx(payload)),
|
|
121
|
-
sendMock: mockedSend,
|
|
122
|
-
to: "987654321",
|
|
123
|
-
};
|
|
124
|
-
},
|
|
144
|
+
createHarness: createZalouserHarness,
|
|
125
145
|
});
|
|
126
146
|
});
|
|
127
147
|
|
|
128
148
|
describe("zalouserPlugin messaging target normalization", () => {
|
|
129
149
|
it("normalizes user/group aliases to canonical targets", () => {
|
|
130
150
|
const normalize = zalouserPlugin.messaging?.normalizeTarget;
|
|
131
|
-
expect(normalize).toBeTypeOf("function");
|
|
132
151
|
if (!normalize) {
|
|
133
|
-
|
|
152
|
+
throw new Error("normalizeTarget unavailable");
|
|
134
153
|
}
|
|
135
154
|
expect(normalize("zlu:g:30003")).toBe("group:30003");
|
|
136
155
|
expect(normalize("zalouser:u:20002")).toBe("user:20002");
|
|
@@ -141,9 +160,8 @@ describe("zalouserPlugin messaging target normalization", () => {
|
|
|
141
160
|
|
|
142
161
|
it("treats canonical and provider-native user/group targets as ids", () => {
|
|
143
162
|
const looksLikeId = zalouserPlugin.messaging?.targetResolver?.looksLikeId;
|
|
144
|
-
expect(looksLikeId).toBeTypeOf("function");
|
|
145
163
|
if (!looksLikeId) {
|
|
146
|
-
|
|
164
|
+
throw new Error("looksLikeId unavailable");
|
|
147
165
|
}
|
|
148
166
|
expect(looksLikeId("user:20002")).toBe(true);
|
|
149
167
|
expect(looksLikeId("group:30003")).toBe(true);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createPluginSetupWizardStatus } from "openclaw/plugin-sdk/plugin-test-runtime";
|
|
5
|
+
import { withEnvAsync } from "openclaw/plugin-sdk/test-env";
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import "./zalo-js.test-mocks.js";
|
|
8
|
+
import { zalouserSetupPlugin } from "./setup-test-helpers.js";
|
|
9
|
+
|
|
10
|
+
const zalouserSetupGetStatus = createPluginSetupWizardStatus(zalouserSetupPlugin);
|
|
11
|
+
|
|
12
|
+
describe("zalouser setup plugin", () => {
|
|
13
|
+
it("builds setup status without an initialized runtime", async () => {
|
|
14
|
+
const stateDir = await mkdtemp(path.join(os.tmpdir(), "openclaw-zalouser-setup-"));
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
await withEnvAsync({ OPENCLAW_STATE_DIR: stateDir }, async () => {
|
|
18
|
+
await expect(
|
|
19
|
+
zalouserSetupGetStatus({
|
|
20
|
+
cfg: {},
|
|
21
|
+
accountOverrides: {},
|
|
22
|
+
}),
|
|
23
|
+
).resolves.toMatchObject({
|
|
24
|
+
channel: "zalouser",
|
|
25
|
+
configured: false,
|
|
26
|
+
statusLines: ["Zalo Personal: needs QR login"],
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
} finally {
|
|
30
|
+
await rm(stateDir, { recursive: true, force: true });
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ResolvedZalouserAccount } from "./accounts.js";
|
|
2
|
+
import type { ChannelPlugin } from "./channel-api.js";
|
|
3
|
+
import { zalouserSetupAdapter } from "./setup-core.js";
|
|
4
|
+
import { zalouserSetupWizard } from "./setup-surface.js";
|
|
5
|
+
import { createZalouserPluginBase } from "./shared.js";
|
|
6
|
+
|
|
7
|
+
export const zalouserSetupPlugin: ChannelPlugin<ResolvedZalouserAccount> = {
|
|
8
|
+
...createZalouserPluginBase({
|
|
9
|
+
setupWizard: zalouserSetupWizard,
|
|
10
|
+
setup: zalouserSetupAdapter,
|
|
11
|
+
}),
|
|
12
|
+
};
|
package/src/channel.test.ts
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
|
+
import { createNonExitingRuntimeEnv } from "openclaw/plugin-sdk/plugin-test-runtime";
|
|
1
2
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import
|
|
3
|
+
import "./zalo-js.test-mocks.js";
|
|
4
|
+
import {
|
|
5
|
+
zalouserAuthAdapter,
|
|
6
|
+
zalouserGroupsAdapter,
|
|
7
|
+
zalouserMessageActions,
|
|
8
|
+
zalouserOutboundAdapter,
|
|
9
|
+
zalouserPairingTextAdapter,
|
|
10
|
+
zalouserResolverAdapter,
|
|
11
|
+
zalouserSecurityAdapter,
|
|
12
|
+
} from "./channel.adapters.js";
|
|
3
13
|
import { setZalouserRuntime } from "./runtime.js";
|
|
4
14
|
import { sendMessageZalouser, sendReactionZalouser } from "./send.js";
|
|
15
|
+
import {
|
|
16
|
+
listZaloFriendsMatchingMock,
|
|
17
|
+
startZaloQrLoginMock,
|
|
18
|
+
waitForZaloQrLoginMock,
|
|
19
|
+
} from "./zalo-js.test-mocks.js";
|
|
5
20
|
|
|
6
|
-
vi.mock("./
|
|
7
|
-
|
|
21
|
+
vi.mock("./qr-temp-file.js", () => ({
|
|
22
|
+
writeQrDataUrlToTempFile: vi.fn(async () => null),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
vi.mock("./send.js", async () => {
|
|
26
|
+
const actual = (await vi.importActual("./send.js")) as Record<string, unknown>;
|
|
8
27
|
return {
|
|
9
28
|
...actual,
|
|
10
29
|
sendMessageZalouser: vi.fn(async () => ({ ok: true, messageId: "mid-1" })),
|
|
@@ -15,15 +34,38 @@ vi.mock("./send.js", async (importOriginal) => {
|
|
|
15
34
|
const mockSendMessage = vi.mocked(sendMessageZalouser);
|
|
16
35
|
const mockSendReaction = vi.mocked(sendReactionZalouser);
|
|
17
36
|
|
|
37
|
+
function requireZalouserSendText() {
|
|
38
|
+
const sendText = zalouserOutboundAdapter.sendText;
|
|
39
|
+
if (!sendText) {
|
|
40
|
+
throw new Error("zalouser outbound.sendText unavailable");
|
|
41
|
+
}
|
|
42
|
+
return sendText;
|
|
43
|
+
}
|
|
44
|
+
|
|
18
45
|
function getResolveToolPolicy() {
|
|
19
|
-
const resolveToolPolicy =
|
|
20
|
-
expect(resolveToolPolicy).toBeTypeOf("function");
|
|
46
|
+
const resolveToolPolicy = zalouserGroupsAdapter.resolveToolPolicy;
|
|
21
47
|
if (!resolveToolPolicy) {
|
|
22
48
|
throw new Error("resolveToolPolicy unavailable");
|
|
23
49
|
}
|
|
24
50
|
return resolveToolPolicy;
|
|
25
51
|
}
|
|
26
52
|
|
|
53
|
+
function requireZalouserResolveRequireMention() {
|
|
54
|
+
const resolveRequireMention = zalouserGroupsAdapter.resolveRequireMention;
|
|
55
|
+
if (!resolveRequireMention) {
|
|
56
|
+
throw new Error("resolveRequireMention unavailable");
|
|
57
|
+
}
|
|
58
|
+
return resolveRequireMention;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function requireZalouserPairingNormalizer() {
|
|
62
|
+
const normalizeAllowEntry = zalouserPairingTextAdapter.normalizeAllowEntry;
|
|
63
|
+
if (!normalizeAllowEntry) {
|
|
64
|
+
throw new Error("pairing.normalizeAllowEntry unavailable");
|
|
65
|
+
}
|
|
66
|
+
return normalizeAllowEntry;
|
|
67
|
+
}
|
|
68
|
+
|
|
27
69
|
function resolveGroupToolPolicy(
|
|
28
70
|
groups: Record<string, { tools: { allow?: string[]; deny?: string[] } }>,
|
|
29
71
|
groupId: string,
|
|
@@ -56,11 +98,7 @@ describe("zalouser outbound", () => {
|
|
|
56
98
|
});
|
|
57
99
|
|
|
58
100
|
it("passes markdown chunk settings through sendText", async () => {
|
|
59
|
-
const sendText =
|
|
60
|
-
expect(sendText).toBeTypeOf("function");
|
|
61
|
-
if (!sendText) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
101
|
+
const sendText = requireZalouserSendText();
|
|
64
102
|
|
|
65
103
|
const result = await sendText({
|
|
66
104
|
cfg: { channels: { zalouser: { enabled: true } } } as never,
|
|
@@ -90,18 +128,67 @@ describe("zalouser outbound", () => {
|
|
|
90
128
|
});
|
|
91
129
|
});
|
|
92
130
|
|
|
131
|
+
describe("zalouser outbound chunking", () => {
|
|
132
|
+
it("chunks outbound text without requiring Zalouser runtime initialization", () => {
|
|
133
|
+
const chunker = zalouserOutboundAdapter.chunker;
|
|
134
|
+
if (!chunker) {
|
|
135
|
+
throw new Error("zalouser outbound.chunker unavailable");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
expect(chunker("alpha beta", 5)).toEqual(["alpha", "beta"]);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
93
142
|
describe("zalouser channel policies", () => {
|
|
94
143
|
beforeEach(() => {
|
|
95
144
|
mockSendReaction.mockClear();
|
|
96
145
|
mockSendReaction.mockResolvedValue({ ok: true });
|
|
97
146
|
});
|
|
98
147
|
|
|
99
|
-
it("
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return;
|
|
148
|
+
it("normalizes dm allowlist entries after trimming channel prefixes", () => {
|
|
149
|
+
const resolveDmPolicy = zalouserSecurityAdapter.resolveDmPolicy;
|
|
150
|
+
if (!resolveDmPolicy) {
|
|
151
|
+
throw new Error("resolveDmPolicy unavailable");
|
|
104
152
|
}
|
|
153
|
+
|
|
154
|
+
const cfg = {
|
|
155
|
+
channels: {
|
|
156
|
+
zalouser: {
|
|
157
|
+
dmPolicy: "allowlist",
|
|
158
|
+
allowFrom: [" zlu:123456 "],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
} as never;
|
|
162
|
+
const account = {
|
|
163
|
+
accountId: "default",
|
|
164
|
+
enabled: true,
|
|
165
|
+
authenticated: false,
|
|
166
|
+
profile: "default",
|
|
167
|
+
config: {
|
|
168
|
+
dmPolicy: "allowlist",
|
|
169
|
+
allowFrom: [" zlu:123456 "],
|
|
170
|
+
},
|
|
171
|
+
} as never;
|
|
172
|
+
|
|
173
|
+
const result = resolveDmPolicy({ cfg, account });
|
|
174
|
+
if (!result) {
|
|
175
|
+
throw new Error("zalouser resolveDmPolicy returned null");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
expect(result.policy).toBe("allowlist");
|
|
179
|
+
expect(result.allowFrom).toEqual([" zlu:123456 "]);
|
|
180
|
+
expect(result.normalizeEntry?.(" zlu:123456 ")).toBe("123456");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("normalizes pairing allowlist entries after trimming channel prefixes", () => {
|
|
184
|
+
const normalizeAllowEntry = requireZalouserPairingNormalizer();
|
|
185
|
+
|
|
186
|
+
expect(normalizeAllowEntry(" zlu:123456 ")).toBe("123456");
|
|
187
|
+
expect(normalizeAllowEntry(" zalouser:654321 ")).toBe("654321");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("resolves requireMention from group config", () => {
|
|
191
|
+
const resolveRequireMention = requireZalouserResolveRequireMention();
|
|
105
192
|
const requireMention = resolveRequireMention({
|
|
106
193
|
cfg: {
|
|
107
194
|
channels: {
|
|
@@ -130,10 +217,11 @@ describe("zalouser channel policies", () => {
|
|
|
130
217
|
});
|
|
131
218
|
|
|
132
219
|
it("handles react action", async () => {
|
|
133
|
-
const actions =
|
|
134
|
-
expect(
|
|
135
|
-
|
|
136
|
-
|
|
220
|
+
const actions = zalouserMessageActions;
|
|
221
|
+
expect(
|
|
222
|
+
actions?.describeMessageTool?.({ cfg: { channels: { zalouser: { enabled: true } } } })
|
|
223
|
+
?.actions,
|
|
224
|
+
).toEqual(["react"]);
|
|
137
225
|
const result = await actions?.handleAction?.({
|
|
138
226
|
channel: "zalouser",
|
|
139
227
|
action: "react",
|
|
@@ -161,6 +249,129 @@ describe("zalouser channel policies", () => {
|
|
|
161
249
|
emoji: "👍",
|
|
162
250
|
remove: false,
|
|
163
251
|
});
|
|
164
|
-
expect(result).
|
|
252
|
+
expect(result).toMatchObject({
|
|
253
|
+
content: [{ type: "text", text: "Reacted 👍 on 111" }],
|
|
254
|
+
details: {
|
|
255
|
+
messageId: "111",
|
|
256
|
+
cliMsgId: "222",
|
|
257
|
+
threadId: "123456",
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("honors the selected Zalouser account during discovery", () => {
|
|
263
|
+
const actions = zalouserMessageActions;
|
|
264
|
+
const cfg = {
|
|
265
|
+
channels: {
|
|
266
|
+
zalouser: {
|
|
267
|
+
enabled: true,
|
|
268
|
+
profile: "default",
|
|
269
|
+
accounts: {
|
|
270
|
+
default: {
|
|
271
|
+
enabled: false,
|
|
272
|
+
profile: "default",
|
|
273
|
+
},
|
|
274
|
+
work: {
|
|
275
|
+
enabled: true,
|
|
276
|
+
profile: "work",
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
expect(actions?.describeMessageTool?.({ cfg, accountId: "default" })).toBeNull();
|
|
284
|
+
expect(actions?.describeMessageTool?.({ cfg, accountId: "work" })?.actions).toEqual(["react"]);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe("zalouser account resolution", () => {
|
|
289
|
+
beforeEach(() => {
|
|
290
|
+
listZaloFriendsMatchingMock.mockReset();
|
|
291
|
+
startZaloQrLoginMock.mockReset();
|
|
292
|
+
waitForZaloQrLoginMock.mockReset();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("uses the configured default account for omitted target lookup", async () => {
|
|
296
|
+
const resolveTargets = zalouserResolverAdapter.resolveTargets;
|
|
297
|
+
if (!resolveTargets) {
|
|
298
|
+
throw new Error("zalouser resolver.resolveTargets unavailable");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
listZaloFriendsMatchingMock.mockResolvedValue([
|
|
302
|
+
{ userId: "42", displayName: "Work User" } as never,
|
|
303
|
+
]);
|
|
304
|
+
|
|
305
|
+
const result = await resolveTargets({
|
|
306
|
+
cfg: {
|
|
307
|
+
channels: {
|
|
308
|
+
zalouser: {
|
|
309
|
+
defaultAccount: "work",
|
|
310
|
+
accounts: {
|
|
311
|
+
work: {
|
|
312
|
+
profile: "work-profile",
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
} as never,
|
|
318
|
+
inputs: ["Work User"],
|
|
319
|
+
kind: "user",
|
|
320
|
+
runtime: createNonExitingRuntimeEnv(),
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
expect(listZaloFriendsMatchingMock).toHaveBeenCalledWith("work-profile", "Work User");
|
|
324
|
+
expect(result).toEqual([
|
|
325
|
+
expect.objectContaining({
|
|
326
|
+
input: "Work User",
|
|
327
|
+
resolved: true,
|
|
328
|
+
id: "42",
|
|
329
|
+
name: "Work User",
|
|
330
|
+
}),
|
|
331
|
+
]);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it("uses the configured default account for omitted qr login", async () => {
|
|
335
|
+
const login = zalouserAuthAdapter.login;
|
|
336
|
+
if (!login) {
|
|
337
|
+
throw new Error("zalouser auth.login unavailable");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
startZaloQrLoginMock.mockResolvedValue({
|
|
341
|
+
message: "qr ready",
|
|
342
|
+
qrDataUrl: "data:image/png;base64,abc",
|
|
343
|
+
} as never);
|
|
344
|
+
waitForZaloQrLoginMock.mockResolvedValue({
|
|
345
|
+
connected: true,
|
|
346
|
+
userId: "u-1",
|
|
347
|
+
displayName: "Work User",
|
|
348
|
+
} as never);
|
|
349
|
+
|
|
350
|
+
const runtime = createNonExitingRuntimeEnv();
|
|
351
|
+
|
|
352
|
+
await login({
|
|
353
|
+
cfg: {
|
|
354
|
+
channels: {
|
|
355
|
+
zalouser: {
|
|
356
|
+
defaultAccount: "work",
|
|
357
|
+
accounts: {
|
|
358
|
+
work: {
|
|
359
|
+
profile: "work-profile",
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
} as never,
|
|
365
|
+
runtime,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
expect(startZaloQrLoginMock).toHaveBeenCalledWith({
|
|
369
|
+
profile: "work-profile",
|
|
370
|
+
timeoutMs: 35_000,
|
|
371
|
+
});
|
|
372
|
+
expect(waitForZaloQrLoginMock).toHaveBeenCalledWith({
|
|
373
|
+
profile: "work-profile",
|
|
374
|
+
timeoutMs: 180_000,
|
|
375
|
+
});
|
|
165
376
|
});
|
|
166
377
|
});
|