mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 17:20:22 +00:00
test: trim extension setup startup
This commit is contained in:
87
extensions/nextcloud-talk/src/channel.core.test.ts
Normal file
87
extensions/nextcloud-talk/src/channel.core.test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import type { CoreConfig } from "./types.js";
|
||||
|
||||
vi.mock("../../../src/config/bundled-channel-config-runtime.js", () => ({
|
||||
getBundledChannelRuntimeMap: () => new Map(),
|
||||
getBundledChannelConfigSchemaMap: () => new Map(),
|
||||
}));
|
||||
|
||||
vi.mock("../../../src/channels/plugins/bundled.js", () => ({
|
||||
bundledChannelPlugins: [],
|
||||
bundledChannelSetupPlugins: [],
|
||||
}));
|
||||
|
||||
let nextcloudTalkPlugin: typeof import("./channel.js").nextcloudTalkPlugin;
|
||||
let NextcloudTalkConfigSchema: typeof import("./config-schema.js").NextcloudTalkConfigSchema;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ nextcloudTalkPlugin } = await import("./channel.js"));
|
||||
({ NextcloudTalkConfigSchema } = await import("./config-schema.js"));
|
||||
});
|
||||
|
||||
describe("nextcloud talk channel core", () => {
|
||||
it("accepts SecretRef botSecret and apiPassword at top-level", () => {
|
||||
const result = NextcloudTalkConfigSchema.safeParse({
|
||||
baseUrl: "https://cloud.example.com",
|
||||
botSecret: { source: "env", provider: "default", id: "NEXTCLOUD_TALK_BOT_SECRET" },
|
||||
apiUser: "bot",
|
||||
apiPassword: { source: "env", provider: "default", id: "NEXTCLOUD_TALK_API_PASSWORD" },
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts SecretRef botSecret and apiPassword on account", () => {
|
||||
const result = NextcloudTalkConfigSchema.safeParse({
|
||||
accounts: {
|
||||
main: {
|
||||
baseUrl: "https://cloud.example.com",
|
||||
botSecret: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "NEXTCLOUD_TALK_MAIN_BOT_SECRET",
|
||||
},
|
||||
apiUser: "bot",
|
||||
apiPassword: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "NEXTCLOUD_TALK_MAIN_API_PASSWORD",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("normalizes trimmed DM allowlist prefixes to lowercase ids", () => {
|
||||
const resolveDmPolicy = nextcloudTalkPlugin.security?.resolveDmPolicy;
|
||||
if (!resolveDmPolicy) {
|
||||
throw new Error("resolveDmPolicy unavailable");
|
||||
}
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
"nextcloud-talk": {
|
||||
baseUrl: "https://cloud.example.com",
|
||||
botSecret: "secret",
|
||||
dmPolicy: "allowlist",
|
||||
allowFrom: [" nc:User-Id "],
|
||||
},
|
||||
},
|
||||
} as CoreConfig;
|
||||
|
||||
const result = resolveDmPolicy({
|
||||
cfg,
|
||||
account: nextcloudTalkPlugin.config.resolveAccount(cfg, "default"),
|
||||
});
|
||||
if (!result) {
|
||||
throw new Error("nextcloud-talk resolveDmPolicy returned null");
|
||||
}
|
||||
|
||||
expect(result.policy).toBe("allowlist");
|
||||
expect(result.allowFrom).toEqual([" nc:User-Id "]);
|
||||
expect(result.normalizeEntry?.(" nc:User-Id ")).toBe("user-id");
|
||||
expect(nextcloudTalkPlugin.pairing?.normalizeAllowEntry?.(" nextcloud-talk:User-Id ")).toBe(
|
||||
"user-id",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -24,57 +24,40 @@ import {
|
||||
generateNextcloudTalkSignature,
|
||||
verifyNextcloudTalkSignature,
|
||||
} from "./signature.js";
|
||||
import type { CoreConfig } from "./types.js";
|
||||
|
||||
vi.mock("../../../src/config/bundled-channel-config-runtime.js", () => ({
|
||||
getBundledChannelRuntimeMap: () => new Map(),
|
||||
getBundledChannelConfigSchemaMap: () => new Map(),
|
||||
}));
|
||||
|
||||
vi.mock("../../../src/channels/plugins/bundled.js", () => ({
|
||||
bundledChannelPlugins: [],
|
||||
bundledChannelSetupPlugins: [],
|
||||
}));
|
||||
|
||||
const fetchWithSsrFGuard = vi.hoisted(() => vi.fn());
|
||||
const readFileSync = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("../runtime-api.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../runtime-api.js")>();
|
||||
return {
|
||||
...actual,
|
||||
fetchWithSsrFGuard,
|
||||
};
|
||||
vi.mock("../runtime-api.js", () => {
|
||||
return vi
|
||||
.importActual<typeof import("../runtime-api.js")>("../runtime-api.js")
|
||||
.then((actual) => ({
|
||||
...actual,
|
||||
fetchWithSsrFGuard,
|
||||
}));
|
||||
});
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/ssrf-runtime", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/ssrf-runtime")>();
|
||||
return {
|
||||
...actual,
|
||||
fetchWithSsrFGuard,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("node:fs", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("node:fs")>();
|
||||
return {
|
||||
vi.mock("node:fs", () => {
|
||||
return vi.importActual<typeof import("node:fs")>("node:fs").then((actual) => ({
|
||||
...actual,
|
||||
readFileSync,
|
||||
};
|
||||
}));
|
||||
});
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
let nextcloudTalkPlugin: typeof import("./channel.js").nextcloudTalkPlugin;
|
||||
let NextcloudTalkConfigSchema: typeof import("./config-schema.js").NextcloudTalkConfigSchema;
|
||||
let resolveNextcloudTalkRoomKind: typeof import("./room-info.js").resolveNextcloudTalkRoomKind;
|
||||
let resetNextcloudTalkRoomCache: () => void;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ nextcloudTalkPlugin } = await import("./channel.js"));
|
||||
({ NextcloudTalkConfigSchema } = await import("./config-schema.js"));
|
||||
const roomInfo = await import("./room-info.js");
|
||||
resolveNextcloudTalkRoomKind = roomInfo.resolveNextcloudTalkRoomKind;
|
||||
resetNextcloudTalkRoomCache = roomInfo.__testing.resetRoomCache;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
fetchWithSsrFGuard.mockReset();
|
||||
readFileSync.mockReset();
|
||||
resetNextcloudTalkRoomCache();
|
||||
while (tempDirs.length > 0) {
|
||||
const dir = tempDirs.pop();
|
||||
if (dir) {
|
||||
@@ -91,38 +74,6 @@ async function makeTempDir(): Promise<string> {
|
||||
|
||||
describe("nextcloud talk core", () => {
|
||||
it("accepts SecretRef botSecret and apiPassword at top-level", () => {
|
||||
const result = NextcloudTalkConfigSchema.safeParse({
|
||||
baseUrl: "https://cloud.example.com",
|
||||
botSecret: { source: "env", provider: "default", id: "NEXTCLOUD_TALK_BOT_SECRET" },
|
||||
apiUser: "bot",
|
||||
apiPassword: { source: "env", provider: "default", id: "NEXTCLOUD_TALK_API_PASSWORD" },
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts SecretRef botSecret and apiPassword on account", () => {
|
||||
const result = NextcloudTalkConfigSchema.safeParse({
|
||||
accounts: {
|
||||
main: {
|
||||
baseUrl: "https://cloud.example.com",
|
||||
botSecret: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "NEXTCLOUD_TALK_MAIN_BOT_SECRET",
|
||||
},
|
||||
apiUser: "bot",
|
||||
apiPassword: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "NEXTCLOUD_TALK_MAIN_API_PASSWORD",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps markdown mostly intact while trimming outer whitespace", () => {
|
||||
expect(markdownToNextcloudTalk(" **hello** ")).toBe("**hello**");
|
||||
});
|
||||
|
||||
@@ -289,39 +240,6 @@ describe("nextcloud talk core", () => {
|
||||
expect(accountBFirst).toBe(true);
|
||||
});
|
||||
|
||||
it("normalizes trimmed DM allowlist prefixes to lowercase ids", () => {
|
||||
const resolveDmPolicy = nextcloudTalkPlugin.security?.resolveDmPolicy;
|
||||
if (!resolveDmPolicy) {
|
||||
throw new Error("resolveDmPolicy unavailable");
|
||||
}
|
||||
|
||||
const cfg = {
|
||||
channels: {
|
||||
"nextcloud-talk": {
|
||||
baseUrl: "https://cloud.example.com",
|
||||
botSecret: "secret",
|
||||
dmPolicy: "allowlist",
|
||||
allowFrom: [" nc:User-Id "],
|
||||
},
|
||||
},
|
||||
} as CoreConfig;
|
||||
|
||||
const result = resolveDmPolicy({
|
||||
cfg,
|
||||
account: nextcloudTalkPlugin.config.resolveAccount(cfg, "default"),
|
||||
});
|
||||
if (!result) {
|
||||
throw new Error("nextcloud-talk resolveDmPolicy returned null");
|
||||
}
|
||||
|
||||
expect(result.policy).toBe("allowlist");
|
||||
expect(result.allowFrom).toEqual([" nc:User-Id "]);
|
||||
expect(result.normalizeEntry?.(" nc:User-Id ")).toBe("user-id");
|
||||
expect(nextcloudTalkPlugin.pairing?.normalizeAllowEntry?.(" nextcloud-talk:User-Id ")).toBe(
|
||||
"user-id",
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves allowlist matches and group policy decisions", () => {
|
||||
expect(
|
||||
resolveNextcloudTalkAllowlistMatch({
|
||||
@@ -429,7 +347,6 @@ describe("nextcloud talk core", () => {
|
||||
});
|
||||
|
||||
it("resolves direct rooms from the room info endpoint", async () => {
|
||||
vi.resetModules();
|
||||
const release = vi.fn(async () => {});
|
||||
fetchWithSsrFGuard.mockResolvedValue({
|
||||
response: {
|
||||
@@ -445,7 +362,6 @@ describe("nextcloud talk core", () => {
|
||||
release,
|
||||
});
|
||||
|
||||
const { resolveNextcloudTalkRoomKind } = await import("./room-info.js");
|
||||
const kind = await resolveNextcloudTalkRoomKind({
|
||||
account: {
|
||||
accountId: "acct-direct",
|
||||
@@ -469,7 +385,6 @@ describe("nextcloud talk core", () => {
|
||||
});
|
||||
|
||||
it("reads the api password from a file and logs non-ok room info responses", async () => {
|
||||
vi.resetModules();
|
||||
const release = vi.fn(async () => {});
|
||||
const log = vi.fn();
|
||||
const error = vi.fn();
|
||||
@@ -484,7 +399,6 @@ describe("nextcloud talk core", () => {
|
||||
release,
|
||||
});
|
||||
|
||||
const { resolveNextcloudTalkRoomKind } = await import("./room-info.js");
|
||||
const kind = await resolveNextcloudTalkRoomKind({
|
||||
account: {
|
||||
accountId: "acct-group",
|
||||
@@ -505,9 +419,6 @@ describe("nextcloud talk core", () => {
|
||||
});
|
||||
|
||||
it("returns undefined from room info without credentials or base url", async () => {
|
||||
vi.resetModules();
|
||||
const { resolveNextcloudTalkRoomKind } = await import("./room-info.js");
|
||||
|
||||
await expect(
|
||||
resolveNextcloudTalkRoomKind({
|
||||
account: {
|
||||
|
||||
@@ -11,6 +11,12 @@ const roomCache = new Map<
|
||||
{ kind?: "direct" | "group"; fetchedAt: number; error?: string }
|
||||
>();
|
||||
|
||||
export const __testing = {
|
||||
resetRoomCache() {
|
||||
roomCache.clear();
|
||||
},
|
||||
};
|
||||
|
||||
function resolveRoomCacheKey(params: { accountId: string; roomToken: string }) {
|
||||
return `${params.accountId}:${params.roomToken}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user