import fs from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import { describe, expect, it } from 'vitest'; import { NotificationStateStore } from '../src/platforms/xiaohongshu/notification-state.js'; import type { CommentNotification } from '../src/platforms/xiaohongshu/types.js'; function makeNotification(overrides?: Partial): CommentNotification { return { userId: 'u1', nickname: 'tester', avatar: 'https://example.com/a.png', content: '你好', type: '评论了你的笔记', time: '1分钟前', feedId: 'feed123', xsecToken: 'token123', noteImage: 'https://example.com/note.png', ...overrides, }; } describe('notification-state store', () => { it('upserts notifications and tracks status transitions', async () => { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'social-mcp-notif-')); try { const store = new NotificationStateStore(tempDir, 'test.db'); const n1 = makeNotification(); const first = store.upsertNotifications([n1]); expect(first).toEqual({ fetched: 1, inserted: 1, updated: 0 }); const second = store.upsertNotifications([n1]); expect(second).toEqual({ fetched: 1, inserted: 0, updated: 1 }); const openTasks = store.listByStatuses(['new'], 10); expect(openTasks).toHaveLength(1); expect(openTasks[0]?.notification.userId).toBe('u1'); const fp = store.findOpenFingerprint('u1', '你好'); expect(fp).toBeTypeOf('string'); if (!fp) { throw new Error('fingerprint should not be null'); } store.markPending(fp); expect(store.listByStatuses(['pending'], 10)).toHaveLength(1); store.markFailed(fp, 'network error'); const failed = store.listByStatuses(['failed'], 10); expect(failed).toHaveLength(1); expect(failed[0]?.retryCount).toBe(1); expect(failed[0]?.errorMessage).toBe('network error'); store.markReplied(fp, '收到,感谢反馈'); const replied = store.listByStatuses(['replied'], 10); expect(replied).toHaveLength(1); expect(replied[0]?.replyContent).toBe('收到,感谢反馈'); } finally { await fs.rm(tempDir, { recursive: true, force: true }); } }); });