@tomgiee/tsdp 1.0.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/LICENSE +21 -0
- package/README.md +124 -0
- package/dist/src/builder/media-builder.d.ts +221 -0
- package/dist/src/builder/media-builder.d.ts.map +1 -0
- package/dist/src/builder/media-builder.js +385 -0
- package/dist/src/builder/session-builder.d.ts +195 -0
- package/dist/src/builder/session-builder.d.ts.map +1 -0
- package/dist/src/builder/session-builder.js +366 -0
- package/dist/src/index.d.ts +67 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +250 -0
- package/dist/src/parser/attribute-parser.d.ts +100 -0
- package/dist/src/parser/attribute-parser.d.ts.map +1 -0
- package/dist/src/parser/attribute-parser.js +217 -0
- package/dist/src/parser/field-parser.d.ts +124 -0
- package/dist/src/parser/field-parser.d.ts.map +1 -0
- package/dist/src/parser/field-parser.js +335 -0
- package/dist/src/parser/media-parser.d.ts +45 -0
- package/dist/src/parser/media-parser.d.ts.map +1 -0
- package/dist/src/parser/media-parser.js +157 -0
- package/dist/src/parser/primitive-parser.d.ts +138 -0
- package/dist/src/parser/primitive-parser.d.ts.map +1 -0
- package/dist/src/parser/primitive-parser.js +316 -0
- package/dist/src/parser/scanner.d.ts +142 -0
- package/dist/src/parser/scanner.d.ts.map +1 -0
- package/dist/src/parser/scanner.js +284 -0
- package/dist/src/parser/session-parser.d.ts +35 -0
- package/dist/src/parser/session-parser.d.ts.map +1 -0
- package/dist/src/parser/session-parser.js +207 -0
- package/dist/src/parser/time-parser.d.ts +74 -0
- package/dist/src/parser/time-parser.d.ts.map +1 -0
- package/dist/src/parser/time-parser.js +168 -0
- package/dist/src/serializer/attribute-serializer.d.ts +18 -0
- package/dist/src/serializer/attribute-serializer.d.ts.map +1 -0
- package/dist/src/serializer/attribute-serializer.js +34 -0
- package/dist/src/serializer/field-serializer.d.ts +112 -0
- package/dist/src/serializer/field-serializer.d.ts.map +1 -0
- package/dist/src/serializer/field-serializer.js +212 -0
- package/dist/src/serializer/media-serializer.d.ts +31 -0
- package/dist/src/serializer/media-serializer.d.ts.map +1 -0
- package/dist/src/serializer/media-serializer.js +83 -0
- package/dist/src/serializer/session-serializer.d.ts +29 -0
- package/dist/src/serializer/session-serializer.d.ts.map +1 -0
- package/dist/src/serializer/session-serializer.js +99 -0
- package/dist/src/serializer/time-serializer.d.ts +46 -0
- package/dist/src/serializer/time-serializer.d.ts.map +1 -0
- package/dist/src/serializer/time-serializer.js +86 -0
- package/dist/src/types/attributes.d.ts +318 -0
- package/dist/src/types/attributes.d.ts.map +1 -0
- package/dist/src/types/attributes.js +225 -0
- package/dist/src/types/errors.d.ts +129 -0
- package/dist/src/types/errors.d.ts.map +1 -0
- package/dist/src/types/errors.js +186 -0
- package/dist/src/types/fields.d.ts +100 -0
- package/dist/src/types/fields.d.ts.map +1 -0
- package/dist/src/types/fields.js +48 -0
- package/dist/src/types/media.d.ts +148 -0
- package/dist/src/types/media.d.ts.map +1 -0
- package/dist/src/types/media.js +137 -0
- package/dist/src/types/network.d.ts +136 -0
- package/dist/src/types/network.d.ts.map +1 -0
- package/dist/src/types/network.js +130 -0
- package/dist/src/types/primitives.d.ts +193 -0
- package/dist/src/types/primitives.d.ts.map +1 -0
- package/dist/src/types/primitives.js +195 -0
- package/dist/src/types/session.d.ts +122 -0
- package/dist/src/types/session.d.ts.map +1 -0
- package/dist/src/types/session.js +81 -0
- package/dist/src/types/time.d.ts +129 -0
- package/dist/src/types/time.d.ts.map +1 -0
- package/dist/src/types/time.js +84 -0
- package/dist/src/utils/address-parser.d.ts +100 -0
- package/dist/src/utils/address-parser.d.ts.map +1 -0
- package/dist/src/utils/address-parser.js +338 -0
- package/dist/src/utils/format-validators.d.ts +77 -0
- package/dist/src/utils/format-validators.d.ts.map +1 -0
- package/dist/src/utils/format-validators.js +504 -0
- package/dist/src/utils/line-reader.d.ts +84 -0
- package/dist/src/utils/line-reader.d.ts.map +1 -0
- package/dist/src/utils/line-reader.js +169 -0
- package/dist/src/utils/time-converter.d.ts +99 -0
- package/dist/src/utils/time-converter.d.ts.map +1 -0
- package/dist/src/utils/time-converter.js +195 -0
- package/dist/src/validator/media-validator.d.ts +27 -0
- package/dist/src/validator/media-validator.d.ts.map +1 -0
- package/dist/src/validator/media-validator.js +241 -0
- package/dist/src/validator/semantic-validator.d.ts +47 -0
- package/dist/src/validator/semantic-validator.d.ts.map +1 -0
- package/dist/src/validator/semantic-validator.js +207 -0
- package/dist/src/validator/session-validator.d.ts +36 -0
- package/dist/src/validator/session-validator.d.ts.map +1 -0
- package/dist/src/validator/session-validator.js +280 -0
- package/dist/tests/integration/round-trip.test.d.ts +5 -0
- package/dist/tests/integration/round-trip.test.d.ts.map +1 -0
- package/dist/tests/integration/round-trip.test.js +320 -0
- package/dist/tests/integration/voip-examples.test.d.ts +5 -0
- package/dist/tests/integration/voip-examples.test.d.ts.map +1 -0
- package/dist/tests/integration/voip-examples.test.js +361 -0
- package/dist/tests/unit/builder/media-builder.test.d.ts +5 -0
- package/dist/tests/unit/builder/media-builder.test.d.ts.map +1 -0
- package/dist/tests/unit/builder/media-builder.test.js +524 -0
- package/dist/tests/unit/builder/session-builder.test.d.ts +5 -0
- package/dist/tests/unit/builder/session-builder.test.d.ts.map +1 -0
- package/dist/tests/unit/builder/session-builder.test.js +367 -0
- package/dist/tests/unit/parser/attribute-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/attribute-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/attribute-parser.test.js +319 -0
- package/dist/tests/unit/parser/field-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/field-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/field-parser.test.js +355 -0
- package/dist/tests/unit/parser/media-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/media-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/media-parser.test.js +241 -0
- package/dist/tests/unit/parser/primitive-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/primitive-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/primitive-parser.test.js +261 -0
- package/dist/tests/unit/parser/scanner.test.d.ts +5 -0
- package/dist/tests/unit/parser/scanner.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/scanner.test.js +241 -0
- package/dist/tests/unit/parser/session-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/session-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/session-parser.test.js +346 -0
- package/dist/tests/unit/parser/time-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/time-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/time-parser.test.js +173 -0
- package/dist/tests/unit/serializer/attribute-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/attribute-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/attribute-serializer.test.js +78 -0
- package/dist/tests/unit/serializer/field-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/field-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/field-serializer.test.js +159 -0
- package/dist/tests/unit/serializer/media-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/media-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/media-serializer.test.js +155 -0
- package/dist/tests/unit/serializer/session-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/session-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/session-serializer.test.js +317 -0
- package/dist/tests/unit/serializer/time-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/time-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/time-serializer.test.js +115 -0
- package/dist/tests/unit/types/errors.test.d.ts +5 -0
- package/dist/tests/unit/types/errors.test.d.ts.map +1 -0
- package/dist/tests/unit/types/errors.test.js +127 -0
- package/dist/tests/unit/types/network.test.d.ts +5 -0
- package/dist/tests/unit/types/network.test.d.ts.map +1 -0
- package/dist/tests/unit/types/network.test.js +132 -0
- package/dist/tests/unit/types/primitives.test.d.ts +5 -0
- package/dist/tests/unit/types/primitives.test.d.ts.map +1 -0
- package/dist/tests/unit/types/primitives.test.js +108 -0
- package/dist/tests/unit/utils/address-parser.test.d.ts +5 -0
- package/dist/tests/unit/utils/address-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/address-parser.test.js +203 -0
- package/dist/tests/unit/utils/format-validators.test.d.ts +5 -0
- package/dist/tests/unit/utils/format-validators.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/format-validators.test.js +224 -0
- package/dist/tests/unit/utils/line-reader.test.d.ts +5 -0
- package/dist/tests/unit/utils/line-reader.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/line-reader.test.js +157 -0
- package/dist/tests/unit/utils/time-converter.test.d.ts +5 -0
- package/dist/tests/unit/utils/time-converter.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/time-converter.test.js +190 -0
- package/dist/tests/unit/validator/media-validator.test.d.ts +5 -0
- package/dist/tests/unit/validator/media-validator.test.d.ts.map +1 -0
- package/dist/tests/unit/validator/media-validator.test.js +313 -0
- package/dist/tests/unit/validator/semantic-validator.test.d.ts +5 -0
- package/dist/tests/unit/validator/semantic-validator.test.d.ts.map +1 -0
- package/dist/tests/unit/validator/semantic-validator.test.js +262 -0
- package/dist/tests/unit/validator/session-validator.test.d.ts +5 -0
- package/dist/tests/unit/validator/session-validator.test.d.ts.map +1 -0
- package/dist/tests/unit/validator/session-validator.test.js +447 -0
- package/package.json +50 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for media-validator.ts
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const media_validator_1 = require("../../../src/validator/media-validator");
|
|
7
|
+
const media_1 = require("../../../src/types/media");
|
|
8
|
+
const fields_1 = require("../../../src/types/fields");
|
|
9
|
+
const network_1 = require("../../../src/types/network");
|
|
10
|
+
const attributes_1 = require("../../../src/types/attributes");
|
|
11
|
+
describe('media-validator', () => {
|
|
12
|
+
describe('validateMediaDescription', () => {
|
|
13
|
+
describe('valid media descriptions', () => {
|
|
14
|
+
it('should validate minimal media description with session connection', () => {
|
|
15
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']));
|
|
16
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
17
|
+
expect(result.valid).toBe(true);
|
|
18
|
+
expect(result.errors).toHaveLength(0);
|
|
19
|
+
});
|
|
20
|
+
it('should validate media description with own connection', () => {
|
|
21
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
22
|
+
connections: [(0, fields_1.createConnection)('IN', 'IP4', (0, network_1.createIP4Address)('192.168.1.100'))],
|
|
23
|
+
});
|
|
24
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, false);
|
|
25
|
+
expect(result.valid).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
it('should validate media with port range', () => {
|
|
28
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createPortRange)(49170, 2), 'RTP/AVP', ['0']));
|
|
29
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
30
|
+
expect(result.valid).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
it('should validate media with single direction attribute', () => {
|
|
33
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
34
|
+
attributes: [(0, attributes_1.createSendrecv)()],
|
|
35
|
+
});
|
|
36
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
37
|
+
expect(result.valid).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
it('should validate media with valid rtpmap', () => {
|
|
40
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0', '8']), {
|
|
41
|
+
attributes: [
|
|
42
|
+
(0, attributes_1.createRtpmap)(0, 'PCMU', 8000),
|
|
43
|
+
(0, attributes_1.createRtpmap)(8, 'PCMA', 8000),
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
47
|
+
expect(result.valid).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
it('should validate media with valid fmtp', () => {
|
|
50
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('video', (0, media_1.createSimplePort)(51372), 'RTP/AVP', ['96']), {
|
|
51
|
+
attributes: [
|
|
52
|
+
(0, attributes_1.createRtpmap)(96, 'H264', 90000),
|
|
53
|
+
(0, attributes_1.createFmtp)('96', 'profile-level-id=42e01f'),
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
57
|
+
expect(result.valid).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
it('should validate media with valid ptime', () => {
|
|
60
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
61
|
+
attributes: [(0, attributes_1.createPtime)(20)],
|
|
62
|
+
});
|
|
63
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
64
|
+
expect(result.valid).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
it('should validate port 0 (disabled)', () => {
|
|
67
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(0), 'RTP/AVP', ['0']));
|
|
68
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
69
|
+
expect(result.valid).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('invalid media descriptions', () => {
|
|
73
|
+
it('should reject missing connection when no session connection', () => {
|
|
74
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']));
|
|
75
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, false);
|
|
76
|
+
expect(result.valid).toBe(false);
|
|
77
|
+
expect(result.errors.some((e) => e.constraint === 'RFC8866:5.7')).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
it('should reject port out of range', () => {
|
|
80
|
+
const md = {
|
|
81
|
+
media: {
|
|
82
|
+
type: 'audio',
|
|
83
|
+
port: { kind: 'simple', value: 70000 },
|
|
84
|
+
proto: 'RTP/AVP',
|
|
85
|
+
formats: ['0'],
|
|
86
|
+
},
|
|
87
|
+
connections: [],
|
|
88
|
+
bandwidths: [],
|
|
89
|
+
attributes: [],
|
|
90
|
+
};
|
|
91
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
92
|
+
expect(result.valid).toBe(false);
|
|
93
|
+
expect(result.errors.some((e) => { var _a; return (_a = e.field) === null || _a === void 0 ? void 0 : _a.includes('port'); })).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
it('should reject port range exceeding 65535', () => {
|
|
96
|
+
const md = {
|
|
97
|
+
media: {
|
|
98
|
+
type: 'audio',
|
|
99
|
+
port: { kind: 'range', base: 65530, count: 10 },
|
|
100
|
+
proto: 'RTP/AVP',
|
|
101
|
+
formats: ['0'],
|
|
102
|
+
},
|
|
103
|
+
connections: [],
|
|
104
|
+
bandwidths: [],
|
|
105
|
+
attributes: [],
|
|
106
|
+
};
|
|
107
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
108
|
+
expect(result.valid).toBe(false);
|
|
109
|
+
expect(result.errors.some((e) => e.message.includes('exceeds 65535'))).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
it('should reject multiple direction attributes', () => {
|
|
112
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
113
|
+
attributes: [(0, attributes_1.createRecvonly)(), (0, attributes_1.createSendrecv)()],
|
|
114
|
+
});
|
|
115
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
116
|
+
expect(result.valid).toBe(false);
|
|
117
|
+
expect(result.errors.some((e) => e.constraint === 'RFC8866:6.7')).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
it('should reject rtpmap payload type not in format list', () => {
|
|
120
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
121
|
+
attributes: [(0, attributes_1.createRtpmap)(96, 'H264', 90000)], // 96 not in formats
|
|
122
|
+
});
|
|
123
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
124
|
+
expect(result.valid).toBe(false);
|
|
125
|
+
expect(result.errors.some((e) => e.constraint === 'RFC8866:6.6')).toBe(true);
|
|
126
|
+
});
|
|
127
|
+
it('should reject fmtp format not in format list', () => {
|
|
128
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
129
|
+
attributes: [(0, attributes_1.createFmtp)('96', 'profile-level-id=42e01f')], // 96 not in formats
|
|
130
|
+
});
|
|
131
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
132
|
+
expect(result.valid).toBe(false);
|
|
133
|
+
expect(result.errors.some((e) => e.constraint === 'RFC8866:6.15')).toBe(true);
|
|
134
|
+
});
|
|
135
|
+
it('should reject invalid ptime (zero)', () => {
|
|
136
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
137
|
+
attributes: [(0, attributes_1.createValueAttribute)('ptime', '0')],
|
|
138
|
+
});
|
|
139
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
140
|
+
expect(result.valid).toBe(false);
|
|
141
|
+
expect(result.errors.some((e) => e.constraint === 'RFC8866:6.4')).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
it('should reject invalid maxptime (negative)', () => {
|
|
144
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
145
|
+
attributes: [(0, attributes_1.createValueAttribute)('maxptime', '-10')],
|
|
146
|
+
});
|
|
147
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
148
|
+
expect(result.valid).toBe(false);
|
|
149
|
+
expect(result.errors.some((e) => e.constraint === 'RFC8866:6.5')).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
it('should reject invalid quality (out of range)', () => {
|
|
152
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('video', (0, media_1.createSimplePort)(51372), 'RTP/AVP', ['96']), {
|
|
153
|
+
attributes: [(0, attributes_1.createValueAttribute)('quality', '11')],
|
|
154
|
+
});
|
|
155
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
156
|
+
expect(result.valid).toBe(false);
|
|
157
|
+
expect(result.errors.some((e) => e.constraint === 'RFC8866:6.14')).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
it('should reject invalid framerate (zero)', () => {
|
|
160
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('video', (0, media_1.createSimplePort)(51372), 'RTP/AVP', ['96']), {
|
|
161
|
+
attributes: [(0, attributes_1.createValueAttribute)('framerate', '0')],
|
|
162
|
+
});
|
|
163
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
164
|
+
expect(result.valid).toBe(false);
|
|
165
|
+
expect(result.errors.some((e) => e.constraint === 'RFC8866:6.13')).toBe(true);
|
|
166
|
+
});
|
|
167
|
+
it('should reject negative bandwidth', () => {
|
|
168
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
169
|
+
bandwidths: [{ type: 'AS', value: -1 }],
|
|
170
|
+
});
|
|
171
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
172
|
+
expect(result.valid).toBe(false);
|
|
173
|
+
expect(result.errors.some((e) => { var _a; return (_a = e.field) === null || _a === void 0 ? void 0 : _a.includes('bandwidth'); })).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
it('should reject session-only attribute cat at media level (RFC 8866 Section 6.1)', () => {
|
|
176
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
177
|
+
attributes: [(0, attributes_1.createValueAttribute)('cat', 'conference')],
|
|
178
|
+
});
|
|
179
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
180
|
+
expect(result.valid).toBe(false);
|
|
181
|
+
expect(result.errors.some((e) => e.message.includes('cat') && e.message.includes('session level'))).toBe(true);
|
|
182
|
+
});
|
|
183
|
+
it('should reject session-only attribute tool at media level (RFC 8866 Section 6.3)', () => {
|
|
184
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
185
|
+
attributes: [(0, attributes_1.createValueAttribute)('tool', 'my-tool v1.0')],
|
|
186
|
+
});
|
|
187
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
188
|
+
expect(result.valid).toBe(false);
|
|
189
|
+
expect(result.errors.some((e) => e.message.includes('tool') && e.message.includes('session level'))).toBe(true);
|
|
190
|
+
});
|
|
191
|
+
it('should reject session-only attribute type at media level (RFC 8866 Section 6.9)', () => {
|
|
192
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
193
|
+
attributes: [(0, attributes_1.createValueAttribute)('type', 'broadcast')],
|
|
194
|
+
});
|
|
195
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
196
|
+
expect(result.valid).toBe(false);
|
|
197
|
+
expect(result.errors.some((e) => e.message.includes('type') && e.message.includes('session level'))).toBe(true);
|
|
198
|
+
});
|
|
199
|
+
it('should reject all session-only attributes at media level', () => {
|
|
200
|
+
// Test all session-only attributes: cat, keywds, tool, type, charset
|
|
201
|
+
const sessionOnlyAttrs = ['cat', 'keywds', 'tool', 'type', 'charset'];
|
|
202
|
+
for (const attrName of sessionOnlyAttrs) {
|
|
203
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
204
|
+
attributes: [(0, attributes_1.createValueAttribute)(attrName, 'test')],
|
|
205
|
+
});
|
|
206
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
207
|
+
expect(result.valid).toBe(false);
|
|
208
|
+
expect(result.errors.some((e) => e.message.includes(attrName) && e.message.includes('session level'))).toBe(true);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
it('should allow both-level attributes at media level', () => {
|
|
212
|
+
// Test both-level attributes: lang, sdplang, recvonly, etc.
|
|
213
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
214
|
+
attributes: [
|
|
215
|
+
(0, attributes_1.createValueAttribute)('lang', 'en'),
|
|
216
|
+
(0, attributes_1.createValueAttribute)('sdplang', 'en'),
|
|
217
|
+
(0, attributes_1.createRecvonly)(),
|
|
218
|
+
],
|
|
219
|
+
});
|
|
220
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
221
|
+
expect(result.valid).toBe(true);
|
|
222
|
+
});
|
|
223
|
+
it('should reject duplicate rtpmap for same payload type (RFC 8866 Section 6.6)', () => {
|
|
224
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['96']), {
|
|
225
|
+
attributes: [
|
|
226
|
+
(0, attributes_1.createRtpmap)(96, 'opus', 48000),
|
|
227
|
+
(0, attributes_1.createRtpmap)(96, 'PCMU', 8000), // Duplicate payload type 96
|
|
228
|
+
],
|
|
229
|
+
});
|
|
230
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
231
|
+
expect(result.valid).toBe(false);
|
|
232
|
+
expect(result.errors.some((e) => e.message.includes('duplicate rtpmap') &&
|
|
233
|
+
e.message.includes('96') &&
|
|
234
|
+
e.constraint === 'RFC8866:6.6')).toBe(true);
|
|
235
|
+
});
|
|
236
|
+
it('should reject duplicate fmtp for same format (RFC 8866 Section 6.15)', () => {
|
|
237
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('video', (0, media_1.createSimplePort)(51372), 'RTP/AVP', ['96']), {
|
|
238
|
+
attributes: [
|
|
239
|
+
(0, attributes_1.createRtpmap)(96, 'H264', 90000),
|
|
240
|
+
(0, attributes_1.createFmtp)('96', 'profile-level-id=42e01f'),
|
|
241
|
+
(0, attributes_1.createFmtp)('96', 'packetization-mode=1'), // Duplicate format 96
|
|
242
|
+
],
|
|
243
|
+
});
|
|
244
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
245
|
+
expect(result.valid).toBe(false);
|
|
246
|
+
expect(result.errors.some((e) => e.message.includes('duplicate fmtp') &&
|
|
247
|
+
e.message.includes('96') &&
|
|
248
|
+
e.constraint === 'RFC8866:6.15')).toBe(true);
|
|
249
|
+
});
|
|
250
|
+
it('should allow different payload types for rtpmap', () => {
|
|
251
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0', '8', '96']), {
|
|
252
|
+
attributes: [
|
|
253
|
+
(0, attributes_1.createRtpmap)(0, 'PCMU', 8000),
|
|
254
|
+
(0, attributes_1.createRtpmap)(8, 'PCMA', 8000),
|
|
255
|
+
(0, attributes_1.createRtpmap)(96, 'opus', 48000),
|
|
256
|
+
],
|
|
257
|
+
});
|
|
258
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
259
|
+
expect(result.valid).toBe(true);
|
|
260
|
+
});
|
|
261
|
+
it('should allow different formats for fmtp', () => {
|
|
262
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('video', (0, media_1.createSimplePort)(51372), 'RTP/AVP', ['96', '97']), {
|
|
263
|
+
attributes: [
|
|
264
|
+
(0, attributes_1.createRtpmap)(96, 'H264', 90000),
|
|
265
|
+
(0, attributes_1.createRtpmap)(97, 'VP8', 90000),
|
|
266
|
+
(0, attributes_1.createFmtp)('96', 'profile-level-id=42e01f'),
|
|
267
|
+
(0, attributes_1.createFmtp)('97', 'max-fr=30'),
|
|
268
|
+
],
|
|
269
|
+
});
|
|
270
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
271
|
+
expect(result.valid).toBe(true);
|
|
272
|
+
});
|
|
273
|
+
it('should detect multiple duplicate rtpmap entries', () => {
|
|
274
|
+
const md = (0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['96', '97']), {
|
|
275
|
+
attributes: [
|
|
276
|
+
(0, attributes_1.createRtpmap)(96, 'opus', 48000),
|
|
277
|
+
(0, attributes_1.createRtpmap)(96, 'PCMU', 8000), // Duplicate 96
|
|
278
|
+
(0, attributes_1.createRtpmap)(97, 'PCMA', 8000),
|
|
279
|
+
(0, attributes_1.createRtpmap)(97, 'G722', 8000), // Duplicate 97
|
|
280
|
+
],
|
|
281
|
+
});
|
|
282
|
+
const result = (0, media_validator_1.validateMediaDescription)(md, 0, true);
|
|
283
|
+
expect(result.valid).toBe(false);
|
|
284
|
+
expect(result.errors.filter((e) => e.message.includes('duplicate rtpmap')).length).toBe(2);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
describe('validateAllMediaDescriptions', () => {
|
|
289
|
+
it('should validate empty media list', () => {
|
|
290
|
+
const result = (0, media_validator_1.validateAllMediaDescriptions)([], true);
|
|
291
|
+
expect(result.valid).toBe(true);
|
|
292
|
+
});
|
|
293
|
+
it('should validate multiple valid media descriptions', () => {
|
|
294
|
+
const mediaDescriptions = [
|
|
295
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0'])),
|
|
296
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('video', (0, media_1.createSimplePort)(51372), 'RTP/AVP', ['96'])),
|
|
297
|
+
];
|
|
298
|
+
const result = (0, media_validator_1.validateAllMediaDescriptions)(mediaDescriptions, true);
|
|
299
|
+
expect(result.valid).toBe(true);
|
|
300
|
+
});
|
|
301
|
+
it('should collect errors from all media descriptions', () => {
|
|
302
|
+
const mediaDescriptions = [
|
|
303
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), { attributes: [(0, attributes_1.createRecvonly)(), (0, attributes_1.createSendrecv)()] } // Multiple direction
|
|
304
|
+
),
|
|
305
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('video', (0, media_1.createSimplePort)(51372), 'RTP/AVP', ['96']), { attributes: [(0, attributes_1.createValueAttribute)('quality', '15')] } // Invalid quality
|
|
306
|
+
),
|
|
307
|
+
];
|
|
308
|
+
const result = (0, media_validator_1.validateAllMediaDescriptions)(mediaDescriptions, true);
|
|
309
|
+
expect(result.valid).toBe(false);
|
|
310
|
+
expect(result.errors.length).toBeGreaterThanOrEqual(2);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic-validator.test.d.ts","sourceRoot":"","sources":["../../../../tests/unit/validator/semantic-validator.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for semantic-validator.ts
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const semantic_validator_1 = require("../../../src/validator/semantic-validator");
|
|
7
|
+
const session_1 = require("../../../src/types/session");
|
|
8
|
+
const fields_1 = require("../../../src/types/fields");
|
|
9
|
+
const time_1 = require("../../../src/types/time");
|
|
10
|
+
const network_1 = require("../../../src/types/network");
|
|
11
|
+
const media_1 = require("../../../src/types/media");
|
|
12
|
+
const attributes_1 = require("../../../src/types/attributes");
|
|
13
|
+
describe('semantic-validator', () => {
|
|
14
|
+
// Helper to create a minimal valid session
|
|
15
|
+
function createValidSession() {
|
|
16
|
+
return (0, session_1.createSessionDescription)({
|
|
17
|
+
version: 0,
|
|
18
|
+
origin: (0, fields_1.createOrigin)('jdoe', '2890844526', '2890842807', 'IN', 'IP4', (0, network_1.createIP4Address)('10.47.16.5')),
|
|
19
|
+
sessionName: 'Test Session',
|
|
20
|
+
timeDescriptions: [(0, time_1.createTimeDescription)((0, time_1.createTiming)(0, 0))],
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
describe('validateSdp', () => {
|
|
24
|
+
it('should validate minimal valid session', () => {
|
|
25
|
+
const session = createValidSession();
|
|
26
|
+
const result = (0, semantic_validator_1.validateSdp)(session);
|
|
27
|
+
expect(result.valid).toBe(true);
|
|
28
|
+
expect(result.errors).toHaveLength(0);
|
|
29
|
+
});
|
|
30
|
+
it('should validate session with media and session-level connection', () => {
|
|
31
|
+
const session = (0, session_1.createSessionDescription)({
|
|
32
|
+
version: 0,
|
|
33
|
+
origin: (0, fields_1.createOrigin)('jdoe', '123', '456', 'IN', 'IP4', (0, network_1.createIP4Address)('10.0.0.1')),
|
|
34
|
+
sessionName: 'Test',
|
|
35
|
+
connection: (0, fields_1.createConnection)('IN', 'IP4', (0, network_1.createIP4Address)('192.168.1.1')),
|
|
36
|
+
timeDescriptions: [(0, time_1.createTimeDescription)((0, time_1.createTiming)(0, 0))],
|
|
37
|
+
mediaDescriptions: [
|
|
38
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0'])),
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
const result = (0, semantic_validator_1.validateSdp)(session);
|
|
42
|
+
expect(result.valid).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
it('should collect errors from session and media validation', () => {
|
|
45
|
+
const session = {
|
|
46
|
+
version: 0,
|
|
47
|
+
origin: (0, fields_1.createOrigin)('jdoe', '123', '456', 'IN', 'IP4', (0, network_1.createIP4Address)('10.0.0.1')),
|
|
48
|
+
sessionName: '', // Invalid - empty
|
|
49
|
+
emails: [],
|
|
50
|
+
phones: [],
|
|
51
|
+
bandwidths: [],
|
|
52
|
+
timeDescriptions: [(0, time_1.createTimeDescription)((0, time_1.createTiming)(0, 0))],
|
|
53
|
+
attributes: [],
|
|
54
|
+
mediaDescriptions: [
|
|
55
|
+
{
|
|
56
|
+
media: {
|
|
57
|
+
type: 'audio',
|
|
58
|
+
port: { kind: 'simple', value: 49170 },
|
|
59
|
+
proto: 'RTP/AVP',
|
|
60
|
+
formats: ['0'],
|
|
61
|
+
},
|
|
62
|
+
connections: [], // Missing connection
|
|
63
|
+
bandwidths: [],
|
|
64
|
+
attributes: [],
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
const result = (0, semantic_validator_1.validateSdp)(session);
|
|
69
|
+
expect(result.valid).toBe(false);
|
|
70
|
+
expect(result.errors.length).toBeGreaterThanOrEqual(2);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('validateSemanticConstraints', () => {
|
|
74
|
+
describe('bandwidth consistency', () => {
|
|
75
|
+
it('should pass when AS sum is within CT', () => {
|
|
76
|
+
const session = (0, session_1.createSessionDescription)({
|
|
77
|
+
version: 0,
|
|
78
|
+
origin: (0, fields_1.createOrigin)('jdoe', '123', '456', 'IN', 'IP4', (0, network_1.createIP4Address)('10.0.0.1')),
|
|
79
|
+
sessionName: 'Test',
|
|
80
|
+
bandwidths: [(0, fields_1.createBandwidth)('CT', 1000)],
|
|
81
|
+
timeDescriptions: [(0, time_1.createTimeDescription)((0, time_1.createTiming)(0, 0))],
|
|
82
|
+
mediaDescriptions: [
|
|
83
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
84
|
+
connections: [(0, fields_1.createConnection)('IN', 'IP4', (0, network_1.createIP4Address)('192.168.1.1'))],
|
|
85
|
+
bandwidths: [(0, fields_1.createBandwidth)('AS', 500)],
|
|
86
|
+
}),
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
const result = (0, semantic_validator_1.validateSemanticConstraints)(session);
|
|
90
|
+
expect(result.valid).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
it('should fail when AS sum exceeds CT', () => {
|
|
93
|
+
const session = (0, session_1.createSessionDescription)({
|
|
94
|
+
version: 0,
|
|
95
|
+
origin: (0, fields_1.createOrigin)('jdoe', '123', '456', 'IN', 'IP4', (0, network_1.createIP4Address)('10.0.0.1')),
|
|
96
|
+
sessionName: 'Test',
|
|
97
|
+
bandwidths: [(0, fields_1.createBandwidth)('CT', 100)],
|
|
98
|
+
timeDescriptions: [(0, time_1.createTimeDescription)((0, time_1.createTiming)(0, 0))],
|
|
99
|
+
mediaDescriptions: [
|
|
100
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
101
|
+
connections: [(0, fields_1.createConnection)('IN', 'IP4', (0, network_1.createIP4Address)('192.168.1.1'))],
|
|
102
|
+
bandwidths: [(0, fields_1.createBandwidth)('AS', 500)],
|
|
103
|
+
}),
|
|
104
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('video', (0, media_1.createSimplePort)(51372), 'RTP/AVP', ['96']), {
|
|
105
|
+
connections: [(0, fields_1.createConnection)('IN', 'IP4', (0, network_1.createIP4Address)('192.168.1.1'))],
|
|
106
|
+
bandwidths: [(0, fields_1.createBandwidth)('AS', 700)],
|
|
107
|
+
}),
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
const result = (0, semantic_validator_1.validateSemanticConstraints)(session);
|
|
111
|
+
expect(result.valid).toBe(false);
|
|
112
|
+
expect(result.errors.some((e) => e.message.includes('AS') && e.message.includes('CT'))).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
describe('ptime/maxptime consistency', () => {
|
|
116
|
+
it('should pass when ptime <= maxptime', () => {
|
|
117
|
+
const session = (0, session_1.createSessionDescription)({
|
|
118
|
+
version: 0,
|
|
119
|
+
origin: (0, fields_1.createOrigin)('jdoe', '123', '456', 'IN', 'IP4', (0, network_1.createIP4Address)('10.0.0.1')),
|
|
120
|
+
sessionName: 'Test',
|
|
121
|
+
connection: (0, fields_1.createConnection)('IN', 'IP4', (0, network_1.createIP4Address)('192.168.1.1')),
|
|
122
|
+
timeDescriptions: [(0, time_1.createTimeDescription)((0, time_1.createTiming)(0, 0))],
|
|
123
|
+
mediaDescriptions: [
|
|
124
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
125
|
+
attributes: [
|
|
126
|
+
(0, attributes_1.createValueAttribute)('ptime', '20'),
|
|
127
|
+
(0, attributes_1.createValueAttribute)('maxptime', '60'),
|
|
128
|
+
],
|
|
129
|
+
}),
|
|
130
|
+
],
|
|
131
|
+
});
|
|
132
|
+
const result = (0, semantic_validator_1.validateSemanticConstraints)(session);
|
|
133
|
+
expect(result.valid).toBe(true);
|
|
134
|
+
});
|
|
135
|
+
it('should pass when decimal ptime <= maxptime (RFC 8866 non-zero-int-or-real)', () => {
|
|
136
|
+
const session = (0, session_1.createSessionDescription)({
|
|
137
|
+
version: 0,
|
|
138
|
+
origin: (0, fields_1.createOrigin)('jdoe', '123', '456', 'IN', 'IP4', (0, network_1.createIP4Address)('10.0.0.1')),
|
|
139
|
+
sessionName: 'Test',
|
|
140
|
+
connection: (0, fields_1.createConnection)('IN', 'IP4', (0, network_1.createIP4Address)('192.168.1.1')),
|
|
141
|
+
timeDescriptions: [(0, time_1.createTimeDescription)((0, time_1.createTiming)(0, 0))],
|
|
142
|
+
mediaDescriptions: [
|
|
143
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
144
|
+
attributes: [
|
|
145
|
+
(0, attributes_1.createValueAttribute)('ptime', '20.5'),
|
|
146
|
+
(0, attributes_1.createValueAttribute)('maxptime', '60.5'),
|
|
147
|
+
],
|
|
148
|
+
}),
|
|
149
|
+
],
|
|
150
|
+
});
|
|
151
|
+
const result = (0, semantic_validator_1.validateSemanticConstraints)(session);
|
|
152
|
+
expect(result.valid).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
it('should fail when ptime > maxptime', () => {
|
|
155
|
+
const session = (0, session_1.createSessionDescription)({
|
|
156
|
+
version: 0,
|
|
157
|
+
origin: (0, fields_1.createOrigin)('jdoe', '123', '456', 'IN', 'IP4', (0, network_1.createIP4Address)('10.0.0.1')),
|
|
158
|
+
sessionName: 'Test',
|
|
159
|
+
connection: (0, fields_1.createConnection)('IN', 'IP4', (0, network_1.createIP4Address)('192.168.1.1')),
|
|
160
|
+
timeDescriptions: [(0, time_1.createTimeDescription)((0, time_1.createTiming)(0, 0))],
|
|
161
|
+
mediaDescriptions: [
|
|
162
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
163
|
+
attributes: [
|
|
164
|
+
(0, attributes_1.createValueAttribute)('ptime', '100'),
|
|
165
|
+
(0, attributes_1.createValueAttribute)('maxptime', '60'),
|
|
166
|
+
],
|
|
167
|
+
}),
|
|
168
|
+
],
|
|
169
|
+
});
|
|
170
|
+
const result = (0, semantic_validator_1.validateSemanticConstraints)(session);
|
|
171
|
+
expect(result.valid).toBe(false);
|
|
172
|
+
expect(result.errors.some((e) => e.message.includes('ptime') && e.message.includes('maxptime'))).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
it('should fail when decimal ptime > maxptime', () => {
|
|
175
|
+
const session = (0, session_1.createSessionDescription)({
|
|
176
|
+
version: 0,
|
|
177
|
+
origin: (0, fields_1.createOrigin)('jdoe', '123', '456', 'IN', 'IP4', (0, network_1.createIP4Address)('10.0.0.1')),
|
|
178
|
+
sessionName: 'Test',
|
|
179
|
+
connection: (0, fields_1.createConnection)('IN', 'IP4', (0, network_1.createIP4Address)('192.168.1.1')),
|
|
180
|
+
timeDescriptions: [(0, time_1.createTimeDescription)((0, time_1.createTiming)(0, 0))],
|
|
181
|
+
mediaDescriptions: [
|
|
182
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
183
|
+
attributes: [
|
|
184
|
+
(0, attributes_1.createValueAttribute)('ptime', '60.5'),
|
|
185
|
+
(0, attributes_1.createValueAttribute)('maxptime', '60.0'),
|
|
186
|
+
],
|
|
187
|
+
}),
|
|
188
|
+
],
|
|
189
|
+
});
|
|
190
|
+
const result = (0, semantic_validator_1.validateSemanticConstraints)(session);
|
|
191
|
+
expect(result.valid).toBe(false);
|
|
192
|
+
expect(result.errors.some((e) => e.message.includes('ptime') && e.message.includes('maxptime'))).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
describe('time description consistency', () => {
|
|
196
|
+
it('should fail when timezone specified without repeat times', () => {
|
|
197
|
+
const session = {
|
|
198
|
+
version: 0,
|
|
199
|
+
origin: (0, fields_1.createOrigin)('jdoe', '123', '456', 'IN', 'IP4', (0, network_1.createIP4Address)('10.0.0.1')),
|
|
200
|
+
sessionName: 'Test',
|
|
201
|
+
emails: [],
|
|
202
|
+
phones: [],
|
|
203
|
+
bandwidths: [],
|
|
204
|
+
timeDescriptions: [
|
|
205
|
+
{
|
|
206
|
+
timing: { startTime: 2873397496, stopTime: 2873404696 },
|
|
207
|
+
repeats: [],
|
|
208
|
+
timezone: { adjustments: [{ time: 2882844526, offset: { value: -3600, unit: undefined } }] },
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
attributes: [],
|
|
212
|
+
mediaDescriptions: [],
|
|
213
|
+
};
|
|
214
|
+
const result = (0, semantic_validator_1.validateSemanticConstraints)(session);
|
|
215
|
+
expect(result.valid).toBe(false);
|
|
216
|
+
expect(result.errors.some((e) => e.message.includes('timezone') && e.message.includes('repeat'))).toBe(true);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
describe('validateSdp with options', () => {
|
|
221
|
+
it('should validate with default options', () => {
|
|
222
|
+
const session = createValidSession();
|
|
223
|
+
const result = (0, semantic_validator_1.validateSdp)(session);
|
|
224
|
+
expect(result.valid).toBe(true);
|
|
225
|
+
});
|
|
226
|
+
it('should reject deprecated key field when not allowed', () => {
|
|
227
|
+
const session = Object.assign(Object.assign({}, createValidSession()), { key: 'clear:testkey' });
|
|
228
|
+
const result = (0, semantic_validator_1.validateSdp)(session, { allowDeprecatedKeyField: false });
|
|
229
|
+
expect(result.valid).toBe(false);
|
|
230
|
+
expect(result.errors.some((e) => e.message.includes('deprecated'))).toBe(true);
|
|
231
|
+
});
|
|
232
|
+
it('should allow deprecated key field when explicitly permitted', () => {
|
|
233
|
+
const session = Object.assign(Object.assign({}, createValidSession()), { key: 'clear:testkey' });
|
|
234
|
+
const result = (0, semantic_validator_1.validateSdp)(session, { allowDeprecatedKeyField: true });
|
|
235
|
+
expect(result.valid).toBe(true);
|
|
236
|
+
});
|
|
237
|
+
it('should require session connection when option set', () => {
|
|
238
|
+
const session = createValidSession();
|
|
239
|
+
const result = (0, semantic_validator_1.validateSdp)(session, { requireSessionConnection: true });
|
|
240
|
+
expect(result.valid).toBe(false);
|
|
241
|
+
expect(result.errors.some((e) => e.message.includes('Session-level connection'))).toBe(true);
|
|
242
|
+
});
|
|
243
|
+
it('should skip semantic validation when option set', () => {
|
|
244
|
+
const session = (0, session_1.createSessionDescription)({
|
|
245
|
+
version: 0,
|
|
246
|
+
origin: (0, fields_1.createOrigin)('jdoe', '123', '456', 'IN', 'IP4', (0, network_1.createIP4Address)('10.0.0.1')),
|
|
247
|
+
sessionName: 'Test',
|
|
248
|
+
bandwidths: [(0, fields_1.createBandwidth)('CT', 100)],
|
|
249
|
+
timeDescriptions: [(0, time_1.createTimeDescription)((0, time_1.createTiming)(0, 0))],
|
|
250
|
+
mediaDescriptions: [
|
|
251
|
+
(0, media_1.createMediaDescription)((0, media_1.createMedia)('audio', (0, media_1.createSimplePort)(49170), 'RTP/AVP', ['0']), {
|
|
252
|
+
connections: [(0, fields_1.createConnection)('IN', 'IP4', (0, network_1.createIP4Address)('192.168.1.1'))],
|
|
253
|
+
bandwidths: [(0, fields_1.createBandwidth)('AS', 500)], // Would normally exceed CT
|
|
254
|
+
}),
|
|
255
|
+
],
|
|
256
|
+
});
|
|
257
|
+
const result = (0, semantic_validator_1.validateSdp)(session, { skipSemanticValidation: true });
|
|
258
|
+
// Should pass because semantic validation is skipped
|
|
259
|
+
expect(result.valid).toBe(true);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-validator.test.d.ts","sourceRoot":"","sources":["../../../../tests/unit/validator/session-validator.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|