115 lines
6.3 KiB
TypeScript
115 lines
6.3 KiB
TypeScript
import 'fake-indexeddb/auto'
|
|
import { bindDatabase, WorklogDB } from '../src/db/schema'
|
|
|
|
const BASE = 'http://127.0.0.1:3780'
|
|
let pass = 0
|
|
let fail = 0
|
|
function check(name: string, ok: boolean, detail?: string): void {
|
|
if (ok) { pass++; console.log(' PASS ' + name) }
|
|
else { fail++; console.log(' FAIL ' + name + (detail ? ' -> ' + detail : '')) }
|
|
}
|
|
|
|
async function main() {
|
|
// 云端注册(清空服务端数据后全新注册)
|
|
await fetch(BASE + '/api/auth/register', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ username: 'twodev', password: 'dev123456' })
|
|
}).catch(() => { /* 已存在则忽略 */ })
|
|
|
|
const engine = await import('../src/sync/engine')
|
|
|
|
// ===== 设备 A:创建项目、工作日志、日志条目、设置 =====
|
|
bindDatabase(new WorklogDB('dev_a'))
|
|
const a = (await import('../src/db/schema')).db
|
|
await a.project.bulkAdd([{ name: '项目A', archived: 0, created_at: '2025-01-01T00:00:00' }])
|
|
await a.work_log.bulkAdd([{ date: '2025-01-02', created_at: '2025-01-02T00:00:00' }])
|
|
await a.setting.bulkAdd([{ key: 'work_start', value: '09:00' }])
|
|
await a.table('log_item').bulkAdd([{
|
|
work_log_id: 1, content: 'A 设备的日志', project_id: 1, category: 'dev',
|
|
minutes: 60, is_milestone: 1, needs_followup: 0, takeaway: null,
|
|
remark: null, sort_order: 0, created_at: '2025-01-02T10:00:00'
|
|
}])
|
|
const rA = await engine.runSync({ baseUrl: BASE, username: 'twodev', password: 'dev123456' })
|
|
console.log('设备A 推送:', JSON.stringify({ p: rA.pushed, e: rA.errors }))
|
|
|
|
// ===== 设备 B:全新空库,拉取设备 A 数据 =====
|
|
bindDatabase(new WorklogDB('dev_b'))
|
|
const b = (await import('../src/db/schema')).db
|
|
const rB1 = await engine.runSync({ baseUrl: BASE, username: 'twodev', password: 'dev123456' })
|
|
console.log('设备B 拉取:', JSON.stringify({ p: rB1.pushed, e: rB1.errors }))
|
|
|
|
const bProjects = await b.project.toArray()
|
|
const bWorkLogs = await b.work_log.toArray()
|
|
const bItems = await b.log_item.toArray()
|
|
const bSettings = await b.setting.toArray()
|
|
check('B 拉到项目', bProjects.length === 1 && bProjects[0].name === '项目A')
|
|
check('B 拉到工作日志', bWorkLogs.length === 1 && bWorkLogs[0].date === '2025-01-02')
|
|
check('B 拉到日志条目', bItems.length === 1 && bItems[0].content === 'A 设备的日志')
|
|
check('B 外键重映射(log_item.work_log_id 指向本地 work_log)', bItems[0].work_log_id === bWorkLogs[0].id)
|
|
check('B 外键重映射(log_item.project_id 指向本地 project)', bItems[0].project_id === bProjects[0].id)
|
|
check('B 拉到设置', bSettings.some(s => s.key === 'work_start' && s.value === '09:00'))
|
|
|
|
// ===== 设备 B:打卡(创建当日考勤与工作日志)+ 新日志条目,推送 =====
|
|
await b.attendance.bulkAdd([{ date: '2025-01-02', clock_in_at: '2025-01-02T09:00:00', clock_out_at: null, kind: 'normal', note: null, created_at: '2025-01-02T09:00:00' }])
|
|
await b.table('log_item').add({
|
|
work_log_id: bWorkLogs[0].id, content: 'B 设备的日志', project_id: null, category: 'dev',
|
|
minutes: null, is_milestone: 0, needs_followup: 1, takeaway: null,
|
|
remark: null, sort_order: 0, created_at: '2025-01-02T11:00:00'
|
|
})
|
|
const rB2 = await engine.runSync({ baseUrl: BASE, username: 'twodev', password: 'dev123456' })
|
|
console.log('设备B 二次推送:', JSON.stringify({ p: rB2.pushed, e: rB2.errors }))
|
|
|
|
// ===== 设备 A:拉取 B 的数据(考勤 + 新日志条目,含自然键去重场景)=====
|
|
bindDatabase(new WorklogDB('dev_a'))
|
|
const a2 = (await import('../src/db/schema')).db
|
|
const rA2 = await engine.runSync({ baseUrl: BASE, username: 'twodev', password: 'dev123456' })
|
|
console.log('设备A 拉取:', JSON.stringify({ p: rA2.pushed, e: rA2.errors }))
|
|
|
|
const aAtt = await a2.attendance.toArray()
|
|
const aItems = await a2.log_item.toArray()
|
|
const aWorkLogs = await a2.work_log.toArray()
|
|
check('A 拉到 B 的考勤', aAtt.length === 1 && aAtt[0].date === '2025-01-02')
|
|
check('A 拉到 B 的日志条目', aItems.some(i => i.content === 'B 设备的日志'))
|
|
check('A 无重复工作日志(自然键去重)', aWorkLogs.length === 1)
|
|
check('A 的日志条目仍然可见(未因去重丢失)', aItems.length >= 2)
|
|
|
|
// ===== 冲突场景:两端为同一天各自创建工作日志后合并 =====
|
|
bindDatabase(new WorklogDB('dev_b'))
|
|
const b2 = (await import('../src/db/schema')).db
|
|
await b2.work_log.bulkPut([{ date: '2025-01-03', created_at: '2025-01-03T00:00:00', uuid: 'b-day3', updated_at: '2025-01-03T08:00:00' }])
|
|
await b2.table('log_item').add({
|
|
work_log_id: 2, content: 'B 的 01-03 记录', project_id: null, category: 'dev',
|
|
minutes: null, is_milestone: 0, needs_followup: 0, takeaway: null,
|
|
remark: null, sort_order: 0, created_at: '2025-01-03T08:00:00', uuid: 'item-b-day3', updated_at: '2025-01-03T08:00:00'
|
|
})
|
|
await b2.table('log_item').add({
|
|
work_log_id: 999, content: 'B 的 01-03 记录(重映射目标)', project_id: null, category: 'dev',
|
|
minutes: null, is_milestone: 0, needs_followup: 0, takeaway: null,
|
|
remark: null, sort_order: 0, created_at: '2025-01-03T08:30:00', uuid: 'item-b-day3b', updated_at: '2025-01-03T08:30:00', wuuid: 'b-day3'
|
|
})
|
|
await engine.runSync({ baseUrl: BASE, username: 'twodev', password: 'dev123456' })
|
|
|
|
bindDatabase(new WorklogDB('dev_a'))
|
|
const a3 = (await import('../src/db/schema')).db
|
|
await a3.work_log.bulkAdd([{ date: '2025-01-03', created_at: '2025-01-03T07:00:00', uuid: 'a-day3', updated_at: '2025-01-03T07:00:00' }])
|
|
await engine.runSync({ baseUrl: BASE, username: 'twodev', password: 'dev123456' })
|
|
|
|
const a3Logs = await a3.work_log.toArray()
|
|
const day3 = a3Logs.filter(l => l.date === '2025-01-03')
|
|
check('冲突合并后同日仅一条工作日志', day3.length === 1)
|
|
console.log(' DEBUG day3:', JSON.stringify(day3))
|
|
const a3Items = await a3.log_item.toArray()
|
|
const day3Items = a3Items.filter(i => i.content.indexOf('01-03') >= 0)
|
|
console.log(' DEBUG day3Items:', JSON.stringify(day3Items))
|
|
check('冲突合并后 B 的 01-03 条目保留且外键正确', day3Items.length === 2 && day3Items.every(i => i.work_log_id === day3[0].id))
|
|
|
|
console.log('==== 结果: PASS ' + pass + ' / FAIL ' + fail + ' ====')
|
|
process.exit(fail > 0 ? 1 : 0)
|
|
}
|
|
|
|
main().catch(e => {
|
|
console.error('FAILED:', (e && (e.stack || e.message)) || e)
|
|
process.exit(1)
|
|
})
|