|
1 | 1 | import assert from 'node:assert/strict'; |
| 2 | +import fs from 'node:fs'; |
2 | 3 | import { test } from 'vitest'; |
3 | 4 | import { AppError } from '@agent-device/kernel/errors'; |
4 | | -import { makeSession } from '../../__tests__/test-utils/session-factories.ts'; |
| 5 | +import { |
| 6 | + makeSession, |
| 7 | + makeRepairCompleteSession, |
| 8 | + makeRepairArmedSession, |
| 9 | + authoringPublication, |
| 10 | +} from '../../__tests__/test-utils/session-factories.ts'; |
5 | 11 | import { makeSessionStore } from '../../__tests__/test-utils/store-factory.ts'; |
6 | 12 |
|
7 | 13 | const ADDRESS = 'cwd:worktree:default'; |
@@ -121,3 +127,47 @@ test('a ref from another store has no authority over the same address', () => { |
121 | 127 | assert.equal(target.retire(foreign), false); |
122 | 128 | assert.equal(target.requireCurrent(local), foreign.session); |
123 | 129 | }); |
| 130 | + |
| 131 | +test('script writes use the latest matching record and refuse a retired lifetime', () => { |
| 132 | + const store = makeSessionStore(); |
| 133 | + const ref = store.publish(ADDRESS, makeSession('default')); |
| 134 | + store.update(ref, { |
| 135 | + scriptPublication: authoringPublication('armed'), |
| 136 | + actions: [{ ts: 1, command: 'click', positionals: ['id="late-action"'], flags: {} }], |
| 137 | + }); |
| 138 | + const result = store.writeSessionLog(ref); |
| 139 | + assert.equal(result.written, true); |
| 140 | + if (result.written) assert.match(fs.readFileSync(result.path, 'utf8'), /late-action/); |
| 141 | + store.retire(ref); |
| 142 | + const successor = store.publish(ADDRESS, makeRepairCompleteSession('default')); |
| 143 | + assert.throws(() => store.writeSessionLog(ref), ended); |
| 144 | + store.finalizeRepairTeardown(ref); |
| 145 | + const state = store.requireCurrent(successor).scriptPublication; |
| 146 | + assert.equal(state?.kind, 'repair'); |
| 147 | + if (state?.kind === 'repair') assert.equal(state.status, 'complete'); |
| 148 | + assert.equal(successor.session.actions.length, 0); |
| 149 | +}); |
| 150 | + |
| 151 | +test('repair tombstones follow the scoped address and cannot be written by a retired ref', () => { |
| 152 | + const store = makeSessionStore(); |
| 153 | + const ref = store.publish(ADDRESS, makeRepairArmedSession('default')); |
| 154 | + store.update(ref, { |
| 155 | + scriptPublication: { |
| 156 | + kind: 'repair', |
| 157 | + status: 'armed', |
| 158 | + boundary: 0, |
| 159 | + target: { kind: 'default', force: false }, |
| 160 | + sourcePath: '/latest.ad', |
| 161 | + }, |
| 162 | + }); |
| 163 | + store.writeRepairTombstone(ref); |
| 164 | + assert.equal(store.readRepairTombstone(ADDRESS)?.owner, ADDRESS); |
| 165 | + assert.equal(store.readRepairTombstone(ADDRESS)?.sourcePath, '/latest.ad'); |
| 166 | + assert.equal(store.readRepairTombstone('default'), undefined); |
| 167 | + store.retire(ref); |
| 168 | + store.clearRepairTombstone(ADDRESS); |
| 169 | + const successor = store.publish(ADDRESS, makeRepairArmedSession('default')); |
| 170 | + store.writeRepairTombstone(ref); |
| 171 | + assert.equal(store.readRepairTombstone(ADDRESS), undefined); |
| 172 | + assert.equal(store.requireCurrent(successor), successor.session); |
| 173 | +}); |
0 commit comments