@tomgiee/tsdp 1.0.1 → 1.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 +0 -39
- package/dist/src/builder/media-builder.d.ts.map +1 -1
- package/dist/src/builder/media-builder.js +5 -12
- package/dist/src/builder/session-builder.d.ts.map +1 -1
- package/dist/src/builder/session-builder.js +4 -14
- package/dist/src/index.d.ts +6 -10
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +37 -49
- package/dist/src/parser/core.d.ts +366 -0
- package/dist/src/parser/core.d.ts.map +1 -0
- package/dist/src/parser/core.js +802 -0
- package/dist/src/parser/field-parser.d.ts +51 -8
- package/dist/src/parser/field-parser.d.ts.map +1 -1
- package/dist/src/parser/field-parser.js +91 -23
- package/dist/src/parser/media-parser.d.ts +1 -1
- package/dist/src/parser/media-parser.d.ts.map +1 -1
- package/dist/src/parser/media-parser.js +9 -15
- package/dist/src/parser/session-parser.d.ts.map +1 -1
- package/dist/src/parser/session-parser.js +16 -17
- package/dist/src/parser/time-parser.d.ts +1 -1
- package/dist/src/parser/time-parser.d.ts.map +1 -1
- package/dist/src/parser/time-parser.js +8 -8
- package/dist/src/serializer/fields.d.ts +167 -0
- package/dist/src/serializer/fields.d.ts.map +1 -0
- package/dist/src/serializer/fields.js +320 -0
- package/dist/src/serializer/media-serializer.js +6 -7
- package/dist/src/serializer/session-serializer.js +13 -15
- package/dist/src/types/attributes.d.ts.map +1 -1
- package/dist/src/types/fields.d.ts +5 -5
- package/dist/src/types/fields.d.ts.map +1 -1
- package/dist/src/types/fields.js +4 -4
- package/dist/src/types/media.d.ts +9 -10
- package/dist/src/types/media.d.ts.map +1 -1
- package/dist/src/types/media.js +2 -4
- package/dist/src/types/network.d.ts +15 -56
- package/dist/src/types/network.d.ts.map +1 -1
- package/dist/src/types/network.js +3 -34
- package/dist/src/types/primitives.d.ts +3 -147
- package/dist/src/types/primitives.d.ts.map +1 -1
- package/dist/src/types/primitives.js +2 -171
- package/dist/src/types/session.d.ts +14 -14
- package/dist/src/types/session.d.ts.map +1 -1
- package/dist/src/types/time.d.ts +4 -4
- package/dist/src/types/time.d.ts.map +1 -1
- package/dist/src/types/time.js +8 -6
- package/dist/src/utils/address-parser.d.ts +4 -4
- package/dist/src/utils/address-parser.d.ts.map +1 -1
- package/dist/src/utils/address-parser.js +9 -16
- package/dist/src/validator/validator.d.ts +94 -0
- package/dist/src/validator/validator.d.ts.map +1 -0
- package/dist/src/validator/validator.js +573 -0
- package/dist/tests/unit/parser/attribute-parser.test.js +106 -107
- package/dist/tests/unit/parser/field-parser.test.js +66 -66
- package/dist/tests/unit/parser/media-parser.test.js +38 -38
- package/dist/tests/unit/parser/primitive-parser.test.js +89 -90
- package/dist/tests/unit/parser/scanner.test.js +32 -32
- package/dist/tests/unit/parser/time-parser.test.js +22 -22
- package/dist/tests/unit/serializer/attribute-serializer.test.js +22 -22
- package/dist/tests/unit/serializer/field-serializer.test.js +57 -57
- package/dist/tests/unit/serializer/media-serializer.test.js +5 -6
- package/dist/tests/unit/serializer/session-serializer.test.js +24 -24
- package/dist/tests/unit/serializer/time-serializer.test.js +16 -16
- package/dist/tests/unit/types/network.test.js +21 -56
- package/dist/tests/unit/types/primitives.test.js +0 -39
- package/dist/tests/unit/validator/media-validator.test.js +34 -35
- package/dist/tests/unit/validator/semantic-validator.test.js +36 -37
- package/dist/tests/unit/validator/session-validator.test.js +54 -54
- package/package.json +1 -1
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* Tests for media-parser.ts
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
6
|
+
const core_1 = require("../../../src/parser/core");
|
|
7
7
|
const media_parser_1 = require("../../../src/parser/media-parser");
|
|
8
8
|
const errors_1 = require("../../../src/types/errors");
|
|
9
9
|
describe('media-parser', () => {
|
|
10
10
|
describe('parseMediaField', () => {
|
|
11
11
|
it('should parse audio media with simple port', () => {
|
|
12
|
-
const scanner = new
|
|
12
|
+
const scanner = new core_1.Scanner('audio 49170 RTP/AVP 0 8 101');
|
|
13
13
|
const media = (0, media_parser_1.parseMediaField)(scanner);
|
|
14
14
|
expect(media.type).toBe('audio');
|
|
15
15
|
expect(media.port.kind).toBe('simple');
|
|
@@ -20,7 +20,7 @@ describe('media-parser', () => {
|
|
|
20
20
|
expect(media.formats).toEqual(['0', '8', '101']);
|
|
21
21
|
});
|
|
22
22
|
it('should parse video media', () => {
|
|
23
|
-
const scanner = new
|
|
23
|
+
const scanner = new core_1.Scanner('video 51372 RTP/AVP 99');
|
|
24
24
|
const media = (0, media_parser_1.parseMediaField)(scanner);
|
|
25
25
|
expect(media.type).toBe('video');
|
|
26
26
|
expect(media.port.kind).toBe('simple');
|
|
@@ -31,7 +31,7 @@ describe('media-parser', () => {
|
|
|
31
31
|
expect(media.formats).toEqual(['99']);
|
|
32
32
|
});
|
|
33
33
|
it('should parse media with port range', () => {
|
|
34
|
-
const scanner = new
|
|
34
|
+
const scanner = new core_1.Scanner('audio 49170/2 RTP/AVP 0');
|
|
35
35
|
const media = (0, media_parser_1.parseMediaField)(scanner);
|
|
36
36
|
expect(media.type).toBe('audio');
|
|
37
37
|
expect(media.port.kind).toBe('range');
|
|
@@ -43,30 +43,30 @@ describe('media-parser', () => {
|
|
|
43
43
|
expect(media.formats).toEqual(['0']);
|
|
44
44
|
});
|
|
45
45
|
it('should parse text media', () => {
|
|
46
|
-
const scanner = new
|
|
46
|
+
const scanner = new core_1.Scanner('text 49174 RTP/AVP 96');
|
|
47
47
|
const media = (0, media_parser_1.parseMediaField)(scanner);
|
|
48
48
|
expect(media.type).toBe('text');
|
|
49
49
|
});
|
|
50
50
|
it('should parse application media', () => {
|
|
51
|
-
const scanner = new
|
|
51
|
+
const scanner = new core_1.Scanner('application 49175 udp wb');
|
|
52
52
|
const media = (0, media_parser_1.parseMediaField)(scanner);
|
|
53
53
|
expect(media.type).toBe('application');
|
|
54
54
|
expect(media.proto).toBe('udp');
|
|
55
55
|
expect(media.formats).toEqual(['wb']);
|
|
56
56
|
});
|
|
57
57
|
it('should parse message media', () => {
|
|
58
|
-
const scanner = new
|
|
58
|
+
const scanner = new core_1.Scanner('message 49176 RTP/AVP 97');
|
|
59
59
|
const media = (0, media_parser_1.parseMediaField)(scanner);
|
|
60
60
|
expect(media.type).toBe('message');
|
|
61
61
|
});
|
|
62
62
|
it('should parse image media', () => {
|
|
63
|
-
const scanner = new
|
|
63
|
+
const scanner = new core_1.Scanner('image 49177 udp t38');
|
|
64
64
|
const media = (0, media_parser_1.parseMediaField)(scanner);
|
|
65
65
|
expect(media.type).toBe('image');
|
|
66
66
|
expect(media.formats).toEqual(['t38']);
|
|
67
67
|
});
|
|
68
68
|
it('should parse media with port 0 (disabled)', () => {
|
|
69
|
-
const scanner = new
|
|
69
|
+
const scanner = new core_1.Scanner('audio 0 RTP/AVP 0');
|
|
70
70
|
const media = (0, media_parser_1.parseMediaField)(scanner);
|
|
71
71
|
expect(media.port.kind).toBe('simple');
|
|
72
72
|
if (media.port.kind === 'simple') {
|
|
@@ -74,47 +74,47 @@ describe('media-parser', () => {
|
|
|
74
74
|
}
|
|
75
75
|
});
|
|
76
76
|
it('should parse media with multiple formats', () => {
|
|
77
|
-
const scanner = new
|
|
77
|
+
const scanner = new core_1.Scanner('audio 49170 RTP/AVP 0 8 3 18 101');
|
|
78
78
|
const media = (0, media_parser_1.parseMediaField)(scanner);
|
|
79
79
|
expect(media.formats).toHaveLength(5);
|
|
80
80
|
expect(media.formats).toEqual(['0', '8', '3', '18', '101']);
|
|
81
81
|
});
|
|
82
82
|
it('should parse media with RTP/SAVP protocol', () => {
|
|
83
|
-
const scanner = new
|
|
83
|
+
const scanner = new core_1.Scanner('audio 49170 RTP/SAVP 0');
|
|
84
84
|
const media = (0, media_parser_1.parseMediaField)(scanner);
|
|
85
85
|
expect(media.proto).toBe('RTP/SAVP');
|
|
86
86
|
});
|
|
87
87
|
it('should parse media with RTP/SAVPF protocol', () => {
|
|
88
|
-
const scanner = new
|
|
88
|
+
const scanner = new core_1.Scanner('audio 49170 RTP/SAVPF 111');
|
|
89
89
|
const media = (0, media_parser_1.parseMediaField)(scanner);
|
|
90
90
|
expect(media.proto).toBe('RTP/SAVPF');
|
|
91
91
|
});
|
|
92
92
|
it('should throw for invalid port number', () => {
|
|
93
|
-
expect(() => (0, media_parser_1.parseMediaField)(new
|
|
94
|
-
expect(() => (0, media_parser_1.parseMediaField)(new
|
|
93
|
+
expect(() => (0, media_parser_1.parseMediaField)(new core_1.Scanner('audio abc RTP/AVP 0'))).toThrow(errors_1.ParseError);
|
|
94
|
+
expect(() => (0, media_parser_1.parseMediaField)(new core_1.Scanner('audio abc RTP/AVP 0'))).toThrow('Invalid port number');
|
|
95
95
|
});
|
|
96
96
|
it('should throw for invalid port range', () => {
|
|
97
|
-
expect(() => (0, media_parser_1.parseMediaField)(new
|
|
98
|
-
expect(() => (0, media_parser_1.parseMediaField)(new
|
|
97
|
+
expect(() => (0, media_parser_1.parseMediaField)(new core_1.Scanner('audio 49170/abc RTP/AVP 0'))).toThrow(errors_1.ParseError);
|
|
98
|
+
expect(() => (0, media_parser_1.parseMediaField)(new core_1.Scanner('audio 49170/abc RTP/AVP 0'))).toThrow('Invalid port range');
|
|
99
99
|
});
|
|
100
100
|
it('should throw for missing formats', () => {
|
|
101
|
-
expect(() => (0, media_parser_1.parseMediaField)(new
|
|
102
|
-
expect(() => (0, media_parser_1.parseMediaField)(new
|
|
101
|
+
expect(() => (0, media_parser_1.parseMediaField)(new core_1.Scanner('audio 49170 RTP/AVP'))).toThrow(errors_1.ParseError);
|
|
102
|
+
expect(() => (0, media_parser_1.parseMediaField)(new core_1.Scanner('audio 49170 RTP/AVP'))).toThrow('at least one media format');
|
|
103
103
|
});
|
|
104
104
|
it('should throw for port out of range', () => {
|
|
105
|
-
expect(() => (0, media_parser_1.parseMediaField)(new
|
|
105
|
+
expect(() => (0, media_parser_1.parseMediaField)(new core_1.Scanner('audio 70000 RTP/AVP 0'))).toThrow();
|
|
106
106
|
});
|
|
107
107
|
it('should throw for negative port', () => {
|
|
108
|
-
expect(() => (0, media_parser_1.parseMediaField)(new
|
|
108
|
+
expect(() => (0, media_parser_1.parseMediaField)(new core_1.Scanner('audio -1 RTP/AVP 0'))).toThrow();
|
|
109
109
|
});
|
|
110
110
|
it('should throw for invalid port range format', () => {
|
|
111
|
-
expect(() => (0, media_parser_1.parseMediaField)(new
|
|
112
|
-
expect(() => (0, media_parser_1.parseMediaField)(new
|
|
111
|
+
expect(() => (0, media_parser_1.parseMediaField)(new core_1.Scanner('audio 49170/2/3 RTP/AVP 0'))).toThrow(errors_1.ParseError);
|
|
112
|
+
expect(() => (0, media_parser_1.parseMediaField)(new core_1.Scanner('audio 49170/2/3 RTP/AVP 0'))).toThrow('Invalid port range format');
|
|
113
113
|
});
|
|
114
114
|
});
|
|
115
115
|
describe('parseMediaDescription', () => {
|
|
116
116
|
it('should parse minimal media description (m= only)', () => {
|
|
117
|
-
const mediaScanner = new
|
|
117
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0');
|
|
118
118
|
const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, []);
|
|
119
119
|
expect(md.media.type).toBe('audio');
|
|
120
120
|
expect(md.information).toBeUndefined();
|
|
@@ -124,14 +124,14 @@ describe('media-parser', () => {
|
|
|
124
124
|
expect(md.attributes).toHaveLength(0);
|
|
125
125
|
});
|
|
126
126
|
it('should parse media description with information', () => {
|
|
127
|
-
const mediaScanner = new
|
|
127
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0');
|
|
128
128
|
const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
|
|
129
129
|
{ type: 'i', value: 'Audio Stream' },
|
|
130
130
|
]);
|
|
131
131
|
expect(md.information).toBe('Audio Stream');
|
|
132
132
|
});
|
|
133
133
|
it('should parse media description with connection', () => {
|
|
134
|
-
const mediaScanner = new
|
|
134
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0');
|
|
135
135
|
const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
|
|
136
136
|
{ type: 'c', value: 'IN IP4 192.168.1.100' },
|
|
137
137
|
]);
|
|
@@ -140,7 +140,7 @@ describe('media-parser', () => {
|
|
|
140
140
|
expect(md.connections[0].addrType).toBe('IP4');
|
|
141
141
|
});
|
|
142
142
|
it('should parse media description with multiple connections', () => {
|
|
143
|
-
const mediaScanner = new
|
|
143
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0');
|
|
144
144
|
const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
|
|
145
145
|
{ type: 'c', value: 'IN IP4 192.168.1.100' },
|
|
146
146
|
{ type: 'c', value: 'IN IP4 192.168.1.101' },
|
|
@@ -148,7 +148,7 @@ describe('media-parser', () => {
|
|
|
148
148
|
expect(md.connections).toHaveLength(2);
|
|
149
149
|
});
|
|
150
150
|
it('should parse media description with bandwidth', () => {
|
|
151
|
-
const mediaScanner = new
|
|
151
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0');
|
|
152
152
|
const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
|
|
153
153
|
{ type: 'b', value: 'AS:64' },
|
|
154
154
|
]);
|
|
@@ -157,7 +157,7 @@ describe('media-parser', () => {
|
|
|
157
157
|
expect(md.bandwidths[0].value).toBe(64);
|
|
158
158
|
});
|
|
159
159
|
it('should parse media description with multiple bandwidths', () => {
|
|
160
|
-
const mediaScanner = new
|
|
160
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0');
|
|
161
161
|
const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
|
|
162
162
|
{ type: 'b', value: 'AS:64' },
|
|
163
163
|
{ type: 'b', value: 'CT:128' },
|
|
@@ -165,14 +165,14 @@ describe('media-parser', () => {
|
|
|
165
165
|
expect(md.bandwidths).toHaveLength(2);
|
|
166
166
|
});
|
|
167
167
|
it('should parse media description with key', () => {
|
|
168
|
-
const mediaScanner = new
|
|
168
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0');
|
|
169
169
|
const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
|
|
170
170
|
{ type: 'k', value: 'clear:mykey123' },
|
|
171
171
|
]);
|
|
172
172
|
expect(md.key).toBe('clear:mykey123');
|
|
173
173
|
});
|
|
174
174
|
it('should parse media description with attributes', () => {
|
|
175
|
-
const mediaScanner = new
|
|
175
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0 8');
|
|
176
176
|
const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
|
|
177
177
|
{ type: 'a', value: 'rtpmap:0 PCMU/8000' },
|
|
178
178
|
{ type: 'a', value: 'rtpmap:8 PCMA/8000' },
|
|
@@ -182,7 +182,7 @@ describe('media-parser', () => {
|
|
|
182
182
|
expect(md.attributes).toHaveLength(4);
|
|
183
183
|
});
|
|
184
184
|
it('should parse complete media description with all fields', () => {
|
|
185
|
-
const mediaScanner = new
|
|
185
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0');
|
|
186
186
|
const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
|
|
187
187
|
{ type: 'i', value: 'Voice' },
|
|
188
188
|
{ type: 'c', value: 'IN IP4 192.168.1.100' },
|
|
@@ -198,7 +198,7 @@ describe('media-parser', () => {
|
|
|
198
198
|
expect(md.attributes).toHaveLength(2);
|
|
199
199
|
});
|
|
200
200
|
it('should parse media description with correct field ordering', () => {
|
|
201
|
-
const mediaScanner = new
|
|
201
|
+
const mediaScanner = new core_1.Scanner('video 51372 RTP/AVP 99');
|
|
202
202
|
const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
|
|
203
203
|
{ type: 'i', value: 'Video Stream' },
|
|
204
204
|
{ type: 'c', value: 'IN IP4 192.168.1.100' },
|
|
@@ -211,31 +211,31 @@ describe('media-parser', () => {
|
|
|
211
211
|
expect(md.information).toBe('Video Stream');
|
|
212
212
|
});
|
|
213
213
|
it('should throw for duplicate information field', () => {
|
|
214
|
-
const mediaScanner = new
|
|
214
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0');
|
|
215
215
|
expect(() => (0, media_parser_1.parseMediaDescription)(mediaScanner, [
|
|
216
216
|
{ type: 'i', value: 'First' },
|
|
217
217
|
{ type: 'i', value: 'Second' },
|
|
218
218
|
])).toThrow(errors_1.ParseError);
|
|
219
|
-
expect(() => (0, media_parser_1.parseMediaDescription)(new
|
|
219
|
+
expect(() => (0, media_parser_1.parseMediaDescription)(new core_1.Scanner('audio 49170 RTP/AVP 0'), [
|
|
220
220
|
{ type: 'i', value: 'First' },
|
|
221
221
|
{ type: 'i', value: 'Second' },
|
|
222
222
|
])).toThrow('Duplicate information field');
|
|
223
223
|
});
|
|
224
224
|
it('should throw for duplicate key field', () => {
|
|
225
|
-
const mediaScanner = new
|
|
225
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0');
|
|
226
226
|
expect(() => (0, media_parser_1.parseMediaDescription)(mediaScanner, [
|
|
227
227
|
{ type: 'k', value: 'clear:key1' },
|
|
228
228
|
{ type: 'k', value: 'clear:key2' },
|
|
229
229
|
])).toThrow(errors_1.ParseError);
|
|
230
|
-
expect(() => (0, media_parser_1.parseMediaDescription)(new
|
|
230
|
+
expect(() => (0, media_parser_1.parseMediaDescription)(new core_1.Scanner('audio 49170 RTP/AVP 0'), [
|
|
231
231
|
{ type: 'k', value: 'clear:key1' },
|
|
232
232
|
{ type: 'k', value: 'clear:key2' },
|
|
233
233
|
])).toThrow('Duplicate key field');
|
|
234
234
|
});
|
|
235
235
|
it('should throw for unexpected field type', () => {
|
|
236
|
-
const mediaScanner = new
|
|
236
|
+
const mediaScanner = new core_1.Scanner('audio 49170 RTP/AVP 0');
|
|
237
237
|
expect(() => (0, media_parser_1.parseMediaDescription)(mediaScanner, [{ type: 'v', value: '0' }])).toThrow(errors_1.ParseError);
|
|
238
|
-
expect(() => (0, media_parser_1.parseMediaDescription)(new
|
|
238
|
+
expect(() => (0, media_parser_1.parseMediaDescription)(new core_1.Scanner('audio 49170 RTP/AVP 0'), [{ type: 'v', value: '0' }])).toThrow('Unexpected field type');
|
|
239
239
|
});
|
|
240
240
|
});
|
|
241
241
|
});
|
|
@@ -3,258 +3,257 @@
|
|
|
3
3
|
* Tests for primitive-parser.ts
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
7
|
-
const primitive_parser_1 = require("../../../src/parser/primitive-parser");
|
|
6
|
+
const core_1 = require("../../../src/parser/core");
|
|
8
7
|
const errors_1 = require("../../../src/types/errors");
|
|
9
8
|
describe('primitive-parser', () => {
|
|
10
9
|
describe('parseToken', () => {
|
|
11
10
|
it('should parse valid tokens', () => {
|
|
12
|
-
const scanner = new
|
|
13
|
-
const token = (0,
|
|
11
|
+
const scanner = new core_1.Scanner('RTP');
|
|
12
|
+
const token = (0, core_1.parseToken)(scanner);
|
|
14
13
|
expect(token).toBe('RTP');
|
|
15
14
|
});
|
|
16
15
|
it('should parse tokens with various characters', () => {
|
|
17
|
-
const scanner = new
|
|
18
|
-
const token = (0,
|
|
16
|
+
const scanner = new core_1.Scanner('a1B2-test.name_v123');
|
|
17
|
+
const token = (0, core_1.parseToken)(scanner);
|
|
19
18
|
expect(token).toBe('a1B2-test.name_v123');
|
|
20
19
|
});
|
|
21
20
|
it('should stop at whitespace', () => {
|
|
22
|
-
const scanner = new
|
|
23
|
-
const token = (0,
|
|
21
|
+
const scanner = new core_1.Scanner('token next');
|
|
22
|
+
const token = (0, core_1.parseToken)(scanner);
|
|
24
23
|
expect(token).toBe('token');
|
|
25
24
|
expect(scanner.peek()).toBe(' ');
|
|
26
25
|
});
|
|
27
26
|
it('should throw if no token found', () => {
|
|
28
|
-
const scanner = new
|
|
29
|
-
expect(() => (0,
|
|
27
|
+
const scanner = new core_1.Scanner(' ');
|
|
28
|
+
expect(() => (0, core_1.parseToken)(scanner)).toThrow(errors_1.ParseError);
|
|
30
29
|
});
|
|
31
30
|
});
|
|
32
31
|
describe('parseInteger', () => {
|
|
33
32
|
it('should parse positive integers', () => {
|
|
34
|
-
const scanner = new
|
|
35
|
-
const num = (0,
|
|
33
|
+
const scanner = new core_1.Scanner('12345');
|
|
34
|
+
const num = (0, core_1.parseInteger)(scanner);
|
|
36
35
|
expect(num).toBe(12345);
|
|
37
36
|
});
|
|
38
37
|
it('should parse zero', () => {
|
|
39
|
-
const scanner = new
|
|
40
|
-
const num = (0,
|
|
38
|
+
const scanner = new core_1.Scanner('0');
|
|
39
|
+
const num = (0, core_1.parseInteger)(scanner);
|
|
41
40
|
expect(num).toBe(0);
|
|
42
41
|
});
|
|
43
42
|
it('should stop at non-digit', () => {
|
|
44
|
-
const scanner = new
|
|
45
|
-
const num = (0,
|
|
43
|
+
const scanner = new core_1.Scanner('123abc');
|
|
44
|
+
const num = (0, core_1.parseInteger)(scanner);
|
|
46
45
|
expect(num).toBe(123);
|
|
47
46
|
expect(scanner.peek()).toBe('a');
|
|
48
47
|
});
|
|
49
48
|
it('should throw if no digits found', () => {
|
|
50
|
-
const scanner = new
|
|
51
|
-
expect(() => (0,
|
|
49
|
+
const scanner = new core_1.Scanner('abc');
|
|
50
|
+
expect(() => (0, core_1.parseInteger)(scanner)).toThrow(errors_1.ParseError);
|
|
52
51
|
});
|
|
53
52
|
});
|
|
54
53
|
describe('parseByte', () => {
|
|
55
54
|
it('should parse valid byte values', () => {
|
|
56
|
-
expect((0,
|
|
57
|
-
expect((0,
|
|
58
|
-
expect((0,
|
|
55
|
+
expect((0, core_1.parseByte)(new core_1.Scanner('0'))).toBe(0);
|
|
56
|
+
expect((0, core_1.parseByte)(new core_1.Scanner('128'))).toBe(128);
|
|
57
|
+
expect((0, core_1.parseByte)(new core_1.Scanner('255'))).toBe(255);
|
|
59
58
|
});
|
|
60
59
|
it('should throw for values > 255', () => {
|
|
61
|
-
expect(() => (0,
|
|
62
|
-
expect(() => (0,
|
|
60
|
+
expect(() => (0, core_1.parseByte)(new core_1.Scanner('256'))).toThrow(errors_1.ParseError);
|
|
61
|
+
expect(() => (0, core_1.parseByte)(new core_1.Scanner('256'))).toThrow('out of range');
|
|
63
62
|
});
|
|
64
63
|
it('should throw for negative values', () => {
|
|
65
|
-
const scanner = new
|
|
66
|
-
expect(() => (0,
|
|
64
|
+
const scanner = new core_1.Scanner('-1');
|
|
65
|
+
expect(() => (0, core_1.parseByte)(scanner)).toThrow(errors_1.ParseError);
|
|
67
66
|
});
|
|
68
67
|
});
|
|
69
68
|
describe('parseNonWSString', () => {
|
|
70
69
|
it('should parse non-whitespace string', () => {
|
|
71
|
-
const scanner = new
|
|
72
|
-
const str = (0,
|
|
70
|
+
const scanner = new core_1.Scanner('hello world');
|
|
71
|
+
const str = (0, core_1.parseNonWSString)(scanner);
|
|
73
72
|
expect(str).toBe('hello');
|
|
74
73
|
expect(scanner.peek()).toBe(' ');
|
|
75
74
|
});
|
|
76
75
|
it('should stop at newline', () => {
|
|
77
|
-
const scanner = new
|
|
78
|
-
const str = (0,
|
|
76
|
+
const scanner = new core_1.Scanner('test\nnext');
|
|
77
|
+
const str = (0, core_1.parseNonWSString)(scanner);
|
|
79
78
|
expect(str).toBe('test');
|
|
80
79
|
expect(scanner.peek()).toBe('\n');
|
|
81
80
|
});
|
|
82
81
|
it('should throw if starts with whitespace', () => {
|
|
83
|
-
const scanner = new
|
|
84
|
-
expect(() => (0,
|
|
82
|
+
const scanner = new core_1.Scanner(' test');
|
|
83
|
+
expect(() => (0, core_1.parseNonWSString)(scanner)).toThrow(errors_1.ParseError);
|
|
85
84
|
});
|
|
86
85
|
});
|
|
87
86
|
describe('parseByteString', () => {
|
|
88
87
|
it('should parse byte-string', () => {
|
|
89
|
-
const scanner = new
|
|
90
|
-
const str = (0,
|
|
88
|
+
const scanner = new core_1.Scanner('test123!@#');
|
|
89
|
+
const str = (0, core_1.parseByteString)(scanner);
|
|
91
90
|
expect(str).toBe('test123!@#');
|
|
92
91
|
});
|
|
93
92
|
it('should stop at newline', () => {
|
|
94
|
-
const scanner = new
|
|
95
|
-
const str = (0,
|
|
93
|
+
const scanner = new core_1.Scanner('test\nnext');
|
|
94
|
+
const str = (0, core_1.parseByteString)(scanner);
|
|
96
95
|
expect(str).toBe('test');
|
|
97
96
|
});
|
|
98
97
|
});
|
|
99
98
|
describe('parseIP4Address', () => {
|
|
100
99
|
it('should parse valid IPv4 addresses', () => {
|
|
101
|
-
expect((0,
|
|
102
|
-
expect((0,
|
|
103
|
-
expect((0,
|
|
100
|
+
expect((0, core_1.parseIP4Address)(new core_1.Scanner('192.168.1.1'))).toBe('192.168.1.1');
|
|
101
|
+
expect((0, core_1.parseIP4Address)(new core_1.Scanner('0.0.0.0'))).toBe('0.0.0.0');
|
|
102
|
+
expect((0, core_1.parseIP4Address)(new core_1.Scanner('255.255.255.255'))).toBe('255.255.255.255');
|
|
104
103
|
});
|
|
105
104
|
it('should stop at whitespace', () => {
|
|
106
|
-
const scanner = new
|
|
107
|
-
const addr = (0,
|
|
105
|
+
const scanner = new core_1.Scanner('192.168.1.1 next');
|
|
106
|
+
const addr = (0, core_1.parseIP4Address)(scanner);
|
|
108
107
|
expect(addr).toBe('192.168.1.1');
|
|
109
108
|
expect(scanner.peek()).toBe(' ');
|
|
110
109
|
});
|
|
111
110
|
it('should throw for invalid IPv4', () => {
|
|
112
|
-
expect(() => (0,
|
|
113
|
-
expect(() => (0,
|
|
111
|
+
expect(() => (0, core_1.parseIP4Address)(new core_1.Scanner('256.1.1.1'))).toThrow(errors_1.ParseError);
|
|
112
|
+
expect(() => (0, core_1.parseIP4Address)(new core_1.Scanner('192.168.1'))).toThrow(errors_1.ParseError);
|
|
114
113
|
});
|
|
115
114
|
});
|
|
116
115
|
describe('parseIP6Address', () => {
|
|
117
116
|
it('should parse valid IPv6 addresses', () => {
|
|
118
|
-
expect((0,
|
|
119
|
-
expect((0,
|
|
120
|
-
expect((0,
|
|
117
|
+
expect((0, core_1.parseIP6Address)(new core_1.Scanner('2001:db8::1'))).toBe('2001:db8::1');
|
|
118
|
+
expect((0, core_1.parseIP6Address)(new core_1.Scanner('::1'))).toBe('::1');
|
|
119
|
+
expect((0, core_1.parseIP6Address)(new core_1.Scanner('fe80::1'))).toBe('fe80::1');
|
|
121
120
|
});
|
|
122
121
|
it('should parse IPv6 with mixed IPv4', () => {
|
|
123
|
-
const addr = (0,
|
|
122
|
+
const addr = (0, core_1.parseIP6Address)(new core_1.Scanner('::ffff:192.0.2.1'));
|
|
124
123
|
expect(addr).toBe('::ffff:192.0.2.1');
|
|
125
124
|
});
|
|
126
125
|
it('should stop at whitespace', () => {
|
|
127
|
-
const scanner = new
|
|
128
|
-
const addr = (0,
|
|
126
|
+
const scanner = new core_1.Scanner('2001:db8::1 next');
|
|
127
|
+
const addr = (0, core_1.parseIP6Address)(scanner);
|
|
129
128
|
expect(addr).toBe('2001:db8::1');
|
|
130
129
|
expect(scanner.peek()).toBe(' ');
|
|
131
130
|
});
|
|
132
131
|
it('should throw for invalid IPv6', () => {
|
|
133
|
-
expect(() => (0,
|
|
134
|
-
expect(() => (0,
|
|
132
|
+
expect(() => (0, core_1.parseIP6Address)(new core_1.Scanner('gggg::1'))).toThrow(errors_1.ParseError);
|
|
133
|
+
expect(() => (0, core_1.parseIP6Address)(new core_1.Scanner('::1::2'))).toThrow(errors_1.ParseError);
|
|
135
134
|
});
|
|
136
135
|
});
|
|
137
136
|
describe('parseFQDN', () => {
|
|
138
137
|
it('should parse valid FQDNs', () => {
|
|
139
|
-
expect((0,
|
|
140
|
-
expect((0,
|
|
141
|
-
expect((0,
|
|
138
|
+
expect((0, core_1.parseFQDN)(new core_1.Scanner('example.com'))).toBe('example.com');
|
|
139
|
+
expect((0, core_1.parseFQDN)(new core_1.Scanner('sub.domain.example.org'))).toBe('sub.domain.example.org');
|
|
140
|
+
expect((0, core_1.parseFQDN)(new core_1.Scanner('localhost'))).toBe('localhost');
|
|
142
141
|
});
|
|
143
142
|
it('should parse FQDN with hyphens', () => {
|
|
144
|
-
expect((0,
|
|
143
|
+
expect((0, core_1.parseFQDN)(new core_1.Scanner('my-server.example.com'))).toBe('my-server.example.com');
|
|
145
144
|
});
|
|
146
145
|
it('should stop at whitespace', () => {
|
|
147
|
-
const scanner = new
|
|
148
|
-
const fqdn = (0,
|
|
146
|
+
const scanner = new core_1.Scanner('example.com next');
|
|
147
|
+
const fqdn = (0, core_1.parseFQDN)(scanner);
|
|
149
148
|
expect(fqdn).toBe('example.com');
|
|
150
149
|
expect(scanner.peek()).toBe(' ');
|
|
151
150
|
});
|
|
152
151
|
it('should throw for invalid FQDN', () => {
|
|
153
|
-
expect(() => (0,
|
|
154
|
-
expect(() => (0,
|
|
152
|
+
expect(() => (0, core_1.parseFQDN)(new core_1.Scanner('-invalid.com'))).toThrow(errors_1.ParseError);
|
|
153
|
+
expect(() => (0, core_1.parseFQDN)(new core_1.Scanner('invalid-.com'))).toThrow(errors_1.ParseError);
|
|
155
154
|
});
|
|
156
155
|
});
|
|
157
156
|
describe('parseNtpTime', () => {
|
|
158
157
|
it('should parse NTP timestamps', () => {
|
|
159
|
-
expect((0,
|
|
160
|
-
expect((0,
|
|
158
|
+
expect((0, core_1.parseNtpTime)(new core_1.Scanner('0'))).toBe(0);
|
|
159
|
+
expect((0, core_1.parseNtpTime)(new core_1.Scanner('3724394400'))).toBe(3724394400);
|
|
161
160
|
});
|
|
162
161
|
it('should stop at non-digit', () => {
|
|
163
|
-
const scanner = new
|
|
164
|
-
const time = (0,
|
|
162
|
+
const scanner = new core_1.Scanner('123 456');
|
|
163
|
+
const time = (0, core_1.parseNtpTime)(scanner);
|
|
165
164
|
expect(time).toBe(123);
|
|
166
165
|
expect(scanner.peek()).toBe(' ');
|
|
167
166
|
});
|
|
168
167
|
it('should throw for negative values', () => {
|
|
169
|
-
const scanner = new
|
|
170
|
-
expect(() => (0,
|
|
168
|
+
const scanner = new core_1.Scanner('-1');
|
|
169
|
+
expect(() => (0, core_1.parseNtpTime)(scanner)).toThrow(errors_1.ParseError);
|
|
171
170
|
});
|
|
172
171
|
});
|
|
173
172
|
describe('parseTypedTime', () => {
|
|
174
173
|
it('should parse time with days', () => {
|
|
175
|
-
const time = (0,
|
|
174
|
+
const time = (0, core_1.parseTypedTime)(new core_1.Scanner('7d'));
|
|
176
175
|
expect(time.value).toBe(7);
|
|
177
176
|
expect(time.unit).toBe('days');
|
|
178
177
|
});
|
|
179
178
|
it('should parse time with hours', () => {
|
|
180
|
-
const time = (0,
|
|
179
|
+
const time = (0, core_1.parseTypedTime)(new core_1.Scanner('3h'));
|
|
181
180
|
expect(time.value).toBe(3);
|
|
182
181
|
expect(time.unit).toBe('hours');
|
|
183
182
|
});
|
|
184
183
|
it('should parse time with minutes', () => {
|
|
185
|
-
const time = (0,
|
|
184
|
+
const time = (0, core_1.parseTypedTime)(new core_1.Scanner('120m'));
|
|
186
185
|
expect(time.value).toBe(120);
|
|
187
186
|
expect(time.unit).toBe('minutes');
|
|
188
187
|
});
|
|
189
188
|
it('should parse time with seconds (no suffix)', () => {
|
|
190
|
-
const time = (0,
|
|
189
|
+
const time = (0, core_1.parseTypedTime)(new core_1.Scanner('3600'));
|
|
191
190
|
expect(time.value).toBe(3600);
|
|
192
191
|
expect(time.unit).toBe('seconds');
|
|
193
192
|
});
|
|
194
193
|
it('should parse time with explicit seconds', () => {
|
|
195
|
-
const time = (0,
|
|
194
|
+
const time = (0, core_1.parseTypedTime)(new core_1.Scanner('30s'));
|
|
196
195
|
expect(time.value).toBe(30);
|
|
197
196
|
expect(time.unit).toBe('seconds');
|
|
198
197
|
});
|
|
199
198
|
it('should stop at whitespace', () => {
|
|
200
|
-
const scanner = new
|
|
201
|
-
const time = (0,
|
|
199
|
+
const scanner = new core_1.Scanner('7d 3h');
|
|
200
|
+
const time = (0, core_1.parseTypedTime)(scanner);
|
|
202
201
|
expect(time.value).toBe(7);
|
|
203
202
|
expect(time.unit).toBe('days');
|
|
204
203
|
expect(scanner.peek()).toBe(' ');
|
|
205
204
|
});
|
|
206
205
|
it('should throw for invalid format', () => {
|
|
207
|
-
expect(() => (0,
|
|
206
|
+
expect(() => (0, core_1.parseTypedTime)(new core_1.Scanner('abc'))).toThrow(errors_1.ParseError);
|
|
208
207
|
});
|
|
209
208
|
});
|
|
210
209
|
describe('parseEmail', () => {
|
|
211
210
|
it('should parse email address', () => {
|
|
212
|
-
const scanner = new
|
|
213
|
-
const email = (0,
|
|
211
|
+
const scanner = new core_1.Scanner('user@example.com\n');
|
|
212
|
+
const email = (0, core_1.parseEmail)(scanner);
|
|
214
213
|
expect(email).toBe('user@example.com');
|
|
215
214
|
});
|
|
216
215
|
it('should parse email with display name', () => {
|
|
217
|
-
const scanner = new
|
|
218
|
-
const email = (0,
|
|
216
|
+
const scanner = new core_1.Scanner('John Doe <john@example.com>\n');
|
|
217
|
+
const email = (0, core_1.parseEmail)(scanner);
|
|
219
218
|
expect(email).toBe('John Doe <john@example.com>');
|
|
220
219
|
});
|
|
221
220
|
it('should stop at newline', () => {
|
|
222
|
-
const scanner = new
|
|
223
|
-
const email = (0,
|
|
221
|
+
const scanner = new core_1.Scanner('user@example.com\r\nnext line');
|
|
222
|
+
const email = (0, core_1.parseEmail)(scanner);
|
|
224
223
|
expect(email).toBe('user@example.com');
|
|
225
224
|
});
|
|
226
225
|
});
|
|
227
226
|
describe('parsePhone', () => {
|
|
228
227
|
it('should parse phone number', () => {
|
|
229
|
-
const scanner = new
|
|
230
|
-
const phone = (0,
|
|
228
|
+
const scanner = new core_1.Scanner('+1-555-1234\n');
|
|
229
|
+
const phone = (0, core_1.parsePhone)(scanner);
|
|
231
230
|
expect(phone).toBe('+1-555-1234');
|
|
232
231
|
});
|
|
233
232
|
it('should parse phone with display name', () => {
|
|
234
|
-
const scanner = new
|
|
235
|
-
const phone = (0,
|
|
233
|
+
const scanner = new core_1.Scanner('John Doe +1-555-1234\n');
|
|
234
|
+
const phone = (0, core_1.parsePhone)(scanner);
|
|
236
235
|
expect(phone).toBe('John Doe +1-555-1234');
|
|
237
236
|
});
|
|
238
237
|
it('should stop at newline', () => {
|
|
239
|
-
const scanner = new
|
|
240
|
-
const phone = (0,
|
|
238
|
+
const scanner = new core_1.Scanner('+1-555-1234\r\nnext');
|
|
239
|
+
const phone = (0, core_1.parsePhone)(scanner);
|
|
241
240
|
expect(phone).toBe('+1-555-1234');
|
|
242
241
|
});
|
|
243
242
|
});
|
|
244
243
|
describe('parseURI', () => {
|
|
245
244
|
it('should parse HTTP URI', () => {
|
|
246
|
-
const scanner = new
|
|
247
|
-
const uri = (0,
|
|
245
|
+
const scanner = new core_1.Scanner('http://example.com/path\n');
|
|
246
|
+
const uri = (0, core_1.parseURI)(scanner);
|
|
248
247
|
expect(uri).toBe('http://example.com/path');
|
|
249
248
|
});
|
|
250
249
|
it('should parse HTTPS URI', () => {
|
|
251
|
-
const scanner = new
|
|
252
|
-
const uri = (0,
|
|
250
|
+
const scanner = new core_1.Scanner('https://example.com:8080/path?query=value\n');
|
|
251
|
+
const uri = (0, core_1.parseURI)(scanner);
|
|
253
252
|
expect(uri).toBe('https://example.com:8080/path?query=value');
|
|
254
253
|
});
|
|
255
254
|
it('should stop at newline', () => {
|
|
256
|
-
const scanner = new
|
|
257
|
-
const uri = (0,
|
|
255
|
+
const scanner = new core_1.Scanner('http://example.com\r\nnext');
|
|
256
|
+
const uri = (0, core_1.parseURI)(scanner);
|
|
258
257
|
expect(uri).toBe('http://example.com');
|
|
259
258
|
});
|
|
260
259
|
});
|