refactor(extensions): reuse shared helper primitives

This commit is contained in:
Peter Steinberger
2026-03-07 10:40:57 +00:00
parent 3c71e2bd48
commit 1aa77e4603
58 changed files with 1567 additions and 2195 deletions

View File

@@ -317,20 +317,11 @@ describe("createSynologyChatPlugin", () => {
});
describe("gateway", () => {
it("startAccount returns pending promise for disabled account", async () => {
const plugin = createSynologyChatPlugin();
const abortController = new AbortController();
const ctx = {
cfg: {
channels: { "synology-chat": { enabled: false } },
},
accountId: "default",
log: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
abortSignal: abortController.signal,
};
const result = plugin.gateway.startAccount(ctx);
async function expectPendingStartAccountPromise(
result: Promise<unknown>,
abortController: AbortController,
) {
expect(result).toBeInstanceOf(Promise);
// Promise should stay pending (never resolve) to prevent restart loop
const resolved = await Promise.race([
result,
new Promise((r) => setTimeout(() => r("pending"), 50)),
@@ -338,29 +329,29 @@ describe("createSynologyChatPlugin", () => {
expect(resolved).toBe("pending");
abortController.abort();
await result;
}
async function expectPendingStartAccount(accountConfig: Record<string, unknown>) {
const plugin = createSynologyChatPlugin();
const abortController = new AbortController();
const ctx = {
cfg: {
channels: { "synology-chat": accountConfig },
},
accountId: "default",
log: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
abortSignal: abortController.signal,
};
const result = plugin.gateway.startAccount(ctx);
await expectPendingStartAccountPromise(result, abortController);
}
it("startAccount returns pending promise for disabled account", async () => {
await expectPendingStartAccount({ enabled: false });
});
it("startAccount returns pending promise for account without token", async () => {
const plugin = createSynologyChatPlugin();
const abortController = new AbortController();
const ctx = {
cfg: {
channels: { "synology-chat": { enabled: true } },
},
accountId: "default",
log: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
abortSignal: abortController.signal,
};
const result = plugin.gateway.startAccount(ctx);
expect(result).toBeInstanceOf(Promise);
// Promise should stay pending (never resolve) to prevent restart loop
const resolved = await Promise.race([
result,
new Promise((r) => setTimeout(() => r("pending"), 50)),
]);
expect(resolved).toBe("pending");
abortController.abort();
await result;
await expectPendingStartAccount({ enabled: true });
});
it("startAccount refuses allowlist accounts with empty allowedUserIds", async () => {
@@ -387,16 +378,9 @@ describe("createSynologyChatPlugin", () => {
};
const result = plugin.gateway.startAccount(ctx);
expect(result).toBeInstanceOf(Promise);
const resolved = await Promise.race([
result,
new Promise((r) => setTimeout(() => r("pending"), 50)),
]);
expect(resolved).toBe("pending");
await expectPendingStartAccountPromise(result, abortController);
expect(ctx.log.warn).toHaveBeenCalledWith(expect.stringContaining("empty allowedUserIds"));
expect(registerMock).not.toHaveBeenCalled();
abortController.abort();
await result;
});
it("deregisters stale route before re-registering same account/path", async () => {

View File

@@ -118,26 +118,21 @@ describe("sendFileUrl", () => {
function mockUserListResponse(
users: Array<{ user_id: number; username: string; nickname: string }>,
) {
const httpsGet = vi.mocked((https as any).get);
httpsGet.mockImplementation((_url: any, _opts: any, callback: any) => {
const res = new EventEmitter() as any;
res.statusCode = 200;
process.nextTick(() => {
callback(res);
res.emit("data", Buffer.from(JSON.stringify({ success: true, data: { users } })));
res.emit("end");
});
const req = new EventEmitter() as any;
req.destroy = vi.fn();
return req;
});
mockUserListResponseImpl(users, false);
}
function mockUserListResponseOnce(
users: Array<{ user_id: number; username: string; nickname: string }>,
) {
mockUserListResponseImpl(users, true);
}
function mockUserListResponseImpl(
users: Array<{ user_id: number; username: string; nickname: string }>,
once: boolean,
) {
const httpsGet = vi.mocked((https as any).get);
httpsGet.mockImplementationOnce((_url: any, _opts: any, callback: any) => {
const impl = (_url: any, _opts: any, callback: any) => {
const res = new EventEmitter() as any;
res.statusCode = 200;
process.nextTick(() => {
@@ -148,7 +143,12 @@ function mockUserListResponseOnce(
const req = new EventEmitter() as any;
req.destroy = vi.fn();
return req;
});
};
if (once) {
httpsGet.mockImplementationOnce(impl);
return;
}
httpsGet.mockImplementation(impl);
}
describe("resolveChatUserId", () => {