mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-04 03:01:42 +08:00
feat: 新增定时日报与微信发送能力底座
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
vi.mock('electron', () => ({ app: { getPath: () => '/tmp/tracememo-test-user-data' } }))
|
||||
import { PersonalWechatCapabilityService } from '../../src/main/services/personal-wechat-capability-service'
|
||||
import type { PersonalWechatSenderStatus } from '../../src/shared/personal-wechat'
|
||||
|
||||
const status = (
|
||||
overrides: Partial<PersonalWechatSenderStatus> = {}
|
||||
): PersonalWechatSenderStatus => ({
|
||||
state: 'hook_not_ready',
|
||||
platform: 'darwin',
|
||||
arch: 'arm64',
|
||||
sipDisabled: true,
|
||||
wechatRunning: true,
|
||||
endpoint: '127.0.0.1:58080',
|
||||
endpointReady: true,
|
||||
runtimeReady: true,
|
||||
attachReady: true,
|
||||
baseAddressReady: true,
|
||||
textHookInstalled: true,
|
||||
textHookReady: false,
|
||||
imageHookInstalled: true,
|
||||
imageHookReady: false,
|
||||
messageListenerReady: true,
|
||||
canSend: false,
|
||||
canSendText: false,
|
||||
canSendImage: false,
|
||||
canSendVoice: false,
|
||||
message: 'pending',
|
||||
...overrides
|
||||
})
|
||||
|
||||
describe('PersonalWechatCapabilityService', () => {
|
||||
it.each([
|
||||
['unsupported', status({ platform: 'win32' })],
|
||||
['unconfigured', status({ runtimeReady: false, boundWechatPid: undefined })],
|
||||
['needs_binding', status({ runtimeReady: true, boundWechatPid: undefined })],
|
||||
['needs_verification', status({ boundWechatPid: 123 })],
|
||||
['ready', status({ state: 'online', boundWechatPid: 123, canSendImage: true, canSend: true })],
|
||||
['error', status({ state: 'error', boundWechatPid: 123, error: 'hook failed' })]
|
||||
])('maps %s', (expected, senderStatus) => {
|
||||
const service = new PersonalWechatCapabilityService({ getStatus: async () => senderStatus })
|
||||
return expect(service.getPersonalWechatSendCapability()).resolves.toMatchObject({
|
||||
status: expected,
|
||||
ready: expected === 'ready',
|
||||
supported: expected !== 'unsupported'
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -6,6 +6,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
vi.mock('electron', () => ({
|
||||
app: {
|
||||
getAppPath: () => '/fixture/app',
|
||||
getPath: () => '/tmp/tracememo-test-user-data',
|
||||
isPackaged: false
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { mkdtemp, readFile } from 'fs/promises'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
vi.mock('electron', () => ({ app: { getPath: () => '/tmp/tracememo-test-user-data' } }))
|
||||
import {
|
||||
calculateNextRunAt,
|
||||
ScheduledReportService,
|
||||
validateScheduleTime
|
||||
} from '../../src/main/services/scheduled-report-service'
|
||||
import type { PersonalWechatSendCapability } from '../../src/shared/personal-wechat'
|
||||
|
||||
const capability: PersonalWechatSendCapability = {
|
||||
supported: true,
|
||||
ready: true,
|
||||
status: 'ready',
|
||||
capabilities: { text: true, image: true, voice: true },
|
||||
senderStatus: {} as never,
|
||||
message: 'ready'
|
||||
}
|
||||
|
||||
describe('scheduled report scheduling', () => {
|
||||
it('validates daily HH:mm and computes the next local occurrence', () => {
|
||||
expect(validateScheduleTime('09:05')).toBe(true)
|
||||
expect(validateScheduleTime('24:00')).toBe(false)
|
||||
const from = new Date('2026-08-27T10:00:00+08:00')
|
||||
expect(calculateNextRunAt('09:05', from)).toBe('2026-08-28T01:05:00.000Z')
|
||||
expect(calculateNextRunAt('11:05', from)).toBe('2026-08-27T03:05:00.000Z')
|
||||
})
|
||||
|
||||
it('persists lifecycle, executes generation and image sending, and restores tasks', async () => {
|
||||
const storageDir = await mkdtemp(join(tmpdir(), 'tracememo-scheduled-report-'))
|
||||
const now = new Date('2026-08-27T01:00:00.000Z')
|
||||
const generatedPath = join(storageDir, 'report.png')
|
||||
let generated = 0
|
||||
let sent = 0
|
||||
const service = new ScheduledReportService({
|
||||
storageDir,
|
||||
now: () => now,
|
||||
getCapability: async () => capability,
|
||||
generateReport: async () => {
|
||||
generated += 1
|
||||
return { success: true, pngPath: generatedPath }
|
||||
},
|
||||
send: async () => {
|
||||
sent += 1
|
||||
return { success: true, status: capability.senderStatus }
|
||||
},
|
||||
isDatabaseReady: () => true
|
||||
})
|
||||
const created = await service.createTask({
|
||||
name: '每日群报',
|
||||
group: '研发群',
|
||||
target: '研发群@chatroom',
|
||||
scheduleTime: '09:00'
|
||||
})
|
||||
expect(created.success).toBe(true)
|
||||
expect(created.data?.reportRange).toBe('yesterday')
|
||||
const taskId = created.data!.id
|
||||
const run = await service.runScheduledReportNow(taskId)
|
||||
expect(run.data?.status).toBe('success')
|
||||
expect(generated).toBe(1)
|
||||
expect(sent).toBe(1)
|
||||
expect(await service.listExecutions(taskId)).toHaveLength(1)
|
||||
const restored = new ScheduledReportService({
|
||||
storageDir,
|
||||
getCapability: async () => capability
|
||||
})
|
||||
expect((await restored.listTasks())[0].id).toBe(taskId)
|
||||
await service.setTaskEnabled(taskId, false)
|
||||
expect((await service.listTasks())[0].enabled).toBe(false)
|
||||
await service.deleteTask(taskId)
|
||||
expect(await service.listTasks()).toHaveLength(0)
|
||||
expect(
|
||||
JSON.parse(await readFile(join(storageDir, 'executions.json'), 'utf8'))[0].message
|
||||
).toContain('微信发送成功')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user