Unified activity timeline in Conversation: joins, questions, resolutions, contract, agreements
deploy / deploy (push) Canceled after 0s

This commit is contained in:
2026-09-06 22:26:48 +03:00
parent fde1ae152f
commit 2e55a383ea
8 changed files with 198 additions and 36 deletions
+32 -1
View File
@@ -1,6 +1,6 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { RendezvousService, Store, RendezvousError, redactSecrets } from '../src/index.js';
import { RendezvousService, Store, RendezvousError, redactSecrets, buildTimeline } from '../src/index.js';
function makeService(): { svc: RendezvousService; dbPath: string } {
const dbPath = `:memory:`;
@@ -220,3 +220,34 @@ test('only addressee or author can resolve a question', () => {
const resolved = svc.resolveQuestion(created.room_id, A, q.id, 'checked: ok');
assert.equal(resolved.status, 'resolved');
});
test('activity timeline includes every action of every participant', () => {
const { svc } = makeService();
const created = createTwoPartyRoom(svc);
const [A, B] = created.participants.map((p) => p.token);
svc.join(created.room_id, A);
svc.join(created.room_id, B);
svc.postMessage(created.room_id, A, 'fact from A');
const q = svc.openQuestion(created.room_id, B, 'check IIS logs', true, created.participants[0].id);
svc.resolveQuestion(created.room_id, A, q.id, 'checked: every 15 min');
svc.proposeContract(created.room_id, B, '## Facts\n- ok');
svc.agree(created.room_id, A);
svc.agree(created.room_id, B);
const view = svc.getRoomView(created.room_id, A);
const timeline = buildTimeline({
participants: view.participants,
conversation: view.conversation,
openQuestions: view.open_questions,
resolvedQuestions: view.resolved_questions,
contract: view.current_contract,
});
const kinds: string[] = timeline.map((e) => e.kind);
for (const expected of ['joined', 'joined', 'message', 'asked', 'resolved', 'contract', 'agreed', 'agreed'])
assert.ok(kinds.includes(expected), `timeline must contain "${expected}", got: ${kinds.join(',')}`);
// chronological order
for (let i = 1; i < timeline.length; i++)
assert.ok(timeline[i - 1].at <= timeline[i].at, 'timeline must be sorted by time');
// every event is attributed to a role
for (const e of timeline) assert.ok(e.role.length > 0 && e.action.length > 0);
});