@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,280 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session validators for SDP (RFC 8866)
|
|
4
|
+
*
|
|
5
|
+
* Validates session-level fields and constraints per RFC 8866.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.validResult = validResult;
|
|
9
|
+
exports.invalidResult = invalidResult;
|
|
10
|
+
exports.validateSessionDescription = validateSessionDescription;
|
|
11
|
+
const errors_1 = require("../types/errors");
|
|
12
|
+
const primitives_1 = require("../types/primitives");
|
|
13
|
+
const attributes_1 = require("../types/attributes");
|
|
14
|
+
const format_validators_1 = require("../utils/format-validators");
|
|
15
|
+
/**
|
|
16
|
+
* Create a valid result
|
|
17
|
+
*/
|
|
18
|
+
function validResult() {
|
|
19
|
+
return { valid: true, errors: [] };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create an invalid result with errors
|
|
23
|
+
*/
|
|
24
|
+
function invalidResult(errors) {
|
|
25
|
+
return { valid: false, errors };
|
|
26
|
+
}
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Session Validator
|
|
29
|
+
// ============================================================================
|
|
30
|
+
/**
|
|
31
|
+
* Validate a complete session description
|
|
32
|
+
*
|
|
33
|
+
* Checks RFC 8866 Section 5 requirements:
|
|
34
|
+
* - Required fields are present (v, o, s, t)
|
|
35
|
+
* - Version is 0
|
|
36
|
+
* - At least one time description
|
|
37
|
+
* - Connection coverage (session or all media)
|
|
38
|
+
*
|
|
39
|
+
* @param session - Session description to validate
|
|
40
|
+
* @returns ValidationResult with any errors found
|
|
41
|
+
*/
|
|
42
|
+
function validateSessionDescription(session) {
|
|
43
|
+
const errors = [];
|
|
44
|
+
// 1. Validate version (RFC 8866 Section 5.1)
|
|
45
|
+
validateVersion(session.version, errors);
|
|
46
|
+
// 2. Validate origin (RFC 8866 Section 5.2)
|
|
47
|
+
validateOrigin(session, errors);
|
|
48
|
+
// 3. Validate session name (RFC 8866 Section 5.3)
|
|
49
|
+
validateSessionName(session, errors);
|
|
50
|
+
// 4. Validate time descriptions (RFC 8866 Section 5.9)
|
|
51
|
+
validateTimeDescriptions(session, errors);
|
|
52
|
+
// 5. Validate connection coverage (RFC 8866 Section 5.7)
|
|
53
|
+
validateConnectionCoverage(session, errors);
|
|
54
|
+
// 6. Validate bandwidths (RFC 8866 Section 5.8)
|
|
55
|
+
validateBandwidths(session, errors);
|
|
56
|
+
// 7. Validate attributes (RFC 8866 Section 6)
|
|
57
|
+
validateSessionAttributes(session, errors);
|
|
58
|
+
// 8. Validate email format (RFC 8866 Section 5.6)
|
|
59
|
+
validateEmails(session, errors);
|
|
60
|
+
// 9. Validate phone format (RFC 8866 Section 5.6)
|
|
61
|
+
validatePhones(session, errors);
|
|
62
|
+
// 10. Validate URI format (RFC 8866 Section 5.5)
|
|
63
|
+
validateUri(session, errors);
|
|
64
|
+
return errors.length === 0 ? validResult() : invalidResult(errors);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Validate version field
|
|
68
|
+
*
|
|
69
|
+
* RFC 8866 Section 5.1: Version MUST be 0
|
|
70
|
+
*/
|
|
71
|
+
function validateVersion(version, errors) {
|
|
72
|
+
if (version !== 0) {
|
|
73
|
+
errors.push(errors_1.ValidationError.invalidValue('version', version, 'SDP version must be 0', 'RFC8866:5.1'));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Validate origin field
|
|
78
|
+
*
|
|
79
|
+
* RFC 8866 Section 5.2: Origin is required
|
|
80
|
+
* RFC 8866 Section 9 (Grammar): sess-id = 1*DIGIT, sess-version = 1*DIGIT
|
|
81
|
+
*/
|
|
82
|
+
function validateOrigin(session, errors) {
|
|
83
|
+
if (!session.origin) {
|
|
84
|
+
errors.push(errors_1.ValidationError.missingField('origin', 'RFC8866:5.2'));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Validate username is not empty
|
|
88
|
+
if (!session.origin.username || session.origin.username.length === 0) {
|
|
89
|
+
errors.push(errors_1.ValidationError.invalidValue('origin.username', session.origin.username, 'Username cannot be empty', 'RFC8866:5.2'));
|
|
90
|
+
}
|
|
91
|
+
// Validate session ID is not empty and contains only digits
|
|
92
|
+
if (!session.origin.sessId || session.origin.sessId.length === 0) {
|
|
93
|
+
errors.push(errors_1.ValidationError.invalidValue('origin.sessId', session.origin.sessId, 'Session ID cannot be empty', 'RFC8866:5.2'));
|
|
94
|
+
}
|
|
95
|
+
else if (!(0, primitives_1.isNumericString)(session.origin.sessId)) {
|
|
96
|
+
errors.push(errors_1.ValidationError.invalidValue('origin.sessId', session.origin.sessId, 'Session ID must contain only digits (0-9)', 'RFC8866:5.2'));
|
|
97
|
+
}
|
|
98
|
+
// Validate session version is not empty and contains only digits
|
|
99
|
+
if (!session.origin.sessVersion || session.origin.sessVersion.length === 0) {
|
|
100
|
+
errors.push(errors_1.ValidationError.invalidValue('origin.sessVersion', session.origin.sessVersion, 'Session version cannot be empty', 'RFC8866:5.2'));
|
|
101
|
+
}
|
|
102
|
+
else if (!(0, primitives_1.isNumericString)(session.origin.sessVersion)) {
|
|
103
|
+
errors.push(errors_1.ValidationError.invalidValue('origin.sessVersion', session.origin.sessVersion, 'Session version must contain only digits (0-9)', 'RFC8866:5.2'));
|
|
104
|
+
}
|
|
105
|
+
// Validate network type
|
|
106
|
+
if (!session.origin.netType || session.origin.netType.length === 0) {
|
|
107
|
+
errors.push(errors_1.ValidationError.invalidValue('origin.netType', session.origin.netType, 'Network type cannot be empty', 'RFC8866:5.2'));
|
|
108
|
+
}
|
|
109
|
+
// Validate address type
|
|
110
|
+
if (!session.origin.addrType || session.origin.addrType.length === 0) {
|
|
111
|
+
errors.push(errors_1.ValidationError.invalidValue('origin.addrType', session.origin.addrType, 'Address type cannot be empty', 'RFC8866:5.2'));
|
|
112
|
+
}
|
|
113
|
+
// Validate unicast address
|
|
114
|
+
if (!session.origin.unicastAddress || session.origin.unicastAddress.length === 0) {
|
|
115
|
+
errors.push(errors_1.ValidationError.invalidValue('origin.unicastAddress', session.origin.unicastAddress, 'Unicast address cannot be empty', 'RFC8866:5.2'));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Validate session name field
|
|
120
|
+
*
|
|
121
|
+
* RFC 8866 Section 5.3: Session name is required and cannot be empty
|
|
122
|
+
* (use " " if no meaningful name)
|
|
123
|
+
*/
|
|
124
|
+
function validateSessionName(session, errors) {
|
|
125
|
+
if (session.sessionName === undefined || session.sessionName === null) {
|
|
126
|
+
errors.push(errors_1.ValidationError.missingField('sessionName', 'RFC8866:5.3'));
|
|
127
|
+
}
|
|
128
|
+
else if (session.sessionName.length === 0) {
|
|
129
|
+
errors.push(errors_1.ValidationError.invalidValue('sessionName', session.sessionName, 'Session name cannot be empty (use " " if no meaningful name)', 'RFC8866:5.3'));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Validate time descriptions
|
|
134
|
+
*
|
|
135
|
+
* RFC 8866 Section 5.9: At least one time description is required
|
|
136
|
+
*/
|
|
137
|
+
function validateTimeDescriptions(session, errors) {
|
|
138
|
+
if (!session.timeDescriptions || session.timeDescriptions.length === 0) {
|
|
139
|
+
errors.push(errors_1.ValidationError.constraintViolation('At least one time description (t=) is required', 'RFC8866:5.9'));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// Validate each time description
|
|
143
|
+
for (let i = 0; i < session.timeDescriptions.length; i++) {
|
|
144
|
+
const td = session.timeDescriptions[i];
|
|
145
|
+
// Validate timing
|
|
146
|
+
if (!td.timing) {
|
|
147
|
+
errors.push(errors_1.ValidationError.missingField(`timeDescriptions[${i}].timing`, 'RFC8866:5.9'));
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
// Validate start/stop time relationship
|
|
151
|
+
const { startTime, stopTime } = td.timing;
|
|
152
|
+
if (startTime !== 0 && stopTime !== 0 && startTime > stopTime) {
|
|
153
|
+
errors.push(errors_1.ValidationError.constraintViolation(`Time description ${i}: start time (${startTime}) must be before or equal to stop time (${stopTime})`, 'RFC8866:5.9'));
|
|
154
|
+
}
|
|
155
|
+
// Validate repeat times have at least one offset
|
|
156
|
+
for (let j = 0; j < td.repeats.length; j++) {
|
|
157
|
+
const repeat = td.repeats[j];
|
|
158
|
+
if (!repeat.offsets || repeat.offsets.length === 0) {
|
|
159
|
+
errors.push(errors_1.ValidationError.constraintViolation(`Time description ${i}, repeat ${j}: at least one offset is required`, 'RFC8866:5.10'));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Validate timezone adjustments
|
|
163
|
+
if (td.timezone) {
|
|
164
|
+
if (!td.timezone.adjustments || td.timezone.adjustments.length === 0) {
|
|
165
|
+
errors.push(errors_1.ValidationError.constraintViolation(`Time description ${i}: timezone field requires at least one adjustment`, 'RFC8866:5.11'));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Validate connection coverage
|
|
172
|
+
*
|
|
173
|
+
* RFC 8866 Section 5.7: Connection information MUST be present either
|
|
174
|
+
* at the session level OR in every media description
|
|
175
|
+
*/
|
|
176
|
+
function validateConnectionCoverage(session, errors) {
|
|
177
|
+
var _a;
|
|
178
|
+
const hasSessionConnection = session.connection !== undefined;
|
|
179
|
+
const mediaDescriptions = (_a = session.mediaDescriptions) !== null && _a !== void 0 ? _a : [];
|
|
180
|
+
// If no media descriptions, session-level connection is optional
|
|
181
|
+
if (mediaDescriptions.length === 0) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
// If session has connection, all media are covered
|
|
185
|
+
if (hasSessionConnection) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
// Check each media has connection
|
|
189
|
+
for (let i = 0; i < mediaDescriptions.length; i++) {
|
|
190
|
+
const md = mediaDescriptions[i];
|
|
191
|
+
if (!md.connections || md.connections.length === 0) {
|
|
192
|
+
errors.push(errors_1.ValidationError.constraintViolation(`Media description ${i} (${md.media.type}): connection information required when no session-level connection is present`, 'RFC8866:5.7'));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Validate bandwidth fields
|
|
198
|
+
*
|
|
199
|
+
* RFC 8866 Section 5.8: Bandwidth values must be non-negative
|
|
200
|
+
*/
|
|
201
|
+
function validateBandwidths(session, errors) {
|
|
202
|
+
for (let i = 0; i < session.bandwidths.length; i++) {
|
|
203
|
+
const bw = session.bandwidths[i];
|
|
204
|
+
if (bw.value < 0) {
|
|
205
|
+
errors.push(errors_1.ValidationError.invalidValue(`bandwidths[${i}].value`, bw.value, 'Bandwidth value must be non-negative', 'RFC8866:5.8'));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Validate session-level attributes
|
|
211
|
+
*
|
|
212
|
+
* RFC 8866 Section 6: Various attribute constraints including:
|
|
213
|
+
* - Only one direction attribute allowed
|
|
214
|
+
* - Media-only attributes cannot appear at session level
|
|
215
|
+
*/
|
|
216
|
+
function validateSessionAttributes(session, errors) {
|
|
217
|
+
const directionAttrs = ['sendrecv', 'sendonly', 'recvonly', 'inactive'];
|
|
218
|
+
let directionCount = 0;
|
|
219
|
+
for (const attr of session.attributes) {
|
|
220
|
+
// Check for multiple direction attributes (only one allowed)
|
|
221
|
+
if (attr.kind === 'property' && directionAttrs.includes(attr.name)) {
|
|
222
|
+
directionCount++;
|
|
223
|
+
}
|
|
224
|
+
// Check for media-only attributes at session level (RFC 8866 Section 6)
|
|
225
|
+
if (attributes_1.MEDIA_ONLY_ATTRIBUTES.includes(attr.name)) {
|
|
226
|
+
errors.push(errors_1.ValidationError.constraintViolation(`Attribute '${attr.name}' is only valid at media level, not session level`, 'RFC8866:6'));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (directionCount > 1) {
|
|
230
|
+
errors.push(errors_1.ValidationError.constraintViolation('Only one direction attribute (sendrecv/sendonly/recvonly/inactive) allowed at session level', 'RFC8866:6.7'));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Validate email addresses
|
|
235
|
+
*
|
|
236
|
+
* RFC 8866 Section 5.6: Email formats
|
|
237
|
+
* RFC 8866 Section 9: email-address = address-and-comment / dispname-and-address / addr-spec
|
|
238
|
+
*/
|
|
239
|
+
function validateEmails(session, errors) {
|
|
240
|
+
if (!session.emails || session.emails.length === 0) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
for (let i = 0; i < session.emails.length; i++) {
|
|
244
|
+
const email = session.emails[i];
|
|
245
|
+
if (!(0, format_validators_1.isValidEmailFormat)(email)) {
|
|
246
|
+
errors.push(errors_1.ValidationError.invalidValue(`emails[${i}]`, email, 'Invalid email format. Expected: addr-spec, addr-spec (comment), or Display Name <addr-spec>', 'RFC8866:5.6'));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Validate phone numbers
|
|
252
|
+
*
|
|
253
|
+
* RFC 8866 Section 5.6: Phone formats
|
|
254
|
+
* RFC 8866 Section 9: phone-number = phone *SP "(" 1*email-safe ")" / 1*email-safe "<" phone ">" / phone
|
|
255
|
+
*/
|
|
256
|
+
function validatePhones(session, errors) {
|
|
257
|
+
if (!session.phones || session.phones.length === 0) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
for (let i = 0; i < session.phones.length; i++) {
|
|
261
|
+
const phone = session.phones[i];
|
|
262
|
+
if (!(0, format_validators_1.isValidPhoneFormat)(phone)) {
|
|
263
|
+
errors.push(errors_1.ValidationError.invalidValue(`phones[${i}]`, phone, 'Invalid phone format. Expected: phone, phone (comment), or Display Name <phone>', 'RFC8866:5.6'));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Validate URI
|
|
269
|
+
*
|
|
270
|
+
* RFC 8866 Section 5.5: URI field
|
|
271
|
+
* RFC 3986: URI-reference format
|
|
272
|
+
*/
|
|
273
|
+
function validateUri(session, errors) {
|
|
274
|
+
if (!session.uri) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (!(0, format_validators_1.isValidURIReference)(session.uri)) {
|
|
278
|
+
errors.push(errors_1.ValidationError.invalidValue('uri', session.uri, 'Invalid URI format per RFC 3986', 'RFC8866:5.5'));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"round-trip.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/round-trip.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Integration tests for round-trip parsing, validation, and serialization
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const src_1 = require("../../src");
|
|
7
|
+
describe('Round-trip integration tests', () => {
|
|
8
|
+
describe('parse -> serialize', () => {
|
|
9
|
+
it('should round-trip minimal SDP', () => {
|
|
10
|
+
const originalSdp = [
|
|
11
|
+
'v=0',
|
|
12
|
+
'o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5',
|
|
13
|
+
's=Test Session',
|
|
14
|
+
't=0 0',
|
|
15
|
+
'',
|
|
16
|
+
].join('\r\n');
|
|
17
|
+
const parsed = (0, src_1.parseSessionDescription)(originalSdp);
|
|
18
|
+
const serialized = (0, src_1.serializeSessionDescription)(parsed);
|
|
19
|
+
expect(serialized).toBe(originalSdp);
|
|
20
|
+
});
|
|
21
|
+
it('should round-trip SDP with connection', () => {
|
|
22
|
+
const originalSdp = [
|
|
23
|
+
'v=0',
|
|
24
|
+
'o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5',
|
|
25
|
+
's=Test Session',
|
|
26
|
+
'c=IN IP4 224.2.17.12/127',
|
|
27
|
+
't=0 0',
|
|
28
|
+
'',
|
|
29
|
+
].join('\r\n');
|
|
30
|
+
const parsed = (0, src_1.parseSessionDescription)(originalSdp);
|
|
31
|
+
const serialized = (0, src_1.serializeSessionDescription)(parsed);
|
|
32
|
+
expect(serialized).toBe(originalSdp);
|
|
33
|
+
});
|
|
34
|
+
it('should round-trip SDP with media description', () => {
|
|
35
|
+
const originalSdp = [
|
|
36
|
+
'v=0',
|
|
37
|
+
'o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5',
|
|
38
|
+
's=Test Session',
|
|
39
|
+
'c=IN IP4 192.168.1.1',
|
|
40
|
+
't=0 0',
|
|
41
|
+
'm=audio 49170 RTP/AVP 0 8',
|
|
42
|
+
'a=rtpmap:0 PCMU/8000',
|
|
43
|
+
'a=rtpmap:8 PCMA/8000',
|
|
44
|
+
'',
|
|
45
|
+
].join('\r\n');
|
|
46
|
+
const parsed = (0, src_1.parseSessionDescription)(originalSdp);
|
|
47
|
+
const serialized = (0, src_1.serializeSessionDescription)(parsed);
|
|
48
|
+
expect(serialized).toBe(originalSdp);
|
|
49
|
+
});
|
|
50
|
+
it('should round-trip SDP with multiple media descriptions', () => {
|
|
51
|
+
const originalSdp = [
|
|
52
|
+
'v=0',
|
|
53
|
+
'o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5',
|
|
54
|
+
's=Test Session',
|
|
55
|
+
'c=IN IP4 192.168.1.1',
|
|
56
|
+
't=0 0',
|
|
57
|
+
'm=audio 49170 RTP/AVP 0',
|
|
58
|
+
'a=rtpmap:0 PCMU/8000',
|
|
59
|
+
'm=video 51372 RTP/AVP 96',
|
|
60
|
+
'a=rtpmap:96 H264/90000',
|
|
61
|
+
'',
|
|
62
|
+
].join('\r\n');
|
|
63
|
+
const parsed = (0, src_1.parseSessionDescription)(originalSdp);
|
|
64
|
+
const serialized = (0, src_1.serializeSessionDescription)(parsed);
|
|
65
|
+
expect(serialized).toBe(originalSdp);
|
|
66
|
+
});
|
|
67
|
+
it('should round-trip SDP with all optional fields', () => {
|
|
68
|
+
const originalSdp = [
|
|
69
|
+
'v=0',
|
|
70
|
+
'o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5',
|
|
71
|
+
's=SDP Seminar',
|
|
72
|
+
'i=A seminar on SDP',
|
|
73
|
+
'u=http://www.example.com/seminars/sdp.pdf',
|
|
74
|
+
'e=j.doe@example.com',
|
|
75
|
+
'p=+1-555-1234',
|
|
76
|
+
'c=IN IP4 224.2.17.12/127',
|
|
77
|
+
'b=CT:1000',
|
|
78
|
+
't=2873397496 2873404696',
|
|
79
|
+
'a=recvonly',
|
|
80
|
+
'm=audio 49170 RTP/AVP 0',
|
|
81
|
+
'i=Audio stream',
|
|
82
|
+
'b=AS:128',
|
|
83
|
+
'a=rtpmap:0 PCMU/8000',
|
|
84
|
+
'',
|
|
85
|
+
].join('\r\n');
|
|
86
|
+
const parsed = (0, src_1.parseSessionDescription)(originalSdp);
|
|
87
|
+
const serialized = (0, src_1.serializeSessionDescription)(parsed);
|
|
88
|
+
expect(serialized).toBe(originalSdp);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe('parse -> validate -> serialize', () => {
|
|
92
|
+
it('should parse, validate, and serialize valid SDP', () => {
|
|
93
|
+
const sdpString = [
|
|
94
|
+
'v=0',
|
|
95
|
+
'o=alice 2890844526 2890842807 IN IP4 192.168.1.1',
|
|
96
|
+
's=Call',
|
|
97
|
+
'c=IN IP4 192.168.1.100',
|
|
98
|
+
't=0 0',
|
|
99
|
+
'm=audio 49170 RTP/AVP 0 8',
|
|
100
|
+
'a=rtpmap:0 PCMU/8000',
|
|
101
|
+
'a=rtpmap:8 PCMA/8000',
|
|
102
|
+
'a=sendrecv',
|
|
103
|
+
'',
|
|
104
|
+
].join('\r\n');
|
|
105
|
+
const parsed = (0, src_1.parseSessionDescription)(sdpString);
|
|
106
|
+
const validationResult = (0, src_1.validateSdp)(parsed);
|
|
107
|
+
const serialized = (0, src_1.serializeSessionDescription)(parsed);
|
|
108
|
+
expect(validationResult.valid).toBe(true);
|
|
109
|
+
expect(validationResult.errors).toHaveLength(0);
|
|
110
|
+
expect(serialized).toBe(sdpString);
|
|
111
|
+
});
|
|
112
|
+
it('should detect validation errors in parsed SDP', () => {
|
|
113
|
+
// Create a session that will have validation issues
|
|
114
|
+
const sdpString = [
|
|
115
|
+
'v=0',
|
|
116
|
+
'o=alice 2890844526 2890842807 IN IP4 192.168.1.1',
|
|
117
|
+
's=Call',
|
|
118
|
+
'c=IN IP4 192.168.1.100',
|
|
119
|
+
't=0 0',
|
|
120
|
+
'm=audio 49170 RTP/AVP 0',
|
|
121
|
+
'a=sendrecv',
|
|
122
|
+
'a=recvonly', // Multiple direction attributes - invalid
|
|
123
|
+
'',
|
|
124
|
+
].join('\r\n');
|
|
125
|
+
const parsed = (0, src_1.parseSessionDescription)(sdpString);
|
|
126
|
+
const validationResult = (0, src_1.validateSdp)(parsed);
|
|
127
|
+
expect(validationResult.valid).toBe(false);
|
|
128
|
+
expect(validationResult.errors.some((e) => e.constraint === 'RFC8866:6.7')).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe('build -> validate -> serialize', () => {
|
|
132
|
+
it('should build, validate, and serialize minimal SDP', () => {
|
|
133
|
+
const session = new src_1.SessionBuilder()
|
|
134
|
+
.origin('alice', '123456789', '123456789', '192.168.1.1')
|
|
135
|
+
.sessionName('Test Call')
|
|
136
|
+
.connection('192.168.1.100')
|
|
137
|
+
.build();
|
|
138
|
+
const validationResult = (0, src_1.validateSdp)(session);
|
|
139
|
+
const serialized = (0, src_1.serializeSessionDescription)(session);
|
|
140
|
+
expect(validationResult.valid).toBe(true);
|
|
141
|
+
expect(serialized).toContain('v=0');
|
|
142
|
+
expect(serialized).toContain('o=alice 123456789 123456789 IN IP4 192.168.1.1');
|
|
143
|
+
expect(serialized).toContain('s=Test Call');
|
|
144
|
+
expect(serialized).toContain('c=IN IP4 192.168.1.100');
|
|
145
|
+
expect(serialized).toContain('t=0 0');
|
|
146
|
+
});
|
|
147
|
+
it('should build, validate, and serialize SDP with audio media', () => {
|
|
148
|
+
const session = new src_1.SessionBuilder()
|
|
149
|
+
.origin('alice', '123', '456', '192.168.1.1')
|
|
150
|
+
.sessionName('Audio Call')
|
|
151
|
+
.connection('192.168.1.100')
|
|
152
|
+
.addMediaBuilder((b) => b
|
|
153
|
+
.type('audio')
|
|
154
|
+
.port(49170)
|
|
155
|
+
.protocol('RTP/AVP')
|
|
156
|
+
.formats(['0', '8'])
|
|
157
|
+
.rtpmap(0, 'PCMU', 8000)
|
|
158
|
+
.rtpmap(8, 'PCMA', 8000)
|
|
159
|
+
.ptime(20)
|
|
160
|
+
.sendrecv())
|
|
161
|
+
.build();
|
|
162
|
+
const validationResult = (0, src_1.validateSdp)(session);
|
|
163
|
+
const serialized = (0, src_1.serializeSessionDescription)(session);
|
|
164
|
+
expect(validationResult.valid).toBe(true);
|
|
165
|
+
expect(serialized).toContain('m=audio 49170 RTP/AVP 0 8');
|
|
166
|
+
expect(serialized).toContain('a=rtpmap:0 PCMU/8000');
|
|
167
|
+
expect(serialized).toContain('a=rtpmap:8 PCMA/8000');
|
|
168
|
+
expect(serialized).toContain('a=ptime:20');
|
|
169
|
+
expect(serialized).toContain('a=sendrecv');
|
|
170
|
+
});
|
|
171
|
+
it('should build, validate, and serialize SDP with video media', () => {
|
|
172
|
+
const session = new src_1.SessionBuilder()
|
|
173
|
+
.origin('bob', '123', '456', '10.0.0.1')
|
|
174
|
+
.sessionName('Video Call')
|
|
175
|
+
.connection('10.0.0.100')
|
|
176
|
+
.addMediaBuilder((b) => b
|
|
177
|
+
.type('video')
|
|
178
|
+
.port(51372)
|
|
179
|
+
.protocol('RTP/AVP')
|
|
180
|
+
.formats(['96', '97'])
|
|
181
|
+
.rtpmap(96, 'VP8', 90000)
|
|
182
|
+
.rtpmap(97, 'H264', 90000)
|
|
183
|
+
.fmtp(97, 'profile-level-id=42e01f')
|
|
184
|
+
.framerate(30)
|
|
185
|
+
.sendrecv())
|
|
186
|
+
.build();
|
|
187
|
+
const validationResult = (0, src_1.validateSdp)(session);
|
|
188
|
+
const serialized = (0, src_1.serializeSessionDescription)(session);
|
|
189
|
+
expect(validationResult.valid).toBe(true);
|
|
190
|
+
expect(serialized).toContain('m=video 51372 RTP/AVP 96 97');
|
|
191
|
+
expect(serialized).toContain('a=rtpmap:96 VP8/90000');
|
|
192
|
+
expect(serialized).toContain('a=rtpmap:97 H264/90000');
|
|
193
|
+
expect(serialized).toContain('a=fmtp:97 profile-level-id=42e01f');
|
|
194
|
+
expect(serialized).toContain('a=framerate:30');
|
|
195
|
+
});
|
|
196
|
+
it('should build SDP with both audio and video using factory functions', () => {
|
|
197
|
+
const session = new src_1.SessionBuilder()
|
|
198
|
+
.origin('charlie', '111', '222', '172.16.0.1')
|
|
199
|
+
.sessionName('A/V Call')
|
|
200
|
+
.connection('172.16.0.50')
|
|
201
|
+
.addMedia((0, src_1.audioBuilder)()
|
|
202
|
+
.port(49170)
|
|
203
|
+
.protocol('RTP/SAVPF')
|
|
204
|
+
.formats(['111'])
|
|
205
|
+
.rtpmap(111, 'opus', 48000, 2)
|
|
206
|
+
.fmtp(111, 'minptime=10;useinbandfec=1')
|
|
207
|
+
.sendrecv()
|
|
208
|
+
.rtcpMux()
|
|
209
|
+
.build())
|
|
210
|
+
.addMedia((0, src_1.videoBuilder)()
|
|
211
|
+
.port(51372)
|
|
212
|
+
.protocol('RTP/SAVPF')
|
|
213
|
+
.formats(['96'])
|
|
214
|
+
.rtpmap(96, 'VP8', 90000)
|
|
215
|
+
.sendrecv()
|
|
216
|
+
.rtcpMux()
|
|
217
|
+
.build())
|
|
218
|
+
.build();
|
|
219
|
+
const validationResult = (0, src_1.validateSdp)(session);
|
|
220
|
+
const serialized = (0, src_1.serializeSessionDescription)(session);
|
|
221
|
+
expect(validationResult.valid).toBe(true);
|
|
222
|
+
expect(serialized).toContain('m=audio 49170 RTP/SAVPF 111');
|
|
223
|
+
expect(serialized).toContain('m=video 51372 RTP/SAVPF 96');
|
|
224
|
+
expect(serialized).toContain('a=rtcp-mux');
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
describe('build -> serialize -> parse -> validate', () => {
|
|
228
|
+
it('should complete full round-trip with builder', () => {
|
|
229
|
+
// Build
|
|
230
|
+
const original = new src_1.SessionBuilder()
|
|
231
|
+
.origin('user1', '2890844526', '2890842807', '192.168.1.1')
|
|
232
|
+
.sessionName('Round Trip Test')
|
|
233
|
+
.sessionInformation('Testing full round trip')
|
|
234
|
+
.connection('192.168.1.100')
|
|
235
|
+
.addBandwidth('CT', 1000)
|
|
236
|
+
.addTiming(0, 0)
|
|
237
|
+
.tool('tsdp/1.0')
|
|
238
|
+
.addMediaBuilder((b) => b
|
|
239
|
+
.type('audio')
|
|
240
|
+
.port(49170)
|
|
241
|
+
.protocol('RTP/AVP')
|
|
242
|
+
.formats(['0', '8', '96'])
|
|
243
|
+
.rtpmap(0, 'PCMU', 8000)
|
|
244
|
+
.rtpmap(8, 'PCMA', 8000)
|
|
245
|
+
.rtpmap(96, 'telephone-event', 8000)
|
|
246
|
+
.fmtp(96, '0-15')
|
|
247
|
+
.ptime(20)
|
|
248
|
+
.bandwidth(128)
|
|
249
|
+
.sendrecv())
|
|
250
|
+
.build();
|
|
251
|
+
// Serialize
|
|
252
|
+
const serialized = (0, src_1.serializeSessionDescription)(original);
|
|
253
|
+
// Parse
|
|
254
|
+
const parsed = (0, src_1.parseSessionDescription)(serialized);
|
|
255
|
+
// Validate
|
|
256
|
+
const validationResult = (0, src_1.validateSdp)(parsed);
|
|
257
|
+
// Assertions
|
|
258
|
+
expect(validationResult.valid).toBe(true);
|
|
259
|
+
expect(parsed.version).toBe(original.version);
|
|
260
|
+
expect(parsed.origin.username).toBe(original.origin.username);
|
|
261
|
+
expect(parsed.sessionName).toBe(original.sessionName);
|
|
262
|
+
expect(parsed.sessionInformation).toBe(original.sessionInformation);
|
|
263
|
+
expect(parsed.mediaDescriptions).toHaveLength(1);
|
|
264
|
+
expect(parsed.mediaDescriptions[0].media.type).toBe('audio');
|
|
265
|
+
expect(parsed.mediaDescriptions[0].media.formats).toEqual(['0', '8', '96']);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
describe('validation options', () => {
|
|
269
|
+
it('should allow deprecated key field when option is set', () => {
|
|
270
|
+
const sdpString = [
|
|
271
|
+
'v=0',
|
|
272
|
+
'o=alice 123 456 IN IP4 192.168.1.1',
|
|
273
|
+
's=Test',
|
|
274
|
+
'c=IN IP4 192.168.1.100',
|
|
275
|
+
't=0 0',
|
|
276
|
+
'k=clear:password',
|
|
277
|
+
'',
|
|
278
|
+
].join('\r\n');
|
|
279
|
+
const parsed = (0, src_1.parseSessionDescription)(sdpString);
|
|
280
|
+
// Without allowing deprecated key
|
|
281
|
+
const strictResult = (0, src_1.validateSdp)(parsed, { allowDeprecatedKeyField: false });
|
|
282
|
+
expect(strictResult.valid).toBe(false);
|
|
283
|
+
expect(strictResult.errors.some((e) => e.message.includes('deprecated'))).toBe(true);
|
|
284
|
+
// With allowing deprecated key
|
|
285
|
+
const lenientResult = (0, src_1.validateSdp)(parsed, { allowDeprecatedKeyField: true });
|
|
286
|
+
expect(lenientResult.valid).toBe(true);
|
|
287
|
+
});
|
|
288
|
+
it('should require session connection when option is set', () => {
|
|
289
|
+
const session = new src_1.SessionBuilder()
|
|
290
|
+
.origin('alice', '123', '456', '192.168.1.1')
|
|
291
|
+
.sessionName('Test')
|
|
292
|
+
.build();
|
|
293
|
+
const result = (0, src_1.validateSdp)(session, { requireSessionConnection: true });
|
|
294
|
+
expect(result.valid).toBe(false);
|
|
295
|
+
expect(result.errors.some((e) => e.message.includes('Session-level connection'))).toBe(true);
|
|
296
|
+
});
|
|
297
|
+
it('should skip semantic validation when option is set', () => {
|
|
298
|
+
// Create session with CT < sum of AS (semantic violation)
|
|
299
|
+
const session = new src_1.SessionBuilder()
|
|
300
|
+
.origin('alice', '123', '456', '192.168.1.1')
|
|
301
|
+
.sessionName('Test')
|
|
302
|
+
.connection('192.168.1.100')
|
|
303
|
+
.addBandwidth('CT', 100)
|
|
304
|
+
.addMediaBuilder((b) => b
|
|
305
|
+
.type('audio')
|
|
306
|
+
.port(49170)
|
|
307
|
+
.protocol('RTP/AVP')
|
|
308
|
+
.formats(['0'])
|
|
309
|
+
.addBandwidth('AS', 500) // Exceeds CT
|
|
310
|
+
)
|
|
311
|
+
.build();
|
|
312
|
+
// With semantic validation
|
|
313
|
+
const withSemantic = (0, src_1.validateSdp)(session, { skipSemanticValidation: false });
|
|
314
|
+
expect(withSemantic.valid).toBe(false);
|
|
315
|
+
// Without semantic validation
|
|
316
|
+
const withoutSemantic = (0, src_1.validateSdp)(session, { skipSemanticValidation: true });
|
|
317
|
+
expect(withoutSemantic.valid).toBe(true);
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voip-examples.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/voip-examples.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|