1"""Verify local intent acknowledgements separately from the remote publication chain."""
2import re
3
4
5def tokens(text):
6 assert text.startswith('Concurrent edits:'), 'Unexpected append prefix'
7 tail = text[len('Concurrent edits:'):]
8 found = re.findall(r' \[w[0-9]+:[0-9]+\]', tail)
9 assert ''.join(found) == tail and len(set(found)) == len(found), 'Malformed or duplicated append history'
10 return found
11
12
13def publication_links(logs, operations, partial=False):
14 local = {}
15 for actor, events in logs.items():
16 assert events and events[0]['event'] == 'ready', 'Missing client start'
17 assert partial or events[-1]['event'] == 'done', 'Incomplete client log'
18 if not actor.startswith('w'): continue
19 assert events[0].get('offline') is True, 'Expected an offline writer'
20 edits = [event for event in events if event['event'] == 'local_commit']
21 assert [event['operation'] for event in edits] == list(range(len(edits))), 'Missing or duplicate local acknowledgement'
22 assert len(edits) <= operations and (partial or len(edits) == operations), 'Local operation count differs'
23 assert [event['id'] for event in edits] == sorted(event['id'] for event in edits), 'Local IDs went backwards'
24 for event in edits:
25 token = f' [{actor}:{event["operation"]}]'
26 assert event['token'] == token and token not in local, 'Local token differs from its operation'
27 assert event['started_us'] <= event['finished_us'], 'Invalid local acknowledgement interval'
28 prior = tokens(event['before'])
29 own = [item for item in prior if item.startswith(f' [{actor}:')]
30 assert own == [f' [{actor}:{i}]' for i in range(event['operation'])], 'Local view lost or invented its own edit'
31 local[token] = event
32 for event in local.values():
33 for token in tokens(event['before']):
34 assert token in local and local[token]['started_us'] <= event['finished_us'], 'Local view invented a future token'
35 links = {}
36 seen_revisions = set()
37 for actor, events in logs.items():
38 if not actor.startswith('w'): continue
39 # A save replaces the newest pending save of its page, so consecutive operations
40 # can share one intent and one publication.
41 edits = {}
42 for event in events:
43 if event['event'] == 'local_commit': edits.setdefault(event['id'], []).append(event)
44 def once(rows):
45 # The client reports a coalesced intent's receipt once per operation.
46 found, seen = {}, {}
47 for row in rows:
48 assert found.setdefault(row['id'], row)['revision'] == row['revision'], 'One intent has two receipts'
49 seen[row['id']] = seen.get(row['id'], 0) + 1
50 assert all(count == len(edits.get(id, [])) for id, count in seen.items()), 'Duplicate remote receipt'
51 return list(found.values())
52 receipts = once(event for event in events if event['event'] == 'remote_receipt')
53 assert set(event['id'] for event in receipts) <= set(edits), 'Receipt lacks a local intent'
54 assert partial or len(receipts) == len(edits), 'Local success lacks remote acknowledgement'
55 reopened = once(event for event in events if event['event'] == 'reopened_receipt')
56 if not partial:
57 assert [(event['id'], event['revision']) for event in reopened] == [(event['id'], event['revision']) for event in receipts], 'Receipt changed across reopen'
58 for receipt in receipts:
59 group = edits[receipt['id']]
60 intent = {**group[0], 'token': ''.join(event['token'] for event in group)}
61 attempts = [event for event in events if event['event'] == 'remote_attempt' and event['revision'] == receipt['revision']]
62 assert len(attempts) == 1, 'Receipt does not identify one publication attempt'
63 attempt, = attempts
64 assert attempt['state'] in ('Committed', 'Unknown'), 'Receipt identifies a proven-unpublished attempt'
65 if attempt['state'] == 'Unknown':
66 assert all(field in attempt and field in intent for field in ('space', 'object')), 'Uncertain target identity is missing'
67 assert attempt['space'] == intent['space'] and attempt['object'] == intent['object'], 'Confirmation identifies another target'
68 confirmed = [event for event in events if event['event'] == 'remote_confirm'
69 and event['state'] == 'Committed'
70 and receipt['revision'] in event.get('revisions', {}).get(intent['space'], [])
71 and event.get('text', '').startswith(attempt['after'])
72 and attempt['finished_us'] <= event['started_us'] <= event['finished_us'] <= receipt['at_us']]
73 assert confirmed, 'Uncertain publication lacks a successful retained-revision confirmation'
74 assert not any(event['event'] == 'remote_attempt' and event['started_us'] >= attempt['finished_us']
75 and event['after'] == event['before'] + intent['token'] for event in events), 'An uncertain intent was replayed'
76 assert receipt['revision'] not in seen_revisions, 'A revision was acknowledged twice'
77 seen_revisions.add(receipt['revision'])
78 assert intent['started_us'] <= attempt['started_us'] <= attempt['finished_us'] <= receipt['at_us'], 'Receipt precedes its publication'
79 assert attempt['after'] == attempt['before'] + intent['token'], 'Remote publication differs from local intent'
80 tokens(attempt['after'])
81 assert attempt['before'] not in links, 'Remote publications branched from the same content'
82 event = {**attempt, 'event': 'commit', 'operation': group[-1]['operation'], 'operations': len(group), 'token': intent['token'], 'finished_us': receipt['at_us']}
83 links[attempt['before']] = event, attempt['after']
84 return links