From 197f644d53b9b0226a724d9ff76ad148ed5ff6f0 Mon Sep 17 00:00:00 2001 From: duanshuwen Date: Tue, 21 Apr 2026 23:27:15 +0800 Subject: [PATCH] test: remove obsolete test files for channels and knowledge page --- tests/channels.test.ts | 440 ---------------------------------- tests/knowledge-page.test.tsx | 101 -------- 2 files changed, 541 deletions(-) delete mode 100644 tests/channels.test.ts delete mode 100644 tests/knowledge-page.test.tsx diff --git a/tests/channels.test.ts b/tests/channels.test.ts deleted file mode 100644 index 625fe8b..0000000 --- a/tests/channels.test.ts +++ /dev/null @@ -1,440 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; - -const mocks = vi.hoisted(() => ({ - configGet: vi.fn(), - listStoredChannelAccountRecords: vi.fn(), - listCronJobs: vi.fn(), - listAgentsSnapshot: vi.fn(), - assignChannelToAgent: vi.fn(), - clearAllChannelBindings: vi.fn(), - clearChannelBinding: vi.fn(), - listAgentsSnapshotResult: { - agents: [], - channelOwners: {}, - channelAccountOwners: {}, - }, -})); - -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, -})); - -vi.mock('../electron/utils/agent-config', () => ({ - assignChannelToAgent: mocks.assignChannelToAgent, - clearAllChannelBindings: mocks.clearAllChannelBindings, - clearChannelBinding: mocks.clearChannelBinding, - listAgentsSnapshot: mocks.listAgentsSnapshot, -})); - -import { - getSelectedChannelsConfig, - listSelectedChannelAccountGroups, - listSelectedChannelTargets, -} from '../electron/utils/channels'; -import { normalizeRequest } from '../electron/api/route-utils'; -import { handleChannelRoutes } from '../electron/api/routes/channels'; - -function createRouteContext() { - const notifyRuntimeChanged = vi.fn(); - return { - gatewayManager: { - notifyRuntimeChanged, - }, - providerApiService: { - getAccounts: () => [], - getDefault: () => ({ accountId: 'default' }), - }, - mainWindow: null, - notifyRuntimeChanged, - } as any; -} - -describe('channels utilities', () => { - beforeEach(() => { - mocks.configGet.mockReset(); - mocks.listStoredChannelAccountRecords.mockReset(); - mocks.listCronJobs.mockReset(); - mocks.listAgentsSnapshot.mockReset(); - mocks.assignChannelToAgent.mockReset(); - mocks.clearAllChannelBindings.mockReset(); - mocks.clearChannelBinding.mockReset(); - mocks.listAgentsSnapshot.mockReturnValue(mocks.listAgentsSnapshotResult); - }); - - 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', - }); - }); -}); - -describe('channel credential validation route', () => { - it('returns a valid result with warnings for a token/password/url payload', async () => { - const ctx = createRouteContext(); - - const response = await handleChannelRoutes(normalizeRequest({ - path: '/api/channels/credentials/validate', - method: 'POST', - body: JSON.stringify({ - channelType: 'imessage', - accountId: 'acct-1', - credentials: { - serverUrl: 'http://bridge.example.com', - password: 'bridge-password', - }, - }), - }), ctx); - - expect(response?.ok).toBe(true); - expect(response?.json).toMatchObject({ - success: true, - valid: true, - errors: [], - warnings: ['Server URL 使用了不安全的 http URL,建议改用 https'], - details: expect.objectContaining({ - channelType: 'imessage', - accountId: 'acct-1', - connectionType: 'token', - }), - }); - - expect(response?.json).toMatchObject({ - details: expect.objectContaining({ - fields: expect.arrayContaining([ - expect.objectContaining({ - key: 'serverUrl', - kind: 'url', - provided: true, - valid: true, - warnings: ['Server URL 使用了不安全的 http URL,建议改用 https'], - }), - expect.objectContaining({ - key: 'password', - kind: 'password', - provided: true, - valid: true, - }), - ]), - }), - }); - - expect(ctx.notifyRuntimeChanged).not.toHaveBeenCalled(); - }); - - it('returns field errors for missing token and text credentials', async () => { - const ctx = createRouteContext(); - - const response = await handleChannelRoutes(normalizeRequest({ - path: '/api/channels/credentials/validate', - method: 'POST', - body: JSON.stringify({ - channelType: 'discord', - credentials: { - token: ' ', - guildId: '', - channelId: '123456789012345678', - }, - }), - }), ctx); - - expect(response?.ok).toBe(true); - expect(response?.json).toMatchObject({ - success: true, - valid: false, - warnings: [], - errors: [ - 'Bot Token is required', - 'Guild ID is required', - ], - details: expect.objectContaining({ - channelType: 'discord', - connectionType: 'token', - }), - }); - - expect(response?.json).toMatchObject({ - details: expect.objectContaining({ - fields: expect.arrayContaining([ - expect.objectContaining({ - key: 'token', - kind: 'password', - provided: false, - valid: false, - errors: ['Bot Token is required'], - }), - expect.objectContaining({ - key: 'guildId', - kind: 'text', - provided: false, - valid: false, - errors: ['Guild ID is required'], - }), - expect.objectContaining({ - key: 'channelId', - kind: 'text', - provided: true, - valid: true, - }), - ]), - }), - }); - - expect(ctx.notifyRuntimeChanged).not.toHaveBeenCalled(); - }); -}); diff --git a/tests/knowledge-page.test.tsx b/tests/knowledge-page.test.tsx deleted file mode 100644 index ef96b60..0000000 --- a/tests/knowledge-page.test.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React from 'react'; -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import { beforeEach, describe, expect, it, vi } from 'vitest'; - -const apiMocks = vi.hoisted(() => ({ - list: vi.fn(), - upload: vi.fn(), - delete: vi.fn(), -})); - -vi.mock('../src/lib/knowledge-docs-api', () => ({ - knowledgeDocsApi: apiMocks, -})); - -vi.mock('../src/pages/Knowledge/copy', () => ({ - useKnowledgeCopy: () => (path: string, params?: Record, fallback?: string) => { - const dictionary: Record = { - 'knowledge.title': 'Knowledge Docs', - 'knowledge.subtitle': 'Manage docs', - 'knowledge.documentsLabel': 'Documents', - 'knowledge.storageLabel': 'Storage', - 'knowledge.refresh': 'Refresh', - 'knowledge.upload': 'Upload', - 'knowledge.status.loading': 'Loading documents...', - 'knowledge.status.uploading': 'Uploading...', - 'knowledge.status.deleting': 'Deleting...', - 'knowledge.status.uploadSuccess': 'Uploaded', - 'knowledge.status.deleteSuccess': 'Deleted', - 'knowledge.emptyTitle': 'No documents yet', - 'knowledge.emptyDescription': 'Upload a file', - 'knowledge.deleteDialog.title': 'Delete document?', - 'knowledge.deleteDialog.confirm': 'Delete document', - 'knowledge.deleteDialog.close': 'Close dialog', - 'knowledge.common.cancel': 'Cancel', - 'knowledge.table.name': 'Name', - 'knowledge.table.size': 'Size', - 'knowledge.table.modifiedAt': 'Modified At', - 'knowledge.table.type': 'Type', - 'knowledge.table.actions': 'Actions', - 'knowledge.table.delete': 'Delete', - }; - - if (path === 'knowledge.status.failed') { - return `Knowledge docs request failed: ${params?.error ?? ''}`; - } - - if (path === 'knowledge.deleteDialog.description') { - return `This will permanently remove "${params?.name ?? ''}" from the local knowledge docs directory.`; - } - - return dictionary[path] ?? fallback ?? path; - }, -})); - -import KnowledgePage from '../src/pages/Knowledge'; - -describe('KnowledgePage', () => { - beforeEach(() => { - apiMocks.list.mockReset(); - apiMocks.upload.mockReset(); - apiMocks.delete.mockReset(); - }); - - it('loads and renders docs from the knowledge docs api', async () => { - apiMocks.list.mockResolvedValue([ - { - name: 'guide.md', - size: 1024, - modifiedAt: '2026-04-18T10:00:00.000Z', - type: 'md', - }, - ]); - - render(); - - expect(await screen.findByText('guide.md')).toBeTruthy(); - expect(screen.getByText('MD')).toBeTruthy(); - }); - - it('deletes a doc after confirmation', async () => { - apiMocks.list.mockResolvedValue([ - { - name: 'guide.md', - size: 1024, - modifiedAt: '2026-04-18T10:00:00.000Z', - type: 'md', - }, - ]); - apiMocks.delete.mockResolvedValue(undefined); - - render(); - - const deleteButton = await screen.findByRole('button', { name: /delete/i }); - fireEvent.click(deleteButton); - fireEvent.click(screen.getByRole('button', { name: 'Delete document' })); - - await waitFor(() => { - expect(apiMocks.delete).toHaveBeenCalledWith('guide.md'); - }); - }); -});