Skip to content

Commit 26ad71d

Browse files
committed
refactor(sync-test-utils): streamline message builders for invalid/malformed messages
1 parent 05473bc commit 26ad71d

File tree

1 file changed

+40
-100
lines changed

1 file changed

+40
-100
lines changed

packages/epicenter/tests/helpers/sync-test-utils.ts

Lines changed: 40 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,72 @@
11
/**
22
* Test utilities for y-websocket protocol testing.
33
*
4-
* Provides helpers for building valid and invalid protocol messages,
5-
* and utilities for test coordination.
4+
* Provides helpers for building invalid/malformed messages and test coordination.
65
*
7-
* NOTE: For MESSAGE_TYPE, decodeSyncMessage, and decodeMessageType,
6+
* NOTE: For protocol functions (MESSAGE_TYPE, encodeSyncStep1, etc.),
87
* import directly from '../../src/server/sync/protocol'.
98
*/
109

1110
import * as encoding from 'lib0/encoding';
12-
import * as awarenessProtocol from 'y-protocols/awareness';
13-
import * as syncProtocol from 'y-protocols/sync';
1411
import * as Y from 'yjs';
15-
import { MESSAGE_TYPE } from '../../src/server/sync/protocol';
1612

1713
// ============================================================================
18-
// Valid Message Builders
14+
// Invalid/Malformed Message Builders (for edge case testing)
1915
// ============================================================================
2016

