Files
zn-ai/tests/channels.test.ts
duanshuwen ef46c73c3e Add unit tests for channel utilities and configure testing environment
- Created a new test file `channels.test.ts` to cover utilities related to channel configurations and targets.
- Implemented tests for normalizing and grouping selected channels by type, as well as building channel targets from account data and cron history.
- Mocked necessary dependencies to isolate tests and ensure accurate results.
- Updated `vite.config.ts` to set up the testing environment with jsdom and enable global variables for tests.
2026-04-18 16:12:49 +08:00

288 lines
7.4 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
const mocks = vi.hoisted(() => ({
configGet: vi.fn(),
listStoredChannelAccountRecords: vi.fn(),
listCronJobs: vi.fn(),
}));
vi.mock('@electron/service/config-service', () => ({
default: {
get: mocks.configGet,
},
configManager: {
get: mocks.configGet,
},
}));
vi.mock('../electron/utils/cron-store', () => ({
listCronJobs: mocks.listCronJobs,
}));
vi.mock('../electron/utils/channel-config', () => ({
listStoredChannelAccountRecords: mocks.listStoredChannelAccountRecords,
}));
import {
getSelectedChannelsConfig,
listSelectedChannelAccountGroups,
listSelectedChannelTargets,
} from '../electron/utils/channels';
describe('channels utilities', () => {
beforeEach(() => {
mocks.configGet.mockReset();
mocks.listStoredChannelAccountRecords.mockReset();
mocks.listCronJobs.mockReset();
});
it('normalizes and groups selected channels by inferred channel type', () => {
mocks.configGet.mockReturnValue([
{
id: ' acct-1 ',
channelName: ' 抖音 ',
channelUrl: ' https://life.douyin.com/live ',
},
{
id: 'acct-1',
channelName: '抖音直播',
channelUrl: 'https://douyin.com/duplicate',
},
{
id: 'alpha',
channelName: 'Alpha Store',
channelUrl: 'https://meituan.com/a',
},
{
id: 'beta',
channelName: 'Beta Store',
channelUrl: 'https://me.meituan.com/b',
},
]);
expect(getSelectedChannelsConfig()).toEqual([
{
id: 'acct-1',
channelName: '抖音',
channelUrl: 'https://life.douyin.com/live',
},
{
id: 'alpha',
channelName: 'Alpha Store',
channelUrl: 'https://meituan.com/a',
},
{
id: 'beta',
channelName: 'Beta Store',
channelUrl: 'https://me.meituan.com/b',
},
]);
mocks.listStoredChannelAccountRecords.mockReturnValue([
{
channelType: 'douyin',
channelLabel: '抖音',
defaultAccountId: 'acct-1',
channelEnabled: true,
accountId: 'acct-1',
accountName: '抖音',
accountEnabled: true,
channelUrl: 'https://life.douyin.com/live',
config: {},
metadata: {},
},
{
channelType: 'meituan',
channelLabel: 'Alpha Store',
defaultAccountId: 'alpha',
channelEnabled: true,
accountId: 'alpha',
accountName: 'Alpha Store',
accountEnabled: true,
channelUrl: 'https://meituan.com/a',
config: {},
metadata: {},
},
{
channelType: 'meituan',
channelLabel: 'Alpha Store',
defaultAccountId: 'alpha',
channelEnabled: true,
accountId: 'beta',
accountName: 'Beta Store',
accountEnabled: true,
channelUrl: 'https://me.meituan.com/b',
config: {},
metadata: {},
},
]);
const groups = listSelectedChannelAccountGroups({
agents: [
{ id: 'Agent-A', name: 'Ada' },
{ id: 'agent-b', name: 'Ben' },
],
channelOwners: {
meituan: 'Agent-A',
},
channelAccountOwners: {
'meituan:alpha': 'agent-b',
},
});
const byType = Object.fromEntries(groups.map((group) => [group.channelType, group]));
expect(byType.douyin).toMatchObject({
channelType: 'douyin',
channelLabel: '抖音',
defaultAccountId: 'acct-1',
status: 'degraded',
accounts: [
{
accountId: 'acct-1',
name: '抖音',
configured: true,
status: 'degraded',
isDefault: true,
channelUrl: 'https://life.douyin.com/live',
},
],
});
expect(byType.meituan).toMatchObject({
channelType: 'meituan',
channelLabel: 'Alpha Store',
defaultAccountId: 'alpha',
status: 'connected',
accounts: [
{
accountId: 'alpha',
name: 'Alpha Store',
configured: true,
status: 'connected',
isDefault: true,
agentId: 'agent-b',
bindingScope: 'account',
channelUrl: 'https://meituan.com/a',
},
{
accountId: 'beta',
name: 'Beta Store',
configured: true,
status: 'connected',
isDefault: false,
agentId: 'agent-a',
bindingScope: 'channel',
channelUrl: 'https://me.meituan.com/b',
},
],
});
});
it('builds channel targets from account data, URL hints, and cron history', () => {
mocks.listStoredChannelAccountRecords.mockReturnValue([
{
id: 'acct-1',
channelType: 'douyin',
channelLabel: '抖音直播间',
defaultAccountId: 'acct-1',
channelEnabled: true,
accountId: 'acct-1',
accountName: '抖音直播间',
accountEnabled: true,
channelUrl: 'https://webhook.example.com/send?roomId=ROOM-9#panel?threadId=TH-1',
config: {},
metadata: {},
},
]);
mocks.listCronJobs.mockReturnValue([
{
id: 'cron-1',
name: 'nightly announce',
message: 'hello',
schedule: '* * * * *',
enabled: true,
createdAt: '2026-04-18T00:00:00.000Z',
updatedAt: '2026-04-18T00:00:00.000Z',
delivery: {
mode: 'announce',
channel: 'douyin',
accountId: 'acct-1',
to: 'https://history.example.com/announce',
},
},
{
id: 'cron-2',
name: 'ignored',
message: 'skip me',
schedule: '* * * * *',
enabled: true,
createdAt: '2026-04-18T00:00:00.000Z',
updatedAt: '2026-04-18T00:00:00.000Z',
delivery: {
mode: 'announce',
channel: 'fliggy',
to: 'should-not-appear',
},
},
]);
const targets = listSelectedChannelTargets('douyin', 'acct-1');
expect(targets[0]).toMatchObject({
value: '抖音直播间',
kind: 'name',
source: 'channel-name',
});
expect(targets).toContainEqual(expect.objectContaining({
value: 'acct-1',
label: '账号 ID · acct-1',
kind: 'identifier',
source: 'account-id',
channelType: 'douyin',
accountId: 'acct-1',
}));
expect(targets).toContainEqual(expect.objectContaining({
value: 'ROOM-9',
label: 'Room ID · ROOM-9',
kind: 'identifier',
source: 'query-param',
channelType: 'douyin',
accountId: 'acct-1',
}));
expect(targets).toContainEqual(expect.objectContaining({
value: 'TH-1',
label: 'Thread ID · TH-1',
kind: 'identifier',
source: 'hash-param',
channelType: 'douyin',
accountId: 'acct-1',
}));
expect(targets).toContainEqual(expect.objectContaining({
kind: 'webhook',
source: 'channel-url',
channelType: 'douyin',
accountId: 'acct-1',
}));
expect(targets).toContainEqual(expect.objectContaining({
value: 'https://history.example.com/announce',
kind: 'webhook',
source: 'fallback',
channelType: 'douyin',
accountId: 'acct-1',
}));
const filtered = listSelectedChannelTargets('douyin', 'acct-1', 'history');
expect(filtered).toHaveLength(1);
expect(filtered[0]).toMatchObject({
value: 'https://history.example.com/announce',
});
});
});