fix(zalouser): decouple tests from zca-js runtime

This commit is contained in:
Shakker
2026-03-20 05:50:38 +00:00
committed by Shakker
parent 5036ed2699
commit 8d805a02fd
18 changed files with 148 additions and 107 deletions

View File

@@ -1,10 +1,14 @@
import { vi } from "vitest";
import { createDefaultResolvedZalouserAccount } from "./test-helpers.js";
vi.mock("./accounts.js", async (importOriginal) => {
const actual = (await importOriginal()) as Record<string, unknown>;
vi.mock("./accounts.js", () => {
return {
...actual,
listZalouserAccountIds: () => ["default"],
resolveDefaultZalouserAccountId: () => "default",
resolveZalouserAccountSync: () => createDefaultResolvedZalouserAccount(),
resolveZalouserAccount: async () => createDefaultResolvedZalouserAccount(),
listEnabledZalouserAccounts: async () => [createDefaultResolvedZalouserAccount()],
getZcaUserInfo: async () => null,
checkZcaAuthenticated: async () => false,
};
});

View File

@@ -1,18 +1,9 @@
import { describe, expect, it, vi } from "vitest";
import "./accounts.test-mocks.js";
import { createZalouserRuntimeEnv } from "./test-helpers.js";
const listZaloGroupMembersMock = vi.hoisted(() => vi.fn(async () => []));
vi.mock("./zalo-js.js", async (importOriginal) => {
const actual = (await importOriginal()) as Record<string, unknown>;
return {
...actual,
listZaloGroupMembers: listZaloGroupMembersMock,
};
});
import "./zalo-js.test-mocks.js";
import { zalouserPlugin } from "./channel.js";
import { createZalouserRuntimeEnv } from "./test-helpers.js";
import { listZaloGroupMembersMock } from "./zalo-js.test-mocks.js";
const runtimeStub = createZalouserRuntimeEnv();

View File

@@ -1,6 +1,7 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { primeChannelOutboundSendMock } from "../../../src/channels/plugins/contracts/suites.js";
import "./accounts.test-mocks.js";
import "./zalo-js.test-mocks.js";
import type { ReplyPayload } from "../runtime-api.js";
import { zalouserPlugin } from "./channel.js";
import { setZalouserRuntime } from "./runtime.js";

View File

@@ -4,6 +4,7 @@ import path from "node:path";
import { describe, expect, it } from "vitest";
import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/channels/plugins/setup-wizard.js";
import { withEnvAsync } from "../../../test/helpers/extensions/env.js";
import "./zalo-js.test-mocks.js";
import { zalouserSetupPlugin } from "./channel.setup.js";
const zalouserSetupAdapter = buildChannelSetupWizardAdapterFromSetupWizard({

View File

@@ -1,4 +1,5 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import "./zalo-js.test-mocks.js";
import { zalouserPlugin } from "./channel.js";
import { setZalouserRuntime } from "./runtime.js";
import { sendMessageZalouser, sendReactionZalouser } from "./send.js";

View File

@@ -1,6 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig, PluginRuntime, RuntimeEnv } from "../runtime-api.js";
import "./monitor.send-mocks.js";
import "./zalo-js.test-mocks.js";
import { __testing } from "./monitor.js";
import { sendMessageZalouserMock } from "./monitor.send-mocks.js";
import { setZalouserRuntime } from "./runtime.js";

View File

@@ -1,6 +1,7 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig, PluginRuntime, RuntimeEnv } from "../runtime-api.js";
import "./monitor.send-mocks.js";
import "./zalo-js.test-mocks.js";
import { resolveZalouserAccountSync } from "./accounts.js";
import { __testing } from "./monitor.js";
import {

View File

@@ -1,4 +1,4 @@
import { Reactions } from "./zca-client.js";
import { Reactions } from "./zca-constants.js";
const REACTION_ALIAS_MAP = new Map<string, string>([
["like", Reactions.LIKE],

View File

@@ -17,7 +17,7 @@ import {
sendZaloTextMessage,
sendZaloTypingEvent,
} from "./zalo-js.js";
import { TextStyle } from "./zca-client.js";
import { TextStyle } from "./zca-constants.js";
vi.mock("./zalo-js.js", () => ({
sendZaloTextMessage: vi.fn(),

View File

@@ -8,7 +8,7 @@ import {
sendZaloTextMessage,
sendZaloTypingEvent,
} from "./zalo-js.js";
import { TextStyle } from "./zca-client.js";
import { TextStyle } from "./zca-constants.js";
export type ZalouserSendOptions = ZaloSendOptions;
export type ZalouserSendResult = ZaloSendResult;

View File

@@ -3,30 +3,7 @@ import { buildChannelSetupWizardAdapterFromSetupWizard } from "../../../src/chan
import { createRuntimeEnv } from "../../../test/helpers/extensions/runtime-env.js";
import { createTestWizardPrompter } from "../../../test/helpers/extensions/setup-wizard.js";
import type { OpenClawConfig } from "../runtime-api.js";
vi.mock("./zalo-js.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./zalo-js.js")>();
return {
...actual,
checkZaloAuthenticated: vi.fn(async () => false),
logoutZaloProfile: vi.fn(async () => {}),
startZaloQrLogin: vi.fn(async () => ({
message: "qr pending",
qrDataUrl: undefined,
})),
waitForZaloQrLogin: vi.fn(async () => ({
connected: false,
message: "login pending",
})),
resolveZaloAllowFromEntries: vi.fn(async ({ entries }: { entries: string[] }) =>
entries.map((entry) => ({ input: entry, resolved: true, id: entry, note: undefined })),
),
resolveZaloGroupsByEntries: vi.fn(async ({ entries }: { entries: string[] }) =>
entries.map((entry) => ({ input: entry, resolved: true, id: entry, note: undefined })),
),
};
});
import "./zalo-js.test-mocks.js";
import { zalouserPlugin } from "./channel.js";
const zalouserConfigureAdapter = buildChannelSetupWizardAdapterFromSetupWizard({

View File

@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { parseZalouserTextStyles } from "./text-styles.js";
import { TextStyle } from "./zca-client.js";
import { TextStyle } from "./zca-constants.js";
describe("parseZalouserTextStyles", () => {
it("renders inline markdown emphasis as Zalo style ranges", () => {

View File

@@ -1,4 +1,4 @@
import { TextStyle, type Style } from "./zca-client.js";
import { TextStyle, type Style } from "./zca-constants.js";
type InlineStyle = (typeof TextStyle)[keyof typeof TextStyle];

View File

@@ -1,4 +1,4 @@
import type { Style } from "./zca-client.js";
import type { Style } from "./zca-constants.js";
export type ZcaFriend = {
userId: string;

View File

@@ -0,0 +1,60 @@
import { vi } from "vitest";
const zaloJsMocks = vi.hoisted(() => ({
checkZaloAuthenticatedMock: vi.fn(async () => false),
getZaloUserInfoMock: vi.fn(async () => null),
listZaloFriendsMock: vi.fn(async () => []),
listZaloFriendsMatchingMock: vi.fn(async () => []),
listZaloGroupMembersMock: vi.fn(async () => []),
listZaloGroupsMock: vi.fn(async () => []),
listZaloGroupsMatchingMock: vi.fn(async () => []),
logoutZaloProfileMock: vi.fn(async () => {}),
resolveZaloAllowFromEntriesMock: vi.fn(async ({ entries }: { entries: string[] }) =>
entries.map((entry) => ({ input: entry, resolved: true, id: entry, note: undefined })),
),
resolveZaloGroupContextMock: vi.fn(async () => null),
resolveZaloGroupsByEntriesMock: vi.fn(async ({ entries }: { entries: string[] }) =>
entries.map((entry) => ({ input: entry, resolved: true, id: entry, note: undefined })),
),
startZaloListenerMock: vi.fn(async () => ({ stop: vi.fn() })),
startZaloQrLoginMock: vi.fn(async () => ({
message: "qr pending",
qrDataUrl: undefined,
})),
waitForZaloQrLoginMock: vi.fn(async () => ({
connected: false,
message: "login pending",
})),
}));
export const checkZaloAuthenticatedMock = zaloJsMocks.checkZaloAuthenticatedMock;
export const getZaloUserInfoMock = zaloJsMocks.getZaloUserInfoMock;
export const listZaloFriendsMock = zaloJsMocks.listZaloFriendsMock;
export const listZaloFriendsMatchingMock = zaloJsMocks.listZaloFriendsMatchingMock;
export const listZaloGroupMembersMock = zaloJsMocks.listZaloGroupMembersMock;
export const listZaloGroupsMock = zaloJsMocks.listZaloGroupsMock;
export const listZaloGroupsMatchingMock = zaloJsMocks.listZaloGroupsMatchingMock;
export const logoutZaloProfileMock = zaloJsMocks.logoutZaloProfileMock;
export const resolveZaloAllowFromEntriesMock = zaloJsMocks.resolveZaloAllowFromEntriesMock;
export const resolveZaloGroupContextMock = zaloJsMocks.resolveZaloGroupContextMock;
export const resolveZaloGroupsByEntriesMock = zaloJsMocks.resolveZaloGroupsByEntriesMock;
export const startZaloListenerMock = zaloJsMocks.startZaloListenerMock;
export const startZaloQrLoginMock = zaloJsMocks.startZaloQrLoginMock;
export const waitForZaloQrLoginMock = zaloJsMocks.waitForZaloQrLoginMock;
vi.mock("./zalo-js.js", () => ({
checkZaloAuthenticated: checkZaloAuthenticatedMock,
getZaloUserInfo: getZaloUserInfoMock,
listZaloFriends: listZaloFriendsMock,
listZaloFriendsMatching: listZaloFriendsMatchingMock,
listZaloGroupMembers: listZaloGroupMembersMock,
listZaloGroups: listZaloGroupsMock,
listZaloGroupsMatching: listZaloGroupsMatchingMock,
logoutZaloProfile: logoutZaloProfileMock,
resolveZaloAllowFromEntries: resolveZaloAllowFromEntriesMock,
resolveZaloGroupContext: resolveZaloGroupContextMock,
resolveZaloGroupsByEntries: resolveZaloGroupsByEntriesMock,
startZaloListener: startZaloListenerMock,
startZaloQrLogin: startZaloQrLoginMock,
waitForZaloQrLogin: waitForZaloQrLoginMock,
}));

View File

@@ -19,17 +19,16 @@ import type {
ZcaUserInfo,
} from "./types.js";
import {
LoginQRCallbackEventType,
TextStyle,
ThreadType,
Zalo,
type API,
type Credentials,
type GroupInfo,
type LoginQRCallbackEvent,
type Message,
type User,
Zalo,
} from "./zca-client.js";
import { LoginQRCallbackEventType, ThreadType } from "./zca-constants.js";
const API_LOGIN_TIMEOUT_MS = 20_000;
const QR_LOGIN_TTL_MS = 3 * 60_000;

View File

@@ -1,67 +1,17 @@
import * as zcaJsRuntime from "zca-js";
import {
LoginQRCallbackEventType,
Reactions,
TextStyle,
ThreadType,
type Style,
} from "./zca-constants.js";
const zcaJs = zcaJsRuntime as unknown as {
ThreadType: unknown;
LoginQRCallbackEventType: unknown;
Reactions: unknown;
Zalo: unknown;
};
export const ThreadType = zcaJs.ThreadType as {
User: 0;
Group: 1;
};
export const LoginQRCallbackEventType = zcaJs.LoginQRCallbackEventType as {
QRCodeGenerated: 0;
QRCodeExpired: 1;
QRCodeScanned: 2;
QRCodeDeclined: 3;
GotLoginInfo: 4;
};
export const Reactions = zcaJs.Reactions as Record<string, string> & {
HEART: string;
LIKE: string;
HAHA: string;
WOW: string;
CRY: string;
ANGRY: string;
NONE: string;
};
// Mirror zca-js sendMessage style constants locally because the package root
// typing surface does not consistently expose TextStyle/Style to tsgo.
export const TextStyle = {
Bold: "b",
Italic: "i",
Underline: "u",
StrikeThrough: "s",
Red: "c_db342e",
Orange: "c_f27806",
Yellow: "c_f7b503",
Green: "c_15a85f",
Small: "f_13",
Big: "f_18",
UnorderedList: "lst_1",
OrderedList: "lst_2",
Indent: "ind_$",
} as const;
type TextStyleValue = (typeof TextStyle)[keyof typeof TextStyle];
export type Style =
| {
start: number;
len: number;
st: Exclude<TextStyleValue, typeof TextStyle.Indent>;
}
| {
start: number;
len: number;
st: typeof TextStyle.Indent;
indentSize?: number;
};
export { LoginQRCallbackEventType, Reactions, TextStyle, ThreadType };
export type { Style };
export type Credentials = {
imei: string;

View File

@@ -0,0 +1,55 @@
export const ThreadType = {
User: 0,
Group: 1,
} as const;
export const LoginQRCallbackEventType = {
QRCodeGenerated: 0,
QRCodeExpired: 1,
QRCodeScanned: 2,
QRCodeDeclined: 3,
GotLoginInfo: 4,
} as const;
export const Reactions = {
HEART: "/-heart",
LIKE: "/-strong",
HAHA: ":>",
WOW: ":o",
CRY: ":-((",
ANGRY: ":-h",
NONE: "",
} as const;
// Mirror zca-js sendMessage style constants locally because the package root
// typing surface does not consistently expose TextStyle/Style to tsgo.
export const TextStyle = {
Bold: "b",
Italic: "i",
Underline: "u",
StrikeThrough: "s",
Red: "c_db342e",
Orange: "c_f27806",
Yellow: "c_f7b503",
Green: "c_15a85f",
Small: "f_13",
Big: "f_18",
UnorderedList: "lst_1",
OrderedList: "lst_2",
Indent: "ind_$",
} as const;
type TextStyleValue = (typeof TextStyle)[keyof typeof TextStyle];
export type Style =
| {
start: number;
len: number;
st: Exclude<TextStyleValue, typeof TextStyle.Indent>;
}
| {
start: number;
len: number;
st: typeof TextStyle.Indent;
indentSize?: number;
};