@norskvideo/moq-net 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -0
- package/announced.d.ts +45 -0
- package/announced.d.ts.map +1 -0
- package/announced.js +70 -0
- package/announced.js.map +1 -0
- package/bandwidth.d.ts +11 -0
- package/bandwidth.d.ts.map +1 -0
- package/bandwidth.js +7 -0
- package/bandwidth.js.map +1 -0
- package/broadcast.d.ts +39 -0
- package/broadcast.d.ts.map +1 -0
- package/broadcast.js +76 -0
- package/broadcast.js.map +1 -0
- package/connection/accept.d.ts +15 -0
- package/connection/accept.d.ts.map +1 -0
- package/connection/accept.js +137 -0
- package/connection/accept.js.map +1 -0
- package/connection/cert.test.d.ts +2 -0
- package/connection/cert.test.d.ts.map +1 -0
- package/connection/cert.test.js +26 -0
- package/connection/cert.test.js.map +1 -0
- package/connection/connect.d.ts +37 -0
- package/connection/connect.d.ts.map +1 -0
- package/connection/connect.js +348 -0
- package/connection/connect.js.map +1 -0
- package/connection/established.d.ts +30 -0
- package/connection/established.d.ts.map +1 -0
- package/connection/established.js +3 -0
- package/connection/established.js.map +1 -0
- package/connection/handshake.d.ts +10 -0
- package/connection/handshake.d.ts.map +1 -0
- package/connection/handshake.js +48 -0
- package/connection/handshake.js.map +1 -0
- package/connection/index.d.ts +5 -0
- package/connection/index.d.ts.map +1 -0
- package/connection/index.js +6 -0
- package/connection/index.js.map +1 -0
- package/connection/reload.d.ts +47 -0
- package/connection/reload.d.ts.map +1 -0
- package/connection/reload.js +137 -0
- package/connection/reload.js.map +1 -0
- package/group.d.ts +66 -0
- package/group.d.ts.map +1 -0
- package/group.js +135 -0
- package/group.js.map +1 -0
- package/group.test.d.ts +2 -0
- package/group.test.d.ts.map +1 -0
- package/group.test.js +66 -0
- package/group.test.js.map +1 -0
- package/ietf/adapter.d.ts +65 -0
- package/ietf/adapter.d.ts.map +1 -0
- package/ietf/adapter.js +626 -0
- package/ietf/adapter.js.map +1 -0
- package/ietf/connection.d.ts +65 -0
- package/ietf/connection.d.ts.map +1 -0
- package/ietf/connection.js +248 -0
- package/ietf/connection.js.map +1 -0
- package/ietf/control.d.ts +135 -0
- package/ietf/control.d.ts.map +1 -0
- package/ietf/control.js +242 -0
- package/ietf/control.js.map +1 -0
- package/ietf/fetch.d.ts +64 -0
- package/ietf/fetch.d.ts.map +1 -0
- package/ietf/fetch.js +100 -0
- package/ietf/fetch.js.map +1 -0
- package/ietf/goaway.d.ts +15 -0
- package/ietf/goaway.d.ts.map +1 -0
- package/ietf/goaway.js +34 -0
- package/ietf/goaway.js.map +1 -0
- package/ietf/ietf.test.d.ts +2 -0
- package/ietf/ietf.test.d.ts.map +1 -0
- package/ietf/ietf.test.js +873 -0
- package/ietf/ietf.test.js.map +1 -0
- package/ietf/index.d.ts +18 -0
- package/ietf/index.d.ts.map +1 -0
- package/ietf/index.js +19 -0
- package/ietf/index.js.map +1 -0
- package/ietf/message.d.ts +4 -0
- package/ietf/message.d.ts.map +1 -0
- package/ietf/message.js +54 -0
- package/ietf/message.js.map +1 -0
- package/ietf/namespace.d.ts +5 -0
- package/ietf/namespace.d.ts.map +1 -0
- package/ietf/namespace.js +27 -0
- package/ietf/namespace.js.map +1 -0
- package/ietf/object.d.ts +40 -0
- package/ietf/object.d.ts.map +1 -0
- package/ietf/object.js +160 -0
- package/ietf/object.js.map +1 -0
- package/ietf/parameters.d.ts +56 -0
- package/ietf/parameters.d.ts.map +1 -0
- package/ietf/parameters.js +337 -0
- package/ietf/parameters.js.map +1 -0
- package/ietf/properties.d.ts +4 -0
- package/ietf/properties.d.ts.map +1 -0
- package/ietf/properties.js +33 -0
- package/ietf/properties.js.map +1 -0
- package/ietf/publish.d.ts +68 -0
- package/ietf/publish.d.ts.map +1 -0
- package/ietf/publish.js +192 -0
- package/ietf/publish.js.map +1 -0
- package/ietf/publish_namespace.d.ts +68 -0
- package/ietf/publish_namespace.d.ts.map +1 -0
- package/ietf/publish_namespace.js +176 -0
- package/ietf/publish_namespace.js.map +1 -0
- package/ietf/publisher.d.ts +50 -0
- package/ietf/publisher.d.ts.map +1 -0
- package/ietf/publisher.js +330 -0
- package/ietf/publisher.js.map +1 -0
- package/ietf/request.d.ts +52 -0
- package/ietf/request.d.ts.map +1 -0
- package/ietf/request.js +119 -0
- package/ietf/request.js.map +1 -0
- package/ietf/setup.d.ts +38 -0
- package/ietf/setup.d.ts.map +1 -0
- package/ietf/setup.js +129 -0
- package/ietf/setup.js.map +1 -0
- package/ietf/subscribe.d.ts +66 -0
- package/ietf/subscribe.d.ts.map +1 -0
- package/ietf/subscribe.js +276 -0
- package/ietf/subscribe.js.map +1 -0
- package/ietf/subscribe_namespace.d.ts +101 -0
- package/ietf/subscribe_namespace.d.ts.map +1 -0
- package/ietf/subscribe_namespace.js +256 -0
- package/ietf/subscribe_namespace.js.map +1 -0
- package/ietf/subscriber.d.ts +53 -0
- package/ietf/subscriber.d.ts.map +1 -0
- package/ietf/subscriber.js +404 -0
- package/ietf/subscriber.js.map +1 -0
- package/ietf/track.d.ts +40 -0
- package/ietf/track.d.ts.map +1 -0
- package/ietf/track.js +108 -0
- package/ietf/track.js.map +1 -0
- package/ietf/version.d.ts +56 -0
- package/ietf/version.d.ts.map +1 -0
- package/ietf/version.js +63 -0
- package/ietf/version.js.map +1 -0
- package/index.d.ts +22 -0
- package/index.d.ts.map +1 -0
- package/index.js +23 -0
- package/index.js.map +1 -0
- package/integration.test.d.ts +2 -0
- package/integration.test.d.ts.map +1 -0
- package/integration.test.js +90 -0
- package/integration.test.js.map +1 -0
- package/lite/announce.d.ts +35 -0
- package/lite/announce.d.ts.map +1 -0
- package/lite/announce.js +166 -0
- package/lite/announce.js.map +1 -0
- package/lite/connection.d.ts +47 -0
- package/lite/connection.d.ts.map +1 -0
- package/lite/connection.js +247 -0
- package/lite/connection.js.map +1 -0
- package/lite/fetch.d.ts +14 -0
- package/lite/fetch.d.ts.map +1 -0
- package/lite/fetch.js +47 -0
- package/lite/fetch.js.map +1 -0
- package/lite/goaway.d.ts +10 -0
- package/lite/goaway.d.ts.map +1 -0
- package/lite/goaway.js +37 -0
- package/lite/goaway.js.map +1 -0
- package/lite/group.d.ts +18 -0
- package/lite/group.d.ts.map +1 -0
- package/lite/group.js +46 -0
- package/lite/group.js.map +1 -0
- package/lite/index.d.ts +11 -0
- package/lite/index.d.ts.map +1 -0
- package/lite/index.js +12 -0
- package/lite/index.js.map +1 -0
- package/lite/message.d.ts +5 -0
- package/lite/message.d.ts.map +1 -0
- package/lite/message.js +51 -0
- package/lite/message.js.map +1 -0
- package/lite/origin.d.ts +25 -0
- package/lite/origin.d.ts.map +1 -0
- package/lite/origin.js +35 -0
- package/lite/origin.js.map +1 -0
- package/lite/probe.d.ts +12 -0
- package/lite/probe.d.ts.map +1 -0
- package/lite/probe.js +60 -0
- package/lite/probe.js.map +1 -0
- package/lite/publisher.d.ts +63 -0
- package/lite/publisher.d.ts.map +1 -0
- package/lite/publisher.js +350 -0
- package/lite/publisher.js.map +1 -0
- package/lite/session.d.ts +36 -0
- package/lite/session.d.ts.map +1 -0
- package/lite/session.js +130 -0
- package/lite/session.js.map +1 -0
- package/lite/setup.d.ts +18 -0
- package/lite/setup.d.ts.map +1 -0
- package/lite/setup.js +55 -0
- package/lite/setup.js.map +1 -0
- package/lite/stream.d.ts +23 -0
- package/lite/stream.d.ts.map +1 -0
- package/lite/stream.js +20 -0
- package/lite/stream.js.map +1 -0
- package/lite/subscribe.d.ts +111 -0
- package/lite/subscribe.d.ts.map +1 -0
- package/lite/subscribe.js +341 -0
- package/lite/subscribe.js.map +1 -0
- package/lite/subscriber.d.ts +80 -0
- package/lite/subscriber.d.ts.map +1 -0
- package/lite/subscriber.js +316 -0
- package/lite/subscriber.js.map +1 -0
- package/lite/track.d.ts +38 -0
- package/lite/track.d.ts.map +1 -0
- package/lite/track.js +81 -0
- package/lite/track.js.map +1 -0
- package/lite/track.test.d.ts +2 -0
- package/lite/track.test.d.ts.map +1 -0
- package/lite/track.test.js +53 -0
- package/lite/track.test.js.map +1 -0
- package/lite/version.d.ts +16 -0
- package/lite/version.d.ts.map +1 -0
- package/lite/version.js +41 -0
- package/lite/version.js.map +1 -0
- package/mock.d.ts +16 -0
- package/mock.d.ts.map +1 -0
- package/mock.js +154 -0
- package/mock.js.map +1 -0
- package/package.json +27 -0
- package/path.d.ts +111 -0
- package/path.d.ts.map +1 -0
- package/path.js +133 -0
- package/path.js.map +1 -0
- package/path.test.d.ts +2 -0
- package/path.test.d.ts.map +1 -0
- package/path.test.js +169 -0
- package/path.test.js.map +1 -0
- package/stream.d.ts +57 -0
- package/stream.d.ts.map +1 -0
- package/stream.js +326 -0
- package/stream.js.map +1 -0
- package/stream.test.d.ts +2 -0
- package/stream.test.d.ts.map +1 -0
- package/stream.test.js +210 -0
- package/stream.test.js.map +1 -0
- package/time.d.ts +109 -0
- package/time.d.ts.map +1 -0
- package/time.js +94 -0
- package/time.js.map +1 -0
- package/track.d.ts +85 -0
- package/track.d.ts.map +1 -0
- package/track.js +227 -0
- package/track.js.map +1 -0
- package/track.test.d.ts +2 -0
- package/track.test.d.ts.map +1 -0
- package/track.test.js +56 -0
- package/track.test.js.map +1 -0
- package/util/error.d.ts +3 -0
- package/util/error.d.ts.map +1 -0
- package/util/error.js +9 -0
- package/util/error.js.map +1 -0
- package/util/hex.d.ts +3 -0
- package/util/hex.d.ts.map +1 -0
- package/util/hex.js +16 -0
- package/util/hex.js.map +1 -0
- package/util/index.d.ts +2 -0
- package/util/index.d.ts.map +1 -0
- package/util/index.js +3 -0
- package/util/index.js.map +1 -0
- package/varint.d.ts +36 -0
- package/varint.d.ts.map +1 -0
- package/varint.js +288 -0
- package/varint.js.map +1 -0
- package/varint.test.d.ts +2 -0
- package/varint.test.d.ts.map +1 -0
- package/varint.test.js +106 -0
- package/varint.test.js.map +1 -0
- package/zod.d.ts +13 -0
- package/zod.d.ts.map +1 -0
- package/zod.js +19 -0
- package/zod.js.map +1 -0
|
@@ -0,0 +1,873 @@
|
|
|
1
|
+
/* @ts-self-types="./ietf.test.d.ts" */
|
|
2
|
+
import { expect, test } from "bun:test";
|
|
3
|
+
import * as Path from "../path.js";
|
|
4
|
+
import { Reader, Writer } from "../stream.js";
|
|
5
|
+
import * as Varint from "../varint.js";
|
|
6
|
+
import * as GoAway from "./goaway.js";
|
|
7
|
+
import * as Namespace from "./namespace.js";
|
|
8
|
+
import { Group } from "./object.js";
|
|
9
|
+
import { SetupOptions } from "./parameters.js";
|
|
10
|
+
import { Publish, PublishDone } from "./publish.js";
|
|
11
|
+
import * as Announce from "./publish_namespace.js";
|
|
12
|
+
import { RequestError, RequestOk } from "./request.js";
|
|
13
|
+
import * as Setup from "./setup.js";
|
|
14
|
+
import * as Subscribe from "./subscribe.js";
|
|
15
|
+
import * as SubscribeNamespace from "./subscribe_namespace.js";
|
|
16
|
+
import * as Track from "./track.js";
|
|
17
|
+
import { Version } from "./version.js";
|
|
18
|
+
// Helper to create a writable stream that captures written data
|
|
19
|
+
function createTestWritableStream() {
|
|
20
|
+
const written = [];
|
|
21
|
+
const stream = new WritableStream({
|
|
22
|
+
write(chunk) {
|
|
23
|
+
written.push(new Uint8Array(chunk));
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
return { stream, written };
|
|
27
|
+
}
|
|
28
|
+
// Helper to concatenate written chunks
|
|
29
|
+
function concatChunks(chunks) {
|
|
30
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
31
|
+
const result = new Uint8Array(totalLength);
|
|
32
|
+
let offset = 0;
|
|
33
|
+
for (const chunk of chunks) {
|
|
34
|
+
result.set(chunk, offset);
|
|
35
|
+
offset += chunk.byteLength;
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
// Helper to encode a versioned message
|
|
40
|
+
async function encodeVersioned(message, version) {
|
|
41
|
+
const { stream, written } = createTestWritableStream();
|
|
42
|
+
const writer = new Writer(stream, version);
|
|
43
|
+
await message.encode(writer, version);
|
|
44
|
+
writer.close();
|
|
45
|
+
await writer.closed;
|
|
46
|
+
return concatChunks(written);
|
|
47
|
+
}
|
|
48
|
+
// Helper to decode a versioned message
|
|
49
|
+
async function decodeVersioned(bytes, decoder, version) {
|
|
50
|
+
const reader = new Reader(undefined, bytes, version);
|
|
51
|
+
return await decoder(reader, version);
|
|
52
|
+
}
|
|
53
|
+
// Subscribe tests (v14)
|
|
54
|
+
test("Subscribe v14: round trip", async () => {
|
|
55
|
+
const msg = new Subscribe.Subscribe({
|
|
56
|
+
requestId: 1n,
|
|
57
|
+
trackNamespace: Path.from("test"),
|
|
58
|
+
trackName: "video",
|
|
59
|
+
subscriberPriority: 128,
|
|
60
|
+
});
|
|
61
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
62
|
+
const decoded = await decodeVersioned(encoded, Subscribe.Subscribe.decode, Version.DRAFT_14);
|
|
63
|
+
expect(decoded.requestId).toBe(1n);
|
|
64
|
+
expect(decoded.trackNamespace).toBe("test");
|
|
65
|
+
expect(decoded.trackName).toBe("video");
|
|
66
|
+
expect(decoded.subscriberPriority).toBe(128);
|
|
67
|
+
});
|
|
68
|
+
test("Subscribe v14: nested namespace", async () => {
|
|
69
|
+
const msg = new Subscribe.Subscribe({
|
|
70
|
+
requestId: 100n,
|
|
71
|
+
trackNamespace: Path.from("conference/room123"),
|
|
72
|
+
trackName: "audio",
|
|
73
|
+
subscriberPriority: 255,
|
|
74
|
+
});
|
|
75
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
76
|
+
const decoded = await decodeVersioned(encoded, Subscribe.Subscribe.decode, Version.DRAFT_14);
|
|
77
|
+
expect(decoded.trackNamespace).toBe("conference/room123");
|
|
78
|
+
});
|
|
79
|
+
test("SubscribeOk v14: without largest", async () => {
|
|
80
|
+
const msg = new Subscribe.SubscribeOk({ requestId: 42n, trackAlias: 43n });
|
|
81
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
82
|
+
const decoded = await decodeVersioned(encoded, Subscribe.SubscribeOk.decode, Version.DRAFT_14);
|
|
83
|
+
expect(decoded.requestId).toBe(42n);
|
|
84
|
+
expect(decoded.trackAlias).toBe(43n);
|
|
85
|
+
});
|
|
86
|
+
// Subscribe tests (v15)
|
|
87
|
+
test("Subscribe v15: round trip", async () => {
|
|
88
|
+
const msg = new Subscribe.Subscribe({
|
|
89
|
+
requestId: 1n,
|
|
90
|
+
trackNamespace: Path.from("test"),
|
|
91
|
+
trackName: "video",
|
|
92
|
+
subscriberPriority: 128,
|
|
93
|
+
});
|
|
94
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_15);
|
|
95
|
+
const decoded = await decodeVersioned(encoded, Subscribe.Subscribe.decode, Version.DRAFT_15);
|
|
96
|
+
expect(decoded.requestId).toBe(1n);
|
|
97
|
+
expect(decoded.trackNamespace).toBe("test");
|
|
98
|
+
expect(decoded.trackName).toBe("video");
|
|
99
|
+
expect(decoded.subscriberPriority).toBe(128);
|
|
100
|
+
});
|
|
101
|
+
test("SubscribeOk v15: round trip", async () => {
|
|
102
|
+
const msg = new Subscribe.SubscribeOk({ requestId: 42n, trackAlias: 43n });
|
|
103
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_15);
|
|
104
|
+
const decoded = await decodeVersioned(encoded, Subscribe.SubscribeOk.decode, Version.DRAFT_15);
|
|
105
|
+
expect(decoded.requestId).toBe(42n);
|
|
106
|
+
expect(decoded.trackAlias).toBe(43n);
|
|
107
|
+
});
|
|
108
|
+
test("SubscribeError: round trip", async () => {
|
|
109
|
+
const msg = new Subscribe.SubscribeError({ requestId: 123n, errorCode: 500, reasonPhrase: "Not found" });
|
|
110
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
111
|
+
const decoded = await decodeVersioned(encoded, Subscribe.SubscribeError.decode, Version.DRAFT_14);
|
|
112
|
+
expect(decoded.requestId).toBe(123n);
|
|
113
|
+
expect(decoded.errorCode).toBe(500);
|
|
114
|
+
expect(decoded.reasonPhrase).toBe("Not found");
|
|
115
|
+
});
|
|
116
|
+
test("Unsubscribe: round trip", async () => {
|
|
117
|
+
const msg = new Subscribe.Unsubscribe({ requestId: 999n });
|
|
118
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
119
|
+
const decoded = await decodeVersioned(encoded, Subscribe.Unsubscribe.decode, Version.DRAFT_14);
|
|
120
|
+
expect(decoded.requestId).toBe(999n);
|
|
121
|
+
});
|
|
122
|
+
test("PublishDone: basic test", async () => {
|
|
123
|
+
const msg = new PublishDone({ requestId: 10n, statusCode: 0, reasonPhrase: "complete" });
|
|
124
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
125
|
+
const decoded = await decodeVersioned(encoded, PublishDone.decode, Version.DRAFT_14);
|
|
126
|
+
expect(decoded.requestId).toBe(10n);
|
|
127
|
+
expect(decoded.statusCode).toBe(0);
|
|
128
|
+
expect(decoded.reasonPhrase).toBe("complete");
|
|
129
|
+
});
|
|
130
|
+
test("PublishDone: with error", async () => {
|
|
131
|
+
const msg = new PublishDone({ requestId: 10n, statusCode: 1, reasonPhrase: "error" });
|
|
132
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
133
|
+
const decoded = await decodeVersioned(encoded, PublishDone.decode, Version.DRAFT_14);
|
|
134
|
+
expect(decoded.requestId).toBe(10n);
|
|
135
|
+
expect(decoded.statusCode).toBe(1);
|
|
136
|
+
expect(decoded.reasonPhrase).toBe("error");
|
|
137
|
+
});
|
|
138
|
+
// Announce/PublishNamespace tests
|
|
139
|
+
test("PublishNamespace: round trip", async () => {
|
|
140
|
+
const msg = new Announce.PublishNamespace({ requestId: 1n, trackNamespace: Path.from("test/broadcast") });
|
|
141
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
142
|
+
const decoded = await decodeVersioned(encoded, Announce.PublishNamespace.decode, Version.DRAFT_14);
|
|
143
|
+
expect(decoded.requestId).toBe(1n);
|
|
144
|
+
expect(decoded.trackNamespace).toBe("test/broadcast");
|
|
145
|
+
});
|
|
146
|
+
test("PublishNamespaceOk: round trip", async () => {
|
|
147
|
+
const msg = new Announce.PublishNamespaceOk({ requestId: 2n });
|
|
148
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
149
|
+
const decoded = await decodeVersioned(encoded, Announce.PublishNamespaceOk.decode, Version.DRAFT_14);
|
|
150
|
+
expect(decoded.requestId).toBe(2n);
|
|
151
|
+
});
|
|
152
|
+
test("PublishNamespaceError: round trip", async () => {
|
|
153
|
+
const msg = new Announce.PublishNamespaceError({ requestId: 3n, errorCode: 404, reasonPhrase: "Unauthorized" });
|
|
154
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
155
|
+
const decoded = await decodeVersioned(encoded, Announce.PublishNamespaceError.decode, Version.DRAFT_14);
|
|
156
|
+
expect(decoded.requestId).toBe(3n);
|
|
157
|
+
expect(decoded.errorCode).toBe(404);
|
|
158
|
+
expect(decoded.reasonPhrase).toBe("Unauthorized");
|
|
159
|
+
});
|
|
160
|
+
test("PublishNamespaceDone: round trip", async () => {
|
|
161
|
+
const msg = new Announce.PublishNamespaceDone({ trackNamespace: Path.from("old/stream") });
|
|
162
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
163
|
+
const decoded = await decodeVersioned(encoded, Announce.PublishNamespaceDone.decode, Version.DRAFT_14);
|
|
164
|
+
expect(decoded.trackNamespace).toBe("old/stream");
|
|
165
|
+
});
|
|
166
|
+
test("PublishNamespaceCancel: round trip", async () => {
|
|
167
|
+
const msg = new Announce.PublishNamespaceCancel({
|
|
168
|
+
trackNamespace: Path.from("canceled"),
|
|
169
|
+
errorCode: 1,
|
|
170
|
+
reasonPhrase: "Shutdown",
|
|
171
|
+
});
|
|
172
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
173
|
+
const decoded = await decodeVersioned(encoded, Announce.PublishNamespaceCancel.decode, Version.DRAFT_14);
|
|
174
|
+
expect(decoded.trackNamespace).toBe("canceled");
|
|
175
|
+
expect(decoded.errorCode).toBe(1);
|
|
176
|
+
expect(decoded.reasonPhrase).toBe("Shutdown");
|
|
177
|
+
});
|
|
178
|
+
// GoAway tests
|
|
179
|
+
test("GoAway: with URL", async () => {
|
|
180
|
+
const msg = new GoAway.GoAway({ newSessionUri: "https://example.com/new" });
|
|
181
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
182
|
+
const decoded = await decodeVersioned(encoded, GoAway.GoAway.decode, Version.DRAFT_14);
|
|
183
|
+
expect(decoded.newSessionUri).toBe("https://example.com/new");
|
|
184
|
+
});
|
|
185
|
+
test("GoAway: empty", async () => {
|
|
186
|
+
const msg = new GoAway.GoAway({ newSessionUri: "" });
|
|
187
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
188
|
+
const decoded = await decodeVersioned(encoded, GoAway.GoAway.decode, Version.DRAFT_14);
|
|
189
|
+
expect(decoded.newSessionUri).toBe("");
|
|
190
|
+
});
|
|
191
|
+
// Track tests
|
|
192
|
+
test("TrackStatusRequest: round trip", async () => {
|
|
193
|
+
const msg = new Track.TrackStatusRequest({
|
|
194
|
+
requestId: 0n,
|
|
195
|
+
trackNamespace: Path.from("video/stream"),
|
|
196
|
+
trackName: "main",
|
|
197
|
+
});
|
|
198
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
199
|
+
const decoded = await decodeVersioned(encoded, Track.TrackStatusRequest.decode, Version.DRAFT_14);
|
|
200
|
+
expect(decoded.requestId).toBe(0n);
|
|
201
|
+
expect(decoded.trackNamespace).toBe("video/stream");
|
|
202
|
+
expect(decoded.trackName).toBe("main");
|
|
203
|
+
});
|
|
204
|
+
test("TrackStatus v14: round trip", async () => {
|
|
205
|
+
const msg = new Track.TrackStatus({
|
|
206
|
+
trackNamespace: Path.from("test"),
|
|
207
|
+
trackName: "status",
|
|
208
|
+
statusCode: 200,
|
|
209
|
+
lastGroupId: 42n,
|
|
210
|
+
lastObjectId: 100n,
|
|
211
|
+
});
|
|
212
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
213
|
+
const decoded = await decodeVersioned(encoded, Track.TrackStatus.decode, Version.DRAFT_14);
|
|
214
|
+
expect(decoded.trackNamespace).toBe("test");
|
|
215
|
+
expect(decoded.trackName).toBe("status");
|
|
216
|
+
expect(decoded.statusCode).toBe(200);
|
|
217
|
+
expect(decoded.lastGroupId).toBe(42n);
|
|
218
|
+
expect(decoded.lastObjectId).toBe(100n);
|
|
219
|
+
});
|
|
220
|
+
// Validation tests
|
|
221
|
+
test("Subscribe v14: rejects invalid filter type", async () => {
|
|
222
|
+
const invalidBytes = new Uint8Array([
|
|
223
|
+
0x01, // subscribe_id
|
|
224
|
+
0x02, // track_alias
|
|
225
|
+
0x01, // namespace length
|
|
226
|
+
0x04,
|
|
227
|
+
0x74,
|
|
228
|
+
0x65,
|
|
229
|
+
0x73,
|
|
230
|
+
0x74, // "test"
|
|
231
|
+
0x05,
|
|
232
|
+
0x76,
|
|
233
|
+
0x69,
|
|
234
|
+
0x64,
|
|
235
|
+
0x65,
|
|
236
|
+
0x6f, // "video"
|
|
237
|
+
0x80, // subscriber_priority
|
|
238
|
+
0x02, // group_order
|
|
239
|
+
0x99, // INVALID filter_type
|
|
240
|
+
0x00, // num_params
|
|
241
|
+
]);
|
|
242
|
+
await expect((async () => {
|
|
243
|
+
await decodeVersioned(invalidBytes, Subscribe.Subscribe.decode, Version.DRAFT_14);
|
|
244
|
+
})()).rejects.toThrow();
|
|
245
|
+
});
|
|
246
|
+
test("SubscribeOk v14: rejects non-zero expires", async () => {
|
|
247
|
+
const invalidBytes = new Uint8Array([
|
|
248
|
+
0x01, // subscribe_id
|
|
249
|
+
0x05, // INVALID: expires = 5
|
|
250
|
+
0x02, // group_order
|
|
251
|
+
0x00, // content_exists
|
|
252
|
+
0x00, // num_params
|
|
253
|
+
]);
|
|
254
|
+
await expect((async () => {
|
|
255
|
+
await decodeVersioned(invalidBytes, Subscribe.SubscribeOk.decode, Version.DRAFT_14);
|
|
256
|
+
})()).rejects.toThrow();
|
|
257
|
+
});
|
|
258
|
+
// Unicode tests
|
|
259
|
+
test("SubscribeError: unicode strings", async () => {
|
|
260
|
+
const msg = new Subscribe.SubscribeError({ requestId: 1n, errorCode: 400, reasonPhrase: "Error: 错误 🚫" });
|
|
261
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
262
|
+
const decoded = await decodeVersioned(encoded, Subscribe.SubscribeError.decode, Version.DRAFT_14);
|
|
263
|
+
expect(decoded.requestId).toBe(1n);
|
|
264
|
+
expect(decoded.errorCode).toBe(400);
|
|
265
|
+
expect(decoded.reasonPhrase).toBe("Error: 错误 🚫");
|
|
266
|
+
});
|
|
267
|
+
test("PublishNamespace: unicode namespace", async () => {
|
|
268
|
+
const msg = new Announce.PublishNamespace({ requestId: 1n, trackNamespace: Path.from("会议/房间") });
|
|
269
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
270
|
+
const decoded = await decodeVersioned(encoded, Announce.PublishNamespace.decode, Version.DRAFT_14);
|
|
271
|
+
expect(decoded.requestId).toBe(1n);
|
|
272
|
+
expect(decoded.trackNamespace).toBe("会议/房间");
|
|
273
|
+
});
|
|
274
|
+
// Publish v15 tests
|
|
275
|
+
test("Publish v15: round trip", async () => {
|
|
276
|
+
const msg = new Publish({
|
|
277
|
+
requestId: 1n,
|
|
278
|
+
trackNamespace: Path.from("test/ns"),
|
|
279
|
+
trackName: "video",
|
|
280
|
+
trackAlias: 42n,
|
|
281
|
+
groupOrder: 0x02,
|
|
282
|
+
contentExists: false,
|
|
283
|
+
largest: undefined,
|
|
284
|
+
forward: true,
|
|
285
|
+
});
|
|
286
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_15);
|
|
287
|
+
const decoded = await decodeVersioned(encoded, Publish.decode, Version.DRAFT_15);
|
|
288
|
+
expect(decoded.requestId).toBe(1n);
|
|
289
|
+
expect(decoded.trackNamespace).toBe("test/ns");
|
|
290
|
+
expect(decoded.trackName).toBe("video");
|
|
291
|
+
expect(decoded.trackAlias).toBe(42n);
|
|
292
|
+
expect(decoded.forward).toBe(true);
|
|
293
|
+
});
|
|
294
|
+
test("Publish v14: round trip", async () => {
|
|
295
|
+
const msg = new Publish({
|
|
296
|
+
requestId: 1n,
|
|
297
|
+
trackNamespace: Path.from("test/ns"),
|
|
298
|
+
trackName: "video",
|
|
299
|
+
trackAlias: 42n,
|
|
300
|
+
groupOrder: 0x02,
|
|
301
|
+
contentExists: true,
|
|
302
|
+
largest: { groupId: 10n, objectId: 5n },
|
|
303
|
+
forward: true,
|
|
304
|
+
});
|
|
305
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
306
|
+
const decoded = await decodeVersioned(encoded, Publish.decode, Version.DRAFT_14);
|
|
307
|
+
expect(decoded.requestId).toBe(1n);
|
|
308
|
+
expect(decoded.trackNamespace).toBe("test/ns");
|
|
309
|
+
expect(decoded.trackName).toBe("video");
|
|
310
|
+
expect(decoded.trackAlias).toBe(42n);
|
|
311
|
+
expect(decoded.forward).toBe(true);
|
|
312
|
+
expect(decoded.contentExists).toBe(true);
|
|
313
|
+
expect(decoded.largest?.groupId).toBe(10n);
|
|
314
|
+
expect(decoded.largest?.objectId).toBe(5n);
|
|
315
|
+
});
|
|
316
|
+
// ClientSetup v15 tests
|
|
317
|
+
test("ClientSetup v15: round trip", async () => {
|
|
318
|
+
const msg = new Setup.ClientSetup({ versions: [Version.DRAFT_15] });
|
|
319
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_15);
|
|
320
|
+
const decoded = await decodeVersioned(encoded, Setup.ClientSetup.decode, Version.DRAFT_15);
|
|
321
|
+
expect(decoded.versions.length).toBe(1);
|
|
322
|
+
expect(decoded.versions[0]).toBe(Version.DRAFT_15);
|
|
323
|
+
});
|
|
324
|
+
test("ClientSetup v14: round trip", async () => {
|
|
325
|
+
const msg = new Setup.ClientSetup({ versions: [Version.DRAFT_14] });
|
|
326
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
327
|
+
const decoded = await decodeVersioned(encoded, Setup.ClientSetup.decode, Version.DRAFT_14);
|
|
328
|
+
expect(decoded.versions.length).toBe(1);
|
|
329
|
+
expect(decoded.versions[0]).toBe(Version.DRAFT_14);
|
|
330
|
+
});
|
|
331
|
+
// ServerSetup v15 tests
|
|
332
|
+
test("ServerSetup v15: round trip", async () => {
|
|
333
|
+
const msg = new Setup.ServerSetup({ version: Version.DRAFT_15 });
|
|
334
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_15);
|
|
335
|
+
const decoded = await decodeVersioned(encoded, Setup.ServerSetup.decode, Version.DRAFT_15);
|
|
336
|
+
expect(decoded.version).toBe(Version.DRAFT_15);
|
|
337
|
+
});
|
|
338
|
+
test("ServerSetup v14: round trip", async () => {
|
|
339
|
+
const msg = new Setup.ServerSetup({ version: Version.DRAFT_14 });
|
|
340
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
341
|
+
const decoded = await decodeVersioned(encoded, Setup.ServerSetup.decode, Version.DRAFT_14);
|
|
342
|
+
expect(decoded.version).toBe(Version.DRAFT_14);
|
|
343
|
+
});
|
|
344
|
+
// RequestOk / RequestError v15 tests
|
|
345
|
+
test("RequestOk: round trip", async () => {
|
|
346
|
+
const msg = new RequestOk({ requestId: 42n });
|
|
347
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_15);
|
|
348
|
+
const decoded = await decodeVersioned(encoded, RequestOk.decode, Version.DRAFT_15);
|
|
349
|
+
expect(decoded.requestId).toBe(42n);
|
|
350
|
+
});
|
|
351
|
+
test("RequestError v15: round trip", async () => {
|
|
352
|
+
const msg = new RequestError({ requestId: 99n, errorCode: 500, reasonPhrase: "Internal error" });
|
|
353
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_15);
|
|
354
|
+
const decoded = await decodeVersioned(encoded, RequestError.decode, Version.DRAFT_15);
|
|
355
|
+
expect(decoded.requestId).toBe(99n);
|
|
356
|
+
expect(decoded.errorCode).toBe(500);
|
|
357
|
+
expect(decoded.reasonPhrase).toBe("Internal error");
|
|
358
|
+
expect(decoded.retryInterval).toBe(0n);
|
|
359
|
+
});
|
|
360
|
+
test("RequestError v16: round trip with retryInterval", async () => {
|
|
361
|
+
const msg = new RequestError({
|
|
362
|
+
requestId: 99n,
|
|
363
|
+
errorCode: 500,
|
|
364
|
+
reasonPhrase: "Internal error",
|
|
365
|
+
retryInterval: 5000n,
|
|
366
|
+
});
|
|
367
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_16);
|
|
368
|
+
const decoded = await decodeVersioned(encoded, RequestError.decode, Version.DRAFT_16);
|
|
369
|
+
expect(decoded.requestId).toBe(99n);
|
|
370
|
+
expect(decoded.errorCode).toBe(500);
|
|
371
|
+
expect(decoded.reasonPhrase).toBe("Internal error");
|
|
372
|
+
expect(decoded.retryInterval).toBe(5000n);
|
|
373
|
+
});
|
|
374
|
+
// --- Leading-ones varint tests ---
|
|
375
|
+
test("Leading-ones varint: spec test vectors", () => {
|
|
376
|
+
// Test vectors from draft-17 spec (Table 2)
|
|
377
|
+
const cases = [
|
|
378
|
+
[new Uint8Array([0x25]), 37n],
|
|
379
|
+
[new Uint8Array([0x80, 0x25]), 37n], // non-minimal encoding of 37
|
|
380
|
+
[new Uint8Array([0xbb, 0xbd]), 15293n],
|
|
381
|
+
[new Uint8Array([0xfa, 0xa1, 0xa0, 0xe4, 0x03, 0xd8]), 2893212287960n],
|
|
382
|
+
[new Uint8Array([0xfe, 0xfa, 0x31, 0x8f, 0xa8, 0xe3, 0xca, 0x11]), 70423237261249041n],
|
|
383
|
+
[
|
|
384
|
+
new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
|
|
385
|
+
18446744073709551615n, // u64::MAX
|
|
386
|
+
],
|
|
387
|
+
];
|
|
388
|
+
for (const [bytes, expected] of cases) {
|
|
389
|
+
const [decoded, remain] = Varint.decodeLeadingOnes(bytes);
|
|
390
|
+
expect(decoded).toBe(expected);
|
|
391
|
+
expect(remain.length).toBe(0);
|
|
392
|
+
}
|
|
393
|
+
// Test minimal round-trip (skip non-minimal 0x8025 for 37, and u64::MAX which exceeds safe range)
|
|
394
|
+
const roundTripCases = [
|
|
395
|
+
[new Uint8Array([0x25]), 37n],
|
|
396
|
+
[new Uint8Array([0xbb, 0xbd]), 15293n],
|
|
397
|
+
[new Uint8Array([0xfa, 0xa1, 0xa0, 0xe4, 0x03, 0xd8]), 2893212287960n],
|
|
398
|
+
[new Uint8Array([0xfe, 0xfa, 0x31, 0x8f, 0xa8, 0xe3, 0xca, 0x11]), 70423237261249041n],
|
|
399
|
+
];
|
|
400
|
+
for (const [expectedBytes, value] of roundTripCases) {
|
|
401
|
+
const encoded = Varint.encodeLeadingOnes(value);
|
|
402
|
+
expect(encoded).toEqual(expectedBytes);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
test("Leading-ones varint: boundary round-trips", () => {
|
|
406
|
+
const cases = [
|
|
407
|
+
[(1n << 7n) - 1n, 1],
|
|
408
|
+
[1n << 7n, 2],
|
|
409
|
+
[(1n << 14n) - 1n, 2],
|
|
410
|
+
[1n << 14n, 3],
|
|
411
|
+
[(1n << 56n) - 1n, 8],
|
|
412
|
+
[1n << 56n, 9],
|
|
413
|
+
];
|
|
414
|
+
for (const [value, expectedLen] of cases) {
|
|
415
|
+
const encoded = Varint.encodeLeadingOnes(value);
|
|
416
|
+
expect(encoded.length).toBe(expectedLen);
|
|
417
|
+
const [decoded, remain] = Varint.decodeLeadingOnes(encoded);
|
|
418
|
+
expect(decoded).toBe(value);
|
|
419
|
+
expect(remain.length).toBe(0);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
test("Leading-ones varint: 0xFC is a 7-byte form on draft-18+ (per #1595)", () => {
|
|
423
|
+
// Standalone decoder is permissive; draft-17 enforcement is in Reader#readLeadingOnes.
|
|
424
|
+
// Only check that it doesn't reject the prefix as "reserved" — it now needs more bytes.
|
|
425
|
+
expect(() => {
|
|
426
|
+
Varint.decodeLeadingOnes(new Uint8Array([0xfc]));
|
|
427
|
+
}).toThrow(/buffer too short/);
|
|
428
|
+
});
|
|
429
|
+
test("Reader#u62: rejects 0xFC 7-byte form on draft-17", async () => {
|
|
430
|
+
const bytes = new Uint8Array([0xfc, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc]);
|
|
431
|
+
const reader = new Reader(undefined, bytes, Version.DRAFT_17);
|
|
432
|
+
await expect(reader.u62()).rejects.toThrow(/reserved on draft-17/);
|
|
433
|
+
});
|
|
434
|
+
test("Reader#u62: accepts 0xFC 7-byte form on draft-18", async () => {
|
|
435
|
+
const value = 0x123456789abcn;
|
|
436
|
+
const bytes = new Uint8Array([0xfc, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc]);
|
|
437
|
+
const reader = new Reader(undefined, bytes, Version.DRAFT_18);
|
|
438
|
+
expect(await reader.u62()).toBe(value);
|
|
439
|
+
expect(await reader.done()).toBe(true);
|
|
440
|
+
});
|
|
441
|
+
test("Reader#u62: accepts 0xFC 7-byte form on draft-19", async () => {
|
|
442
|
+
const value = 0x123456789abcn;
|
|
443
|
+
const bytes = new Uint8Array([0xfc, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc]);
|
|
444
|
+
const reader = new Reader(undefined, bytes, Version.DRAFT_19);
|
|
445
|
+
expect(await reader.u62()).toBe(value);
|
|
446
|
+
expect(await reader.done()).toBe(true);
|
|
447
|
+
});
|
|
448
|
+
test("Leading-ones varint: 7-byte form round-trips on draft-18", () => {
|
|
449
|
+
// Encode 7 bytes manually: 1111110_0 (prefix bit 48 = 0) + 6 bytes payload
|
|
450
|
+
const value = 0x123456789abcn;
|
|
451
|
+
const bytes = new Uint8Array(7);
|
|
452
|
+
bytes[0] = 0xfc; // 1111110_0
|
|
453
|
+
bytes[1] = 0x12;
|
|
454
|
+
bytes[2] = 0x34;
|
|
455
|
+
bytes[3] = 0x56;
|
|
456
|
+
bytes[4] = 0x78;
|
|
457
|
+
bytes[5] = 0x9a;
|
|
458
|
+
bytes[6] = 0xbc;
|
|
459
|
+
const [decoded, remain] = Varint.decodeLeadingOnes(bytes);
|
|
460
|
+
expect(decoded).toBe(value);
|
|
461
|
+
expect(remain.length).toBe(0);
|
|
462
|
+
});
|
|
463
|
+
// --- Draft-17 message tests ---
|
|
464
|
+
test("Subscribe v17: round trip with requiredRequestIdDelta", async () => {
|
|
465
|
+
const msg = new Subscribe.Subscribe({
|
|
466
|
+
requestId: 1n,
|
|
467
|
+
trackNamespace: Path.from("test"),
|
|
468
|
+
trackName: "video",
|
|
469
|
+
subscriberPriority: 128,
|
|
470
|
+
});
|
|
471
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
472
|
+
const decoded = await decodeVersioned(encoded, Subscribe.Subscribe.decode, Version.DRAFT_17);
|
|
473
|
+
expect(decoded.requestId).toBe(1n);
|
|
474
|
+
expect(decoded.trackNamespace).toBe("test");
|
|
475
|
+
expect(decoded.trackName).toBe("video");
|
|
476
|
+
expect(decoded.subscriberPriority).toBe(128);
|
|
477
|
+
});
|
|
478
|
+
test("SubscribeOk v17: no requestId", async () => {
|
|
479
|
+
const msg = new Subscribe.SubscribeOk({ trackAlias: 42n });
|
|
480
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
481
|
+
const decoded = await decodeVersioned(encoded, Subscribe.SubscribeOk.decode, Version.DRAFT_17);
|
|
482
|
+
expect(decoded.requestId).toBe(undefined);
|
|
483
|
+
expect(decoded.trackAlias).toBe(42n);
|
|
484
|
+
});
|
|
485
|
+
test("RequestOk v17: no requestId", async () => {
|
|
486
|
+
const msg = new RequestOk({});
|
|
487
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
488
|
+
const decoded = await decodeVersioned(encoded, RequestOk.decode, Version.DRAFT_17);
|
|
489
|
+
expect(decoded.requestId).toBe(undefined);
|
|
490
|
+
});
|
|
491
|
+
test("RequestError v17: no requestId, has retryInterval", async () => {
|
|
492
|
+
const msg = new RequestError({
|
|
493
|
+
errorCode: 500,
|
|
494
|
+
reasonPhrase: "Internal error",
|
|
495
|
+
retryInterval: 3000n,
|
|
496
|
+
});
|
|
497
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
498
|
+
const decoded = await decodeVersioned(encoded, RequestError.decode, Version.DRAFT_17);
|
|
499
|
+
expect(decoded.requestId).toBe(undefined);
|
|
500
|
+
expect(decoded.errorCode).toBe(500);
|
|
501
|
+
expect(decoded.reasonPhrase).toBe("Internal error");
|
|
502
|
+
expect(decoded.retryInterval).toBe(3000n);
|
|
503
|
+
});
|
|
504
|
+
test("GoAway v17: with timeout", async () => {
|
|
505
|
+
const msg = new GoAway.GoAway({ newSessionUri: "https://example.com/new", timeout: 5000n });
|
|
506
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
507
|
+
const decoded = await decodeVersioned(encoded, GoAway.GoAway.decode, Version.DRAFT_17);
|
|
508
|
+
expect(decoded.newSessionUri).toBe("https://example.com/new");
|
|
509
|
+
expect(decoded.timeout).toBe(5000n);
|
|
510
|
+
});
|
|
511
|
+
test("Setup v17: unified 0x2F00 round trip", async () => {
|
|
512
|
+
const params = new SetupOptions();
|
|
513
|
+
params.setBytes(7n, new TextEncoder().encode("test-impl"));
|
|
514
|
+
const msg = new Setup.Setup({ parameters: params });
|
|
515
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
516
|
+
const decoded = await decodeVersioned(encoded, Setup.Setup.decode, Version.DRAFT_17);
|
|
517
|
+
expect(decoded.parameters.getBytes(7n)).toEqual(new TextEncoder().encode("test-impl"));
|
|
518
|
+
});
|
|
519
|
+
test("Publish v17: round trip with requiredRequestIdDelta", async () => {
|
|
520
|
+
const msg = new Publish({
|
|
521
|
+
requestId: 1n,
|
|
522
|
+
trackNamespace: Path.from("test/ns"),
|
|
523
|
+
trackName: "video",
|
|
524
|
+
trackAlias: 42n,
|
|
525
|
+
groupOrder: 0x02,
|
|
526
|
+
contentExists: false,
|
|
527
|
+
largest: undefined,
|
|
528
|
+
forward: true,
|
|
529
|
+
});
|
|
530
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
531
|
+
const decoded = await decodeVersioned(encoded, Publish.decode, Version.DRAFT_17);
|
|
532
|
+
expect(decoded.requestId).toBe(1n);
|
|
533
|
+
expect(decoded.trackNamespace).toBe("test/ns");
|
|
534
|
+
expect(decoded.trackName).toBe("video");
|
|
535
|
+
expect(decoded.trackAlias).toBe(42n);
|
|
536
|
+
expect(decoded.forward).toBe(true);
|
|
537
|
+
});
|
|
538
|
+
test("PublishNamespace v17: round trip", async () => {
|
|
539
|
+
const msg = new Announce.PublishNamespace({ requestId: 5n, trackNamespace: Path.from("live/stream") });
|
|
540
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
541
|
+
const decoded = await decodeVersioned(encoded, Announce.PublishNamespace.decode, Version.DRAFT_17);
|
|
542
|
+
expect(decoded.requestId).toBe(5n);
|
|
543
|
+
expect(decoded.trackNamespace).toBe("live/stream");
|
|
544
|
+
});
|
|
545
|
+
test("PublishNamespaceDone v17: encode rejects", async () => {
|
|
546
|
+
const msg = new Announce.PublishNamespaceDone({ trackNamespace: Path.from("old/stream") });
|
|
547
|
+
await expect((() => encodeVersioned(msg, Version.DRAFT_17))()).rejects.toThrow(/removed in draft-17/);
|
|
548
|
+
});
|
|
549
|
+
test("PublishNamespaceCancel v17: encode rejects", async () => {
|
|
550
|
+
const msg = new Announce.PublishNamespaceCancel({ trackNamespace: Path.from("canceled") });
|
|
551
|
+
await expect((() => encodeVersioned(msg, Version.DRAFT_17))()).rejects.toThrow(/removed in draft-17/);
|
|
552
|
+
});
|
|
553
|
+
test("PublishDone v17: no requestId", async () => {
|
|
554
|
+
const msg = new PublishDone({ statusCode: 0, reasonPhrase: "done" });
|
|
555
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
556
|
+
const decoded = await decodeVersioned(encoded, PublishDone.decode, Version.DRAFT_17);
|
|
557
|
+
expect(decoded.requestId).toBe(undefined);
|
|
558
|
+
expect(decoded.statusCode).toBe(0);
|
|
559
|
+
expect(decoded.reasonPhrase).toBe("done");
|
|
560
|
+
});
|
|
561
|
+
// --- SubscribeUpdate tests ---
|
|
562
|
+
test("SubscribeUpdate v14: round trip", async () => {
|
|
563
|
+
const msg = new Subscribe.SubscribeUpdate({ requestId: 5n });
|
|
564
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
565
|
+
const decoded = await decodeVersioned(encoded, Subscribe.SubscribeUpdate.decode, Version.DRAFT_14);
|
|
566
|
+
expect(decoded.requestId).toBe(5n);
|
|
567
|
+
});
|
|
568
|
+
test("SubscribeUpdate v15: round trip", async () => {
|
|
569
|
+
const msg = new Subscribe.SubscribeUpdate({ requestId: 10n });
|
|
570
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_15);
|
|
571
|
+
const decoded = await decodeVersioned(encoded, Subscribe.SubscribeUpdate.decode, Version.DRAFT_15);
|
|
572
|
+
expect(decoded.requestId).toBe(10n);
|
|
573
|
+
});
|
|
574
|
+
test("SubscribeUpdate v17: round trip with requiredRequestIdDelta", async () => {
|
|
575
|
+
const msg = new Subscribe.SubscribeUpdate({ requestId: 42n });
|
|
576
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
577
|
+
const decoded = await decodeVersioned(encoded, Subscribe.SubscribeUpdate.decode, Version.DRAFT_17);
|
|
578
|
+
expect(decoded.requestId).toBe(42n);
|
|
579
|
+
});
|
|
580
|
+
// --- PublishBlocked tests ---
|
|
581
|
+
test("PublishBlocked v17: round trip", async () => {
|
|
582
|
+
const msg = new SubscribeNamespace.PublishBlocked({
|
|
583
|
+
suffix: Path.from("stream1"),
|
|
584
|
+
trackName: "video",
|
|
585
|
+
});
|
|
586
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
587
|
+
const decoded = await decodeVersioned(encoded, SubscribeNamespace.PublishBlocked.decode, Version.DRAFT_17);
|
|
588
|
+
expect(decoded.suffix).toBe("stream1");
|
|
589
|
+
expect(decoded.trackName).toBe("video");
|
|
590
|
+
});
|
|
591
|
+
// --- TrackStatusRequest version-aware tests ---
|
|
592
|
+
test("TrackStatusRequest v14: round trip with subscribe fields", async () => {
|
|
593
|
+
const msg = new Track.TrackStatusRequest({
|
|
594
|
+
requestId: 1n,
|
|
595
|
+
trackNamespace: Path.from("video/stream"),
|
|
596
|
+
trackName: "main",
|
|
597
|
+
});
|
|
598
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_14);
|
|
599
|
+
const decoded = await decodeVersioned(encoded, Track.TrackStatusRequest.decode, Version.DRAFT_14);
|
|
600
|
+
expect(decoded.requestId).toBe(1n);
|
|
601
|
+
expect(decoded.trackNamespace).toBe("video/stream");
|
|
602
|
+
expect(decoded.trackName).toBe("main");
|
|
603
|
+
});
|
|
604
|
+
test("TrackStatusRequest v15: round trip with params", async () => {
|
|
605
|
+
const msg = new Track.TrackStatusRequest({ requestId: 2n, trackNamespace: Path.from("test"), trackName: "audio" });
|
|
606
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_15);
|
|
607
|
+
const decoded = await decodeVersioned(encoded, Track.TrackStatusRequest.decode, Version.DRAFT_15);
|
|
608
|
+
expect(decoded.requestId).toBe(2n);
|
|
609
|
+
expect(decoded.trackNamespace).toBe("test");
|
|
610
|
+
expect(decoded.trackName).toBe("audio");
|
|
611
|
+
});
|
|
612
|
+
test("TrackStatusRequest v17: round trip with requiredRequestIdDelta", async () => {
|
|
613
|
+
const msg = new Track.TrackStatusRequest({ requestId: 3n, trackNamespace: Path.from("live"), trackName: "data" });
|
|
614
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
615
|
+
const decoded = await decodeVersioned(encoded, Track.TrackStatusRequest.decode, Version.DRAFT_17);
|
|
616
|
+
expect(decoded.requestId).toBe(3n);
|
|
617
|
+
expect(decoded.trackNamespace).toBe("live");
|
|
618
|
+
expect(decoded.trackName).toBe("data");
|
|
619
|
+
});
|
|
620
|
+
// --- Namespace encoding tests ---
|
|
621
|
+
// Helper to encode a namespace to raw bytes
|
|
622
|
+
async function encodeNamespace(namespace) {
|
|
623
|
+
const { stream, written } = createTestWritableStream();
|
|
624
|
+
const writer = new Writer(stream);
|
|
625
|
+
await Namespace.encode(writer, namespace);
|
|
626
|
+
writer.close();
|
|
627
|
+
await writer.closed;
|
|
628
|
+
return concatChunks(written);
|
|
629
|
+
}
|
|
630
|
+
// Helper to decode a namespace from raw bytes
|
|
631
|
+
async function decodeNamespace(bytes) {
|
|
632
|
+
const reader = new Reader(undefined, bytes);
|
|
633
|
+
return await Namespace.decode(reader);
|
|
634
|
+
}
|
|
635
|
+
test("Namespace: empty encodes as zero-length tuple", async () => {
|
|
636
|
+
const bytes = await encodeNamespace(Path.empty());
|
|
637
|
+
// Should be a single byte: varint 0 (zero parts)
|
|
638
|
+
expect(bytes).toEqual(new Uint8Array([0x00]));
|
|
639
|
+
});
|
|
640
|
+
test("Namespace: empty round trip", async () => {
|
|
641
|
+
const encoded = await encodeNamespace(Path.empty());
|
|
642
|
+
const decoded = await decodeNamespace(encoded);
|
|
643
|
+
expect(decoded).toBe("");
|
|
644
|
+
});
|
|
645
|
+
test("Namespace: single part round trip", async () => {
|
|
646
|
+
const encoded = await encodeNamespace(Path.from("test"));
|
|
647
|
+
const decoded = await decodeNamespace(encoded);
|
|
648
|
+
expect(decoded).toBe("test");
|
|
649
|
+
});
|
|
650
|
+
test("Namespace: single part encodes as one-length tuple", async () => {
|
|
651
|
+
const bytes = await encodeNamespace(Path.from("test"));
|
|
652
|
+
// varint 1 (one part), then length-prefixed "test"
|
|
653
|
+
expect(bytes[0]).toBe(0x01);
|
|
654
|
+
});
|
|
655
|
+
test("Namespace: multi-part round trip", async () => {
|
|
656
|
+
const encoded = await encodeNamespace(Path.from("conference/room/123"));
|
|
657
|
+
const decoded = await decodeNamespace(encoded);
|
|
658
|
+
expect(decoded).toBe("conference/room/123");
|
|
659
|
+
});
|
|
660
|
+
test("Namespace: multi-part encodes correct count", async () => {
|
|
661
|
+
const bytes = await encodeNamespace(Path.from("a/b/c"));
|
|
662
|
+
// First byte should be varint 3 (three parts)
|
|
663
|
+
expect(bytes[0]).toBe(0x03);
|
|
664
|
+
});
|
|
665
|
+
test("Namespace: Subscribe with empty namespace round trip", async () => {
|
|
666
|
+
const msg = new Subscribe.Subscribe({
|
|
667
|
+
requestId: 1n,
|
|
668
|
+
trackNamespace: Path.empty(),
|
|
669
|
+
trackName: "video",
|
|
670
|
+
subscriberPriority: 128,
|
|
671
|
+
});
|
|
672
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
673
|
+
const decoded = await decodeVersioned(encoded, Subscribe.Subscribe.decode, Version.DRAFT_17);
|
|
674
|
+
expect(decoded.trackNamespace).toBe("");
|
|
675
|
+
expect(decoded.trackName).toBe("video");
|
|
676
|
+
});
|
|
677
|
+
test("Namespace: PublishNamespace with empty namespace round trip", async () => {
|
|
678
|
+
const msg = new Announce.PublishNamespace({ requestId: 1n, trackNamespace: Path.empty() });
|
|
679
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_17);
|
|
680
|
+
const decoded = await decodeVersioned(encoded, Announce.PublishNamespace.decode, Version.DRAFT_17);
|
|
681
|
+
expect(decoded.trackNamespace).toBe("");
|
|
682
|
+
});
|
|
683
|
+
// --- Draft-18 message tests ---
|
|
684
|
+
test("Subscribe v18: round trip (same wire as v17 minus required_request_id_delta)", async () => {
|
|
685
|
+
const msg = new Subscribe.Subscribe({
|
|
686
|
+
requestId: 1n,
|
|
687
|
+
trackNamespace: Path.from("test"),
|
|
688
|
+
trackName: "video",
|
|
689
|
+
subscriberPriority: 128,
|
|
690
|
+
});
|
|
691
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_18);
|
|
692
|
+
const decoded = await decodeVersioned(encoded, Subscribe.Subscribe.decode, Version.DRAFT_18);
|
|
693
|
+
expect(decoded.requestId).toBe(1n);
|
|
694
|
+
expect(decoded.trackNamespace).toBe("test");
|
|
695
|
+
expect(decoded.trackName).toBe("video");
|
|
696
|
+
expect(decoded.subscriberPriority).toBe(128);
|
|
697
|
+
});
|
|
698
|
+
test("SubscribeOk v18: no requestId", async () => {
|
|
699
|
+
const msg = new Subscribe.SubscribeOk({ trackAlias: 42n });
|
|
700
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_18);
|
|
701
|
+
const decoded = await decodeVersioned(encoded, Subscribe.SubscribeOk.decode, Version.DRAFT_18);
|
|
702
|
+
expect(decoded.requestId).toBe(undefined);
|
|
703
|
+
expect(decoded.trackAlias).toBe(42n);
|
|
704
|
+
});
|
|
705
|
+
test("SubscribeUpdate v18: 1 byte shorter than v17 (no required_request_id_delta)", async () => {
|
|
706
|
+
const msg = new Subscribe.SubscribeUpdate({ requestId: 10n });
|
|
707
|
+
const v17 = await encodeVersioned(msg, Version.DRAFT_17);
|
|
708
|
+
const v18 = await encodeVersioned(msg, Version.DRAFT_18);
|
|
709
|
+
expect(v17.length).toBe(v18.length + 1);
|
|
710
|
+
// Round-trip the v18 bytes too. A different shape that happens to be one byte
|
|
711
|
+
// shorter would silently pass the length check otherwise.
|
|
712
|
+
const decoded = await decodeVersioned(v18, Subscribe.SubscribeUpdate.decode, Version.DRAFT_18);
|
|
713
|
+
expect(decoded.requestId).toBe(10n);
|
|
714
|
+
});
|
|
715
|
+
test("PublishNamespace v18: round trip without required_request_id_delta", async () => {
|
|
716
|
+
const msg = new Announce.PublishNamespace({ requestId: 5n, trackNamespace: Path.from("v18/broadcast") });
|
|
717
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_18);
|
|
718
|
+
const decoded = await decodeVersioned(encoded, Announce.PublishNamespace.decode, Version.DRAFT_18);
|
|
719
|
+
expect(decoded.requestId).toBe(5n);
|
|
720
|
+
expect(decoded.trackNamespace).toBe("v18/broadcast");
|
|
721
|
+
});
|
|
722
|
+
test("Publish v18: round trip", async () => {
|
|
723
|
+
const msg = new Publish({
|
|
724
|
+
requestId: 7n,
|
|
725
|
+
trackNamespace: Path.from("v18/ns"),
|
|
726
|
+
trackName: "video",
|
|
727
|
+
trackAlias: 1n,
|
|
728
|
+
groupOrder: 2,
|
|
729
|
+
contentExists: false,
|
|
730
|
+
largest: undefined,
|
|
731
|
+
forward: true,
|
|
732
|
+
});
|
|
733
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_18);
|
|
734
|
+
const decoded = await decodeVersioned(encoded, Publish.decode, Version.DRAFT_18);
|
|
735
|
+
expect(decoded.requestId).toBe(7n);
|
|
736
|
+
expect(decoded.trackNamespace).toBe("v18/ns");
|
|
737
|
+
expect(decoded.trackName).toBe("video");
|
|
738
|
+
expect(decoded.trackAlias).toBe(1n);
|
|
739
|
+
expect(decoded.forward).toBe(true);
|
|
740
|
+
});
|
|
741
|
+
test("PublishDone v18: no requestId", async () => {
|
|
742
|
+
const msg = new PublishDone({ statusCode: 200, reasonPhrase: "OK" });
|
|
743
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_18);
|
|
744
|
+
const decoded = await decodeVersioned(encoded, PublishDone.decode, Version.DRAFT_18);
|
|
745
|
+
expect(decoded.requestId).toBe(undefined);
|
|
746
|
+
expect(decoded.statusCode).toBe(200);
|
|
747
|
+
expect(decoded.reasonPhrase).toBe("OK");
|
|
748
|
+
});
|
|
749
|
+
test("RequestOk v18: no requestId (regression: don't treat Draft18 as legacy)", async () => {
|
|
750
|
+
const msg = new RequestOk({});
|
|
751
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_18);
|
|
752
|
+
const decoded = await decodeVersioned(encoded, RequestOk.decode, Version.DRAFT_18);
|
|
753
|
+
expect(decoded.requestId).toBe(undefined);
|
|
754
|
+
});
|
|
755
|
+
test("RequestError v18: no requestId, retry_interval still present", async () => {
|
|
756
|
+
const msg = new RequestError({
|
|
757
|
+
errorCode: 500,
|
|
758
|
+
reasonPhrase: "boom",
|
|
759
|
+
retryInterval: 1234n,
|
|
760
|
+
});
|
|
761
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_18);
|
|
762
|
+
const decoded = await decodeVersioned(encoded, RequestError.decode, Version.DRAFT_18);
|
|
763
|
+
expect(decoded.requestId).toBe(undefined);
|
|
764
|
+
expect(decoded.errorCode).toBe(500);
|
|
765
|
+
expect(decoded.reasonPhrase).toBe("boom");
|
|
766
|
+
expect(decoded.retryInterval).toBe(1234n);
|
|
767
|
+
});
|
|
768
|
+
test("Subscribe v18 wire bytes match v17 (FIRST_OBJECT bit doesn't affect control messages)", async () => {
|
|
769
|
+
const msg = new Subscribe.Subscribe({
|
|
770
|
+
requestId: 1n,
|
|
771
|
+
trackNamespace: Path.from("test"),
|
|
772
|
+
trackName: "video",
|
|
773
|
+
subscriberPriority: 128,
|
|
774
|
+
});
|
|
775
|
+
const v17 = await encodeVersioned(msg, Version.DRAFT_17);
|
|
776
|
+
const v18 = await encodeVersioned(msg, Version.DRAFT_18);
|
|
777
|
+
// Draft18 drops the required_request_id_delta (1 byte) from v17.
|
|
778
|
+
expect(v18.length).toBe(v17.length - 1);
|
|
779
|
+
});
|
|
780
|
+
test("PublishNamespaceDone v18: rejected (removed in draft-17+)", async () => {
|
|
781
|
+
const msg = new Announce.PublishNamespaceDone({ requestId: 1n });
|
|
782
|
+
const { stream } = createTestWritableStream();
|
|
783
|
+
const writer = new Writer(stream, Version.DRAFT_18);
|
|
784
|
+
await expect(msg.encode(writer, Version.DRAFT_18)).rejects.toThrow(/removed in draft-17/);
|
|
785
|
+
});
|
|
786
|
+
test("GoAway v18: round trip (timeout field still present)", async () => {
|
|
787
|
+
const msg = new GoAway.GoAway({ newSessionUri: "moqt://relay.example/", timeout: 5000n });
|
|
788
|
+
const encoded = await encodeVersioned(msg, Version.DRAFT_18);
|
|
789
|
+
const decoded = await decodeVersioned(encoded, GoAway.GoAway.decode, Version.DRAFT_18);
|
|
790
|
+
expect(decoded.newSessionUri).toBe("moqt://relay.example/");
|
|
791
|
+
expect(decoded.timeout).toBe(5000n);
|
|
792
|
+
});
|
|
793
|
+
test("Parameters v18 wire is identical to v17 (no count prefix, delta encoded keys)", async () => {
|
|
794
|
+
const params = new SetupOptions();
|
|
795
|
+
params.setVarint(0x2n, 42n);
|
|
796
|
+
params.setVarint(0x4n, 100n);
|
|
797
|
+
const v17 = (() => {
|
|
798
|
+
const { stream, written } = createTestWritableStream();
|
|
799
|
+
const w = new Writer(stream, Version.DRAFT_17);
|
|
800
|
+
return params.encode(w, Version.DRAFT_17).then(() => concatChunks(written));
|
|
801
|
+
})();
|
|
802
|
+
const v18 = (() => {
|
|
803
|
+
const { stream, written } = createTestWritableStream();
|
|
804
|
+
const w = new Writer(stream, Version.DRAFT_18);
|
|
805
|
+
return params.encode(w, Version.DRAFT_18).then(() => concatChunks(written));
|
|
806
|
+
})();
|
|
807
|
+
expect(Array.from(await v18)).toEqual(Array.from(await v17));
|
|
808
|
+
});
|
|
809
|
+
test("SubscribeNamespace: message IDs", () => {
|
|
810
|
+
// 0x11 through draft-17 (legacy), renumbered to 0x50 in draft-18 (#1542).
|
|
811
|
+
expect(SubscribeNamespace.SubscribeNamespaceLegacy.id).toBe(0x11);
|
|
812
|
+
expect(SubscribeNamespace.SubscribeNamespace.id).toBe(0x50);
|
|
813
|
+
});
|
|
814
|
+
test("SubscribeNamespace: decode rejects the wrong draft version", async () => {
|
|
815
|
+
// Modern 0x50 is draft-18+ only.
|
|
816
|
+
const modern = await encodeVersioned(new SubscribeNamespace.SubscribeNamespace({ namespace: Path.empty(), requestId: 0n }), Version.DRAFT_18);
|
|
817
|
+
await expect(decodeVersioned(modern, SubscribeNamespace.SubscribeNamespace.decode, Version.DRAFT_17)).rejects.toThrow(/draft-18\+ only/);
|
|
818
|
+
// Legacy 0x11 is draft-14..17 only.
|
|
819
|
+
const legacy = await encodeVersioned(new SubscribeNamespace.SubscribeNamespaceLegacy({ namespace: Path.empty(), requestId: 0n }), Version.DRAFT_17);
|
|
820
|
+
await expect(decodeVersioned(legacy, SubscribeNamespace.SubscribeNamespaceLegacy.decode, Version.DRAFT_18)).rejects.toThrow(/draft-14\.\.17 only/);
|
|
821
|
+
});
|
|
822
|
+
test("SubscribeNamespace: draft-18 omits subscribe options", async () => {
|
|
823
|
+
// The legacy draft-17 body carries the Subscribe Options field, so it is
|
|
824
|
+
// longer than the modern draft-18 body.
|
|
825
|
+
const modern = new SubscribeNamespace.SubscribeNamespace({ namespace: Path.empty(), requestId: 0n });
|
|
826
|
+
const legacy = new SubscribeNamespace.SubscribeNamespaceLegacy({ namespace: Path.empty(), requestId: 0n });
|
|
827
|
+
const v18 = await encodeVersioned(modern, Version.DRAFT_18);
|
|
828
|
+
const v17 = await encodeVersioned(legacy, Version.DRAFT_17);
|
|
829
|
+
expect(v18.byteLength).toBeLessThan(v17.byteLength);
|
|
830
|
+
// Modern round-trips on draft-18.
|
|
831
|
+
const m = await encodeVersioned(new SubscribeNamespace.SubscribeNamespace({ namespace: Path.from("example/meeting"), requestId: 4n }), Version.DRAFT_18);
|
|
832
|
+
const dm = await decodeVersioned(m, SubscribeNamespace.SubscribeNamespace.decode, Version.DRAFT_18);
|
|
833
|
+
expect(dm.requestId).toBe(4n);
|
|
834
|
+
expect(dm.namespace).toBe("example/meeting");
|
|
835
|
+
// Legacy round-trips on draft-16/17.
|
|
836
|
+
for (const version of [Version.DRAFT_16, Version.DRAFT_17]) {
|
|
837
|
+
const encoded = await encodeVersioned(new SubscribeNamespace.SubscribeNamespaceLegacy({ namespace: Path.from("example/meeting"), requestId: 4n }), version);
|
|
838
|
+
const decoded = await decodeVersioned(encoded, SubscribeNamespace.SubscribeNamespaceLegacy.decode, version);
|
|
839
|
+
expect(decoded.requestId).toBe(4n);
|
|
840
|
+
expect(decoded.namespace).toBe("example/meeting");
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
test("Group: draft-18 sets FIRST_OBJECT bit, draft-17 does not", async () => {
|
|
844
|
+
const makeGroup = () => new Group({
|
|
845
|
+
trackAlias: 7n,
|
|
846
|
+
groupId: 3,
|
|
847
|
+
subGroupId: 0,
|
|
848
|
+
publisherPriority: 0,
|
|
849
|
+
flags: {
|
|
850
|
+
hasExtensions: false,
|
|
851
|
+
hasSubgroup: false,
|
|
852
|
+
hasSubgroupObject: false,
|
|
853
|
+
hasEnd: true,
|
|
854
|
+
hasPriority: true,
|
|
855
|
+
},
|
|
856
|
+
});
|
|
857
|
+
// Round-trips on each version.
|
|
858
|
+
for (const version of [Version.DRAFT_17, Version.DRAFT_18]) {
|
|
859
|
+
const encoded = await encodeVersioned(makeGroup(), version);
|
|
860
|
+
const decoded = await decodeVersioned(encoded, Group.decode, version);
|
|
861
|
+
expect(decoded.groupId).toBe(3);
|
|
862
|
+
expect(decoded.trackAlias).toBe(7n);
|
|
863
|
+
expect(decoded.flags.hasEnd).toBe(true);
|
|
864
|
+
expect(decoded.flags.hasPriority).toBe(true);
|
|
865
|
+
}
|
|
866
|
+
// The draft-18 header carries the 0x40 FIRST_OBJECT bit (type 0x18 -> 0x58),
|
|
867
|
+
// so the same bytes are not a valid draft-17 subgroup header.
|
|
868
|
+
const v18 = await encodeVersioned(makeGroup(), Version.DRAFT_18);
|
|
869
|
+
const v17 = await encodeVersioned(makeGroup(), Version.DRAFT_17);
|
|
870
|
+
expect(Array.from(v18)).not.toEqual(Array.from(v17));
|
|
871
|
+
await expect(decodeVersioned(v18, Group.decode, Version.DRAFT_17)).rejects.toThrow(/Unsupported group type/);
|
|
872
|
+
});
|
|
873
|
+
//# sourceMappingURL=ietf.test.js.map
|