2117
/**
22-
* Build a sync step 1 message containing a state vector.
23-
* This is what a client sends to ask "what updates are you missing?"
18+
* Build a truncated message of the specified type.
19+
* Contains only the message type byte with no payload.
2420
*/
25-
export function buildSyncStep1(doc: Y.Doc): Uint8Array {
21+
export function buildTruncatedMessage(messageType: number): Uint8Array {
2622
return encoding.encode((encoder) => {
27-
encoding.writeVarUint(encoder, MESSAGE_TYPE.SYNC);
28-
syncProtocol.writeSyncStep1(encoder, doc);
23+
encoding.writeVarUint(encoder, messageType);
2924
});
3025
}
3126

3227
/**
33-
* Build a sync step 2 message containing a document diff.
34-
* This is the response to sync step 1.
28+
* Build a message with an unknown message type.
3529
*/
36-
export function buildSyncStep2(doc: Y.Doc, stateVector?: Uint8Array): Uint8Array {
30+
export function buildUnknownTypeMessage(unknownType: number): Uint8Array {
3731
return encoding.encode((encoder) => {
38-
encoding.writeVarUint(encoder, MESSAGE_TYPE.SYNC);
39-
if (stateVector) {
40-
// Generate diff based on provided state vector
41-
const update = Y.encodeStateAsUpdate(doc, stateVector);
42-
syncProtocol.writeUpdate(encoder, update);
43-
} else {
44-
// Write full document as sync step 2
45-
syncProtocol.writeSyncStep2(encoder, doc);
46-
}
32+
encoding.writeVarUint(encoder, unknownType);
33+
encoding.writeVarString(encoder, 'unknown payload');
4734
});
4835
}
4936

5037
/**
51-
* Build a sync update message containing incremental changes.
38+
* Build an awareness message with malformed JSON state.
5239
*/
53-
export function buildSyncUpdate(update: Uint8Array): Uint8Array {
54-
return encoding.encode((encoder) => {
55-
encoding.writeVarUint(encoder, MESSAGE_TYPE.SYNC);
56-
syncProtocol.writeUpdate(encoder, update);
40+
export function buildMalformedAwarenessMessage(): Uint8Array {
41+
const MESSAGE_TYPE_AWARENESS = 1;
42+
43+
// Create a raw awareness update with invalid JSON
44+
const innerUpdate = encoding.encode((encoder) => {
45+
encoding.writeVarUint(encoder, 1); // 1 entry
46+
encoding.writeVarUint(encoder, 12345); // clientId
47+
encoding.writeVarUint(encoder, 1); // clock
48+
encoding.writeVarString(encoder, '{invalid json}'); // malformed JSON
5749
});
58-
}
5950

60-
/**
61-
* Build an awareness update message.
62-
*/
63-
export function buildAwarenessUpdate(
64-
awareness: awarenessProtocol.Awareness,
65-
clientIds?: number[],
66-
): Uint8Array {
67-
const clients = clientIds ?? Array.from(awareness.getStates().keys());
68-
const update = awarenessProtocol.encodeAwarenessUpdate(awareness, clients);
6951
return encoding.encode((encoder) => {
70-
encoding.writeVarUint(encoder, MESSAGE_TYPE.AWARENESS);
71-
encoding.writeVarUint8Array(encoder, update);
52+
encoding.writeVarUint(encoder, MESSAGE_TYPE_AWARENESS);
53+
encoding.writeVarUint8Array(encoder, innerUpdate);
7254
});
7355
}
7456

7557
/**
7658
* Build a raw awareness update message from explicit data.
7759
* Useful for testing malformed or edge case awareness states.
7860
*/
79-
export function buildRawAwarenessMessage(entries: Array<{
80-
clientId: number;
81-
clock: number;
82-
state: string | null; // JSON string or null
83-
}>): Uint8Array {
61+
export function buildRawAwarenessMessage(
62+
entries: Array<{
63+
clientId: number;
64+
clock: number;
65+
state: string | null; // JSON string or null
66+
}>,
67+
): Uint8Array {
68+
const MESSAGE_TYPE_AWARENESS = 1;
69+
8470
// Build the inner awareness update
8571
const innerUpdate = encoding.encode((encoder) => {
8672
encoding.writeVarUint(encoder, entries.length);
@@ -93,58 +79,7 @@ export function buildRawAwarenessMessage(entries: Array<{
9379

9480
// Wrap in message envelope
9581
return encoding.encode((encoder) => {
96-
encoding.writeVarUint(encoder, MESSAGE_TYPE.AWARENESS);
97-
encoding.writeVarUint8Array(encoder, innerUpdate);
98-
});
99-
}
100-
101-
/**
102-
* Build a query awareness message.
103-
*/
104-
export function buildQueryAwareness(): Uint8Array {
105-
return encoding.encode((encoder) => {
106-
encoding.writeVarUint(encoder, MESSAGE_TYPE.QUERY_AWARENESS);
107-
});
108-
}
109-
110-
// ============================================================================
111-
// Invalid/Malformed Message Builders (for edge case testing)
112-
// ============================================================================
113-
114-
/**
115-
* Build a truncated message of the specified type.
116-
* Contains only the message type byte with no payload.
117-
*/
118-
export function buildTruncatedMessage(messageType: number): Uint8Array {
119-
return encoding.encode((encoder) => {
120-
encoding.writeVarUint(encoder, messageType);
121-
});
122-
}
123-
124-
/**
125-
* Build a message with an unknown message type.
126-
*/
127-
export function buildUnknownTypeMessage(unknownType: number): Uint8Array {
128-
return encoding.encode((encoder) => {
129-
encoding.writeVarUint(encoder, unknownType);
130-
encoding.writeVarString(encoder, 'unknown payload');
131-
});
132-
}
133-
134-
/**
135-
* Build an awareness message with malformed JSON state.
136-
*/
137-
export function buildMalformedAwarenessMessage(): Uint8Array {
138-
// Create a raw awareness update with invalid JSON
139-
const innerUpdate = encoding.encode((encoder) => {
140-
encoding.writeVarUint(encoder, 1); // 1 entry
141-
encoding.writeVarUint(encoder, 12345); // clientId
142-
encoding.writeVarUint(encoder, 1); // clock
143-
encoding.writeVarString(encoder, '{invalid json}'); // malformed JSON
144-
});
145-
146-
return encoding.encode((encoder) => {
147-
encoding.writeVarUint(encoder, MESSAGE_TYPE.AWARENESS);
82+
encoding.writeVarUint(encoder, MESSAGE_TYPE_AWARENESS);
14883
encoding.writeVarUint8Array(encoder, innerUpdate);
14984
});
15085
}
@@ -187,7 +122,10 @@ export function wait(ms: number): Promise<void> {
187122
/**
188123
* Create a Y.Doc with some initial content for testing.
189124
*/
190-
export function createTestDoc(content?: { mapKey?: string; mapValue?: string }): Y.Doc {
125+
export function createTestDoc(content?: {
126+
mapKey?: string;
127+
mapValue?: string;
128+
}): Y.Doc {
191129
const doc = new Y.Doc();
192130
if (content) {
193131
doc.getMap('test').set(content.mapKey ?? 'key', content.mapValue ?? 'value');
@@ -223,7 +161,9 @@ export function parseWsMessage(data: unknown): Uint8Array | null {
223161
try {
224162
const parsed = JSON.parse(data);
225163
if (typeof parsed === 'object' && parsed !== null) {
226-
const keys = Object.keys(parsed).map(Number).sort((a, b) => a - b);
164+
const keys = Object.keys(parsed)
165+
.map(Number)
166+
.sort((a, b) => a - b);
227167
const arr = new Uint8Array(keys.length);
228168
for (let i = 0; i < keys.length; i++) {
229169
arr[i] = parsed[keys[i]];

0 commit comments

Comments
 (0)