diff --git a/packages/tasks/src/fake.mjs b/packages/tasks/src/fake.mjs index 633224db8..57314be46 100644 --- a/packages/tasks/src/fake.mjs +++ b/packages/tasks/src/fake.mjs @@ -138,7 +138,8 @@ export class FakeVikunja { for (const id of gone) this.#projects.delete(id); } // Drops a view, its buckets and the tasks' places in them. The project still reads, and a read - // of the view answers 404 with code 13001. + // of the view answers 404 with code 3014, as tasks.woltje.com v2.7.0 does (Sage's live probe, + // agents/sage/work/vikunja-setup/2026-10-11_tick-62.json, deletedViewProbe). deleteView(view) { for (const [id, b] of this.#buckets) if (b.view === view) this.#buckets.delete(id); for (const t of this.#tasks.values()) t.buckets.delete(view); @@ -473,14 +474,14 @@ export class FakeVikunja { _buckets(user, [p, k], q) { const r = this.#projectFor(user, p); if (r.error) return r.error; - if (this.#views.get(k)?.project !== p) return { status: 404, body: { title: 'Not Found', status: 404, detail: 'This project view does not exist.', code: 13001 } }; + if (this.#views.get(k)?.project !== p) return { status: 404, body: { title: 'Not Found', status: 404, detail: 'This project view does not exist.', code: 3014 } }; return { status: 200, body: page([...this.#buckets.values()].filter((b) => b.view === k).map((b) => this.#bucketJson(b)), q) }; } _board(user, [p, k], q) { const r = this.#projectFor(user, p); if (r.error) return r.error; const v = this.#views.get(k); - if (v?.project !== p) return { status: 404, body: { title: 'Not Found', status: 404, detail: 'This project view does not exist.', code: 13001 } }; + if (v?.project !== p) return { status: 404, body: { title: 'Not Found', status: 404, detail: 'This project view does not exist.', code: 3014 } }; const f = q.get('filter') ?? ''; if (f && f !== 'done = false') return { status: 400, body: bad('unsupported filter in the fake') }; const per = Math.min(Number(q.get('per_page')) || 50, 50), diff --git a/packages/tasks/tests/tree.test.mjs b/packages/tasks/tests/tree.test.mjs index 5ac99ae51..e325be398 100644 --- a/packages/tasks/tests/tree.test.mjs +++ b/packages/tasks/tests/tree.test.mjs @@ -430,8 +430,8 @@ test('a child whose kanban view is deleted still refuses the tick, and the recon const rootTask = await add(w, w.project, 'root'); await ready(w); const rows = () => w.store.get('SELECT (SELECT count(*) FROM events) + (SELECT count(*) FROM task_snapshots) n').n; - // The board answers 404 with code 13001, and the project still reads, so the child hasn't left - // the tree. The tick reads the project, fails, and writes nothing, not even the root's edit. + // The board answers 404 with code 3014 (the live v2.7.0 answer, tick-62 record), and the project + // still reads, so the child hasn't left the tree. The tick reads the project, fails, and writes nothing, not even the root's edit. w.fake.deleteView(w.fake.kanban(child).id); await w.ui('PATCH', `/tasks/${rootTask}`, { title: 'root renamed' }); await sleep(20); @@ -459,6 +459,17 @@ test('a child whose kanban view is deleted still refuses the tick, and the recon w.fake.fault('GET', new RegExp(`^/projects/${child}/tasks$`), 404); w.fake.fault('GET', new RegExp(`^/projects/${child}$`), 503); await assert.rejects(w.adapter.tick('demo'), { code: 'tracker-not-found' }); + // The confirm read answers another 4xx: the tick still refuses, and the child stays. + w.fake.fault('GET', new RegExp(`^/projects/${child}/tasks$`), 404); + w.fake.fault('GET', new RegExp(`^/projects/${child}$`), 429); + await assert.rejects(w.adapter.tick('demo'), { code: 'tracker-not-found' }); + w.fake.fault('GET', new RegExp(`^/projects/${child}/tasks$`), 404); + w.fake.fault('GET', new RegExp(`^/projects/${child}$`), 401); + await assert.rejects(w.adapter.tick('demo'), { code: 'tracker-not-found' }); + // The confirm read's call fails (status 0, which the client returns for a thrown fetch): the tick refuses. + w.fake.fault('GET', new RegExp(`^/projects/${child}/tasks$`), 404); + w.fake.fault('GET', new RegExp(`^/projects/${child}$`), 0); + await assert.rejects(w.adapter.tick('demo'), { code: 'tracker-not-found' }); await w.ui('PATCH', `/tasks/${task}`, { title: 'child renamed' }); await sleep(20); await w.adapter.tick('demo');