mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-04 03:01:42 +08:00
feat: 新增本地微信查询工具 API
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
||||
|
||||
const fixture = vi.hoisted(() => ({
|
||||
contacts: [
|
||||
{ m_nsUsrName: 'wxid-bobo', m_nsNickName: 'BOBO', md5: 'md5-bobo', type: 'user' as const },
|
||||
{ m_nsUsrName: 'wxid-bobo-2', m_nsNickName: 'BOBO', md5: 'md5-bobo-2', type: 'user' as const }
|
||||
],
|
||||
messages: [
|
||||
{ id: 'm1', from: 'user', type: '图片', datetime: '2026/8/2 10:00:00', content: '', contentData: { type: 'image', md5: 'image-md5' }, isSender: false, name: 'BOBO', createTime: Math.floor(new Date('2026-08-02T10:00:00+08:00').getTime() / 1000) },
|
||||
{ id: 'm2', from: 'assistant', type: '文件', datetime: '2026/8/3 10:00:00', content: '', isSender: true, exportMediaType: 'file' as const, exportMediaName: 'a.pdf', createTime: Math.floor(new Date('2026-08-03T10:00:00+08:00').getTime() / 1000) }
|
||||
]
|
||||
}))
|
||||
vi.mock('../../src/main/services/chat-service', () => ({
|
||||
isReady: () => true,
|
||||
listContactsAsync: vi.fn(async () => fixture.contacts),
|
||||
listMessagesAsync: vi.fn(async () => fixture.messages)
|
||||
}))
|
||||
|
||||
import { LocalQueryApiService } from '../../src/main/services/local-query-api-service'
|
||||
|
||||
describe('LocalQueryApiService', () => {
|
||||
const knowledge = { search: vi.fn(async () => ({ state: 'ready', evidence: [{ conversationId: 'md5-bobo', messageId: 'm1', timestamp: 1, sender: 'BOBO', sourceKind: 'text', text: '你好' }], conversationRetrieval: { totalMessages: 2, chunkCount: 1, complete: true }, voiceCoverage: undefined })) } as any
|
||||
let service: LocalQueryApiService
|
||||
beforeEach(() => { vi.clearAllMocks(); if (!fixture.contacts.some((contact) => contact.md5 === 'md5-bobo-2')) fixture.contacts.push({ m_nsUsrName: 'wxid-bobo-2', m_nsNickName: 'BOBO', md5: 'md5-bobo-2', type: 'user' }); service = new LocalQueryApiService(knowledge, () => new Date('2026-09-09T12:00:00+08:00')) })
|
||||
|
||||
it('publishes capability contract and resolves previous month', async () => {
|
||||
expect(service.capabilities().tools.query_messages.messageTypes).toContain('file')
|
||||
const result = await service.messages({ target: { query: 'BOBO' }, timeRange: { kind: 'previous_month' }, direction: 'any', order: 'asc', limit: 1 })
|
||||
expect(result.status).toBe('ambiguous_contact')
|
||||
fixture.contacts.splice(1)
|
||||
const resolved = await service.messages({ target: { query: 'BOBO' }, timeRange: { kind: 'previous_month' }, direction: 'any', order: 'asc', limit: 1 })
|
||||
expect(resolved.query?.resolvedTimeRange).toMatchObject({ startTime: expect.any(Number), endTime: expect.any(Number), label: '2026年8月' })
|
||||
expect(resolved.returnedCount).toBe(1)
|
||||
})
|
||||
|
||||
it('filters direction and message type, and rejects too many variants', async () => {
|
||||
fixture.contacts.splice(1)
|
||||
const result = await service.messages({ target: { query: 'BOBO' }, timeRange: { kind: 'all' }, direction: 'to_target', messageTypes: ['file'], limit: 1 })
|
||||
expect(result.messages?.[0].sourceKind).toBe('file')
|
||||
await expect(service.search({ target: { query: 'BOBO' }, timeRange: { kind: 'all' }, query: 'x', variants: ['1', '2', '3', '4', '5'] })).resolves.toMatchObject({ status: 'invalid_request' })
|
||||
})
|
||||
|
||||
it('returns canonical type and attachment for the earliest non-text message', async () => {
|
||||
fixture.contacts.splice(1)
|
||||
const result = await service.messages({ target: { query: 'BOBO' }, timeRange: { kind: 'all' }, order: 'asc', limit: 1, excludeSystem: true })
|
||||
expect(result.messages?.[0]).toMatchObject({ messageType: 'image', sourceKind: 'image', attachment: { kind: 'image' } })
|
||||
expect(result.messages?.[0]).not.toHaveProperty('text')
|
||||
})
|
||||
|
||||
it('sorts overview evidence while preserving the selected set and exposes sampling', async () => {
|
||||
fixture.contacts.splice(1)
|
||||
knowledge.search.mockResolvedValueOnce({ state: 'ready', evidence: [
|
||||
{ conversationId: 'md5-bobo', messageId: 'late', timestamp: 300, sender: 'BOBO', sourceKind: 'text', text: 'late' },
|
||||
{ conversationId: 'md5-bobo', messageId: 'early', timestamp: 100, sender: 'BOBO', sourceKind: 'text', text: 'early' }
|
||||
], conversationRetrieval: { totalMessages: 82, chunkCount: 4, complete: true }, voiceCoverage: undefined })
|
||||
const result = await service.overview({ target: { query: 'BOBO' }, timeRange: { kind: 'previous_month' } })
|
||||
expect(result.sourceCoverage).toEqual({ state: 'complete', sourceMessageCount: 82 })
|
||||
expect(result.selection).toEqual({ mode: 'temporal_coverage', selectedEvidenceCount: 2, sampled: true })
|
||||
expect(result.evidence?.map((item) => item.timestamp)).toEqual([100, 300])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user