• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rustymotors / server / 27998838094

23 Jun 2026 02:51AM UTC coverage: 45.083% (+0.8%) from 44.3%
27998838094

push

github

drazisil
Add handler-level tests for relay routing and start broadcast

Tests use vi.hoisted + vi.mock('rusty-motors-shared', importOriginal) to
intercept getSocketQueue/getChannelMembers/getConnectionIdByUserId/databaseProvider
without disrupting real ChannelMembership state or other shared utilities.

Covers:
- handleSendSingleLong: delivers rewritten 16-byte envelope to target, skips self/unknown
- handleSendBuddyLong: same routing contract verified independently (opcode 0x93)
- handleSendNotSingleLong: broadcasts to all members except sender and excluded user
- handleStartGameServer: returns 3 packets to host, pushes all 3 to peers via queue,
  throws on missing userId

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

703 of 1760 branches covered (39.94%)

Branch coverage included in aggregate %.

4184 of 9080 relevant lines covered (46.08%)

133.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.65
/packages/protocol/src/GameMessageHeader.ts
1
import { BufferSerializer } from "./BufferSerializer.js";
2
import type { SerializableInterface } from "./types.js";
3

4
/**
5
 * Represents the header of a game message.
6
 * The header contains the message ID, the length of the message data, 
7
 * and the version of the message.
8
 * 
9
 * This is a big-endian structure.
10
 */
11
export class GameMessageHeader
12
        extends BufferSerializer
13
        implements SerializableInterface
14
{
15
        private id: number = 0; // 2 bytes
9✔
16
        private length: number = 0; // 2 bytes
9✔
17
        private version: 0 | 257 = 257; // 2 bytes
9✔
18

19
        private shouldEncryptPayload: boolean = false;
9✔
20

21
        constructor() {
22
                super();
9✔
23
        }
24

25
        static copy(header: GameMessageHeader): GameMessageHeader {
26
                const newHeader = new GameMessageHeader();
1✔
27
                newHeader.id = header.id;
1✔
28
                newHeader.length = header.length;
1✔
29
                newHeader.version = header.version;
1✔
30
                return newHeader;
1✔
31
        }
32

33
        override getByteSize(): number {
34
                return this.getVersion() === 257 ? 12 : 4;
2✔
35
        }
36

37
        getVersion(): number {
38
                return this.version;
4✔
39
        }
40
        getId(): number {
41
                return this.id;
3✔
42
        }
43
        getLength(): number {
44
                return this.length;
3✔
45
        }
46
        setVersion(version: 0 | 257): void {
47
                if (version !== 0 && version !== 257) {
6!
48
                        throw new Error(`Invalid version ${parseInt(version)}`);
×
49
                }
50
                this.version = version;
6✔
51
        }
52
        setId(id: number): void {
53
                this.id = id;
4✔
54
        }
55
        setLength(length: number): void {
56
                this.length = length;
4✔
57
        }
58

59
        private serializeV0(): Buffer {
60
                const buffer = Buffer.alloc(this.getByteSize());
1✔
61
                buffer.writeUInt16BE(this.id, 0);
1✔
62
                buffer.writeUInt16BE(this.length, 2);
1✔
63

64
                return buffer;
1✔
65
        }
66

67
        private serializeV1(): Buffer {
68
                const buffer = Buffer.alloc(this.getByteSize());
1✔
69
                buffer.writeUInt16BE(this.id, 0);
1✔
70
                buffer.writeUInt16BE(this.length, 2);
1✔
71
                buffer.writeUInt16BE(this.version, 4);
1✔
72
                buffer.writeUInt16BE(0, 6);
1✔
73
                buffer.writeUInt32BE(this.length, 8);
1✔
74

75
                return buffer;
1✔
76
        }
77

78
        override serialize(): Buffer {
79
                return this.version === 257 ? this.serializeV1() : this.serializeV0();
2✔
80
        }
81

82
        private deserializeV0(data: Buffer): void {
83
                this.id = data.readUInt16BE(0);
1✔
84
                this.length = data.readUInt16BE(2);
1✔
85
        }
86

87
        private assertV1Checksum(data: Buffer): void {
88
                const length = data.readUInt16BE(2);
1✔
89
                const checksum = data.readUInt32BE(8);
1✔
90
                if (checksum !== length) {
1!
91
                        throw new Error(
×
92
                                `Checksum mismatch. Expected ${length}, got ${checksum}`,
93
                        );
94
                }
95
        }
96

97
        private deserializeV1(data: Buffer): void {
98
                this.assertV1Checksum(data);
1✔
99
                this.id = data.readUInt16BE(0);
1✔
100
                this.length = data.readUInt16BE(2);
1✔
101
                // Skip version
102
                // Skip padding
103
                this.length = data.readUInt32BE(8);
1✔
104
        }
105

106
        override deserialize(data: Buffer): void {
107
                if (data.length < 4) {
3✔
108
                        throw new Error(
1✔
109
                                `Data is too short. Expected at least 4 bytes, got ${data.length} bytes`,
110
                        );
111
                }
112

113
                if (this.version === 257) {
2✔
114
                        this.deserializeV1(data);
1✔
115
                } else {
116
                        this.deserializeV0(data);
1✔
117
                }
118
        }
119

120
        /**
121
         * Sets the encryption status for the payload.
122
         *
123
         * @param encrypted - A boolean indicating whether the payload should be encrypted (true) or not (false).
124
         */
125
        setPayloadEncryption(encrypted: boolean): void {
126
                this.shouldEncryptPayload = encrypted;
2✔
127
        }
128

129
        /**
130
         * Determines if the payload should be encrypted.
131
         *
132
         * @returns {boolean} True if the payload should be encrypted, otherwise false.
133
         */
134
        isPayloadEncrypted(): boolean {
135
                return this.shouldEncryptPayload;
2✔
136
        }
137

138
        override toString(): string {
139
                return `GameMessageHeader {id: ${this.id}, length: ${this.length}, version: ${this.version}}`;
1✔
140
        }
141
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc