@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.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/dist/src/builder/media-builder.d.ts +221 -0
  4. package/dist/src/builder/media-builder.d.ts.map +1 -0
  5. package/dist/src/builder/media-builder.js +385 -0
  6. package/dist/src/builder/session-builder.d.ts +195 -0
  7. package/dist/src/builder/session-builder.d.ts.map +1 -0
  8. package/dist/src/builder/session-builder.js +366 -0
  9. package/dist/src/index.d.ts +67 -0
  10. package/dist/src/index.d.ts.map +1 -0
  11. package/dist/src/index.js +250 -0
  12. package/dist/src/parser/attribute-parser.d.ts +100 -0
  13. package/dist/src/parser/attribute-parser.d.ts.map +1 -0
  14. package/dist/src/parser/attribute-parser.js +217 -0
  15. package/dist/src/parser/field-parser.d.ts +124 -0
  16. package/dist/src/parser/field-parser.d.ts.map +1 -0
  17. package/dist/src/parser/field-parser.js +335 -0
  18. package/dist/src/parser/media-parser.d.ts +45 -0
  19. package/dist/src/parser/media-parser.d.ts.map +1 -0
  20. package/dist/src/parser/media-parser.js +157 -0
  21. package/dist/src/parser/primitive-parser.d.ts +138 -0
  22. package/dist/src/parser/primitive-parser.d.ts.map +1 -0
  23. package/dist/src/parser/primitive-parser.js +316 -0
  24. package/dist/src/parser/scanner.d.ts +142 -0
  25. package/dist/src/parser/scanner.d.ts.map +1 -0
  26. package/dist/src/parser/scanner.js +284 -0
  27. package/dist/src/parser/session-parser.d.ts +35 -0
  28. package/dist/src/parser/session-parser.d.ts.map +1 -0
  29. package/dist/src/parser/session-parser.js +207 -0
  30. package/dist/src/parser/time-parser.d.ts +74 -0
  31. package/dist/src/parser/time-parser.d.ts.map +1 -0
  32. package/dist/src/parser/time-parser.js +168 -0
  33. package/dist/src/serializer/attribute-serializer.d.ts +18 -0
  34. package/dist/src/serializer/attribute-serializer.d.ts.map +1 -0
  35. package/dist/src/serializer/attribute-serializer.js +34 -0
  36. package/dist/src/serializer/field-serializer.d.ts +112 -0
  37. package/dist/src/serializer/field-serializer.d.ts.map +1 -0
  38. package/dist/src/serializer/field-serializer.js +212 -0
  39. package/dist/src/serializer/media-serializer.d.ts +31 -0
  40. package/dist/src/serializer/media-serializer.d.ts.map +1 -0
  41. package/dist/src/serializer/media-serializer.js +83 -0
  42. package/dist/src/serializer/session-serializer.d.ts +29 -0
  43. package/dist/src/serializer/session-serializer.d.ts.map +1 -0
  44. package/dist/src/serializer/session-serializer.js +99 -0
  45. package/dist/src/serializer/time-serializer.d.ts +46 -0
  46. package/dist/src/serializer/time-serializer.d.ts.map +1 -0
  47. package/dist/src/serializer/time-serializer.js +86 -0
  48. package/dist/src/types/attributes.d.ts +318 -0
  49. package/dist/src/types/attributes.d.ts.map +1 -0
  50. package/dist/src/types/attributes.js +225 -0
  51. package/dist/src/types/errors.d.ts +129 -0
  52. package/dist/src/types/errors.d.ts.map +1 -0
  53. package/dist/src/types/errors.js +186 -0
  54. package/dist/src/types/fields.d.ts +100 -0
  55. package/dist/src/types/fields.d.ts.map +1 -0
  56. package/dist/src/types/fields.js +48 -0
  57. package/dist/src/types/media.d.ts +148 -0
  58. package/dist/src/types/media.d.ts.map +1 -0
  59. package/dist/src/types/media.js +137 -0
  60. package/dist/src/types/network.d.ts +136 -0
  61. package/dist/src/types/network.d.ts.map +1 -0
  62. package/dist/src/types/network.js +130 -0
  63. package/dist/src/types/primitives.d.ts +193 -0
  64. package/dist/src/types/primitives.d.ts.map +1 -0
  65. package/dist/src/types/primitives.js +195 -0
  66. package/dist/src/types/session.d.ts +122 -0
  67. package/dist/src/types/session.d.ts.map +1 -0
  68. package/dist/src/types/session.js +81 -0
  69. package/dist/src/types/time.d.ts +129 -0
  70. package/dist/src/types/time.d.ts.map +1 -0
  71. package/dist/src/types/time.js +84 -0
  72. package/dist/src/utils/address-parser.d.ts +100 -0
  73. package/dist/src/utils/address-parser.d.ts.map +1 -0
  74. package/dist/src/utils/address-parser.js +338 -0
  75. package/dist/src/utils/format-validators.d.ts +77 -0
  76. package/dist/src/utils/format-validators.d.ts.map +1 -0
  77. package/dist/src/utils/format-validators.js +504 -0
  78. package/dist/src/utils/line-reader.d.ts +84 -0
  79. package/dist/src/utils/line-reader.d.ts.map +1 -0
  80. package/dist/src/utils/line-reader.js +169 -0
  81. package/dist/src/utils/time-converter.d.ts +99 -0
  82. package/dist/src/utils/time-converter.d.ts.map +1 -0
  83. package/dist/src/utils/time-converter.js +195 -0
  84. package/dist/src/validator/media-validator.d.ts +27 -0
  85. package/dist/src/validator/media-validator.d.ts.map +1 -0
  86. package/dist/src/validator/media-validator.js +241 -0
  87. package/dist/src/validator/semantic-validator.d.ts +47 -0
  88. package/dist/src/validator/semantic-validator.d.ts.map +1 -0
  89. package/dist/src/validator/semantic-validator.js +207 -0
  90. package/dist/src/validator/session-validator.d.ts +36 -0
  91. package/dist/src/validator/session-validator.d.ts.map +1 -0
  92. package/dist/src/validator/session-validator.js +280 -0
  93. package/dist/tests/integration/round-trip.test.d.ts +5 -0
  94. package/dist/tests/integration/round-trip.test.d.ts.map +1 -0
  95. package/dist/tests/integration/round-trip.test.js +320 -0
  96. package/dist/tests/integration/voip-examples.test.d.ts +5 -0
  97. package/dist/tests/integration/voip-examples.test.d.ts.map +1 -0
  98. package/dist/tests/integration/voip-examples.test.js +361 -0
  99. package/dist/tests/unit/builder/media-builder.test.d.ts +5 -0
  100. package/dist/tests/unit/builder/media-builder.test.d.ts.map +1 -0
  101. package/dist/tests/unit/builder/media-builder.test.js +524 -0
  102. package/dist/tests/unit/builder/session-builder.test.d.ts +5 -0
  103. package/dist/tests/unit/builder/session-builder.test.d.ts.map +1 -0
  104. package/dist/tests/unit/builder/session-builder.test.js +367 -0
  105. package/dist/tests/unit/parser/attribute-parser.test.d.ts +5 -0
  106. package/dist/tests/unit/parser/attribute-parser.test.d.ts.map +1 -0
  107. package/dist/tests/unit/parser/attribute-parser.test.js +319 -0
  108. package/dist/tests/unit/parser/field-parser.test.d.ts +5 -0
  109. package/dist/tests/unit/parser/field-parser.test.d.ts.map +1 -0
  110. package/dist/tests/unit/parser/field-parser.test.js +355 -0
  111. package/dist/tests/unit/parser/media-parser.test.d.ts +5 -0
  112. package/dist/tests/unit/parser/media-parser.test.d.ts.map +1 -0
  113. package/dist/tests/unit/parser/media-parser.test.js +241 -0
  114. package/dist/tests/unit/parser/primitive-parser.test.d.ts +5 -0
  115. package/dist/tests/unit/parser/primitive-parser.test.d.ts.map +1 -0
  116. package/dist/tests/unit/parser/primitive-parser.test.js +261 -0
  117. package/dist/tests/unit/parser/scanner.test.d.ts +5 -0
  118. package/dist/tests/unit/parser/scanner.test.d.ts.map +1 -0
  119. package/dist/tests/unit/parser/scanner.test.js +241 -0
  120. package/dist/tests/unit/parser/session-parser.test.d.ts +5 -0
  121. package/dist/tests/unit/parser/session-parser.test.d.ts.map +1 -0
  122. package/dist/tests/unit/parser/session-parser.test.js +346 -0
  123. package/dist/tests/unit/parser/time-parser.test.d.ts +5 -0
  124. package/dist/tests/unit/parser/time-parser.test.d.ts.map +1 -0
  125. package/dist/tests/unit/parser/time-parser.test.js +173 -0
  126. package/dist/tests/unit/serializer/attribute-serializer.test.d.ts +5 -0
  127. package/dist/tests/unit/serializer/attribute-serializer.test.d.ts.map +1 -0
  128. package/dist/tests/unit/serializer/attribute-serializer.test.js +78 -0
  129. package/dist/tests/unit/serializer/field-serializer.test.d.ts +5 -0
  130. package/dist/tests/unit/serializer/field-serializer.test.d.ts.map +1 -0
  131. package/dist/tests/unit/serializer/field-serializer.test.js +159 -0
  132. package/dist/tests/unit/serializer/media-serializer.test.d.ts +5 -0
  133. package/dist/tests/unit/serializer/media-serializer.test.d.ts.map +1 -0
  134. package/dist/tests/unit/serializer/media-serializer.test.js +155 -0
  135. package/dist/tests/unit/serializer/session-serializer.test.d.ts +5 -0
  136. package/dist/tests/unit/serializer/session-serializer.test.d.ts.map +1 -0
  137. package/dist/tests/unit/serializer/session-serializer.test.js +317 -0
  138. package/dist/tests/unit/serializer/time-serializer.test.d.ts +5 -0
  139. package/dist/tests/unit/serializer/time-serializer.test.d.ts.map +1 -0
  140. package/dist/tests/unit/serializer/time-serializer.test.js +115 -0
  141. package/dist/tests/unit/types/errors.test.d.ts +5 -0
  142. package/dist/tests/unit/types/errors.test.d.ts.map +1 -0
  143. package/dist/tests/unit/types/errors.test.js +127 -0
  144. package/dist/tests/unit/types/network.test.d.ts +5 -0
  145. package/dist/tests/unit/types/network.test.d.ts.map +1 -0
  146. package/dist/tests/unit/types/network.test.js +132 -0
  147. package/dist/tests/unit/types/primitives.test.d.ts +5 -0
  148. package/dist/tests/unit/types/primitives.test.d.ts.map +1 -0
  149. package/dist/tests/unit/types/primitives.test.js +108 -0
  150. package/dist/tests/unit/utils/address-parser.test.d.ts +5 -0
  151. package/dist/tests/unit/utils/address-parser.test.d.ts.map +1 -0
  152. package/dist/tests/unit/utils/address-parser.test.js +203 -0
  153. package/dist/tests/unit/utils/format-validators.test.d.ts +5 -0
  154. package/dist/tests/unit/utils/format-validators.test.d.ts.map +1 -0
  155. package/dist/tests/unit/utils/format-validators.test.js +224 -0
  156. package/dist/tests/unit/utils/line-reader.test.d.ts +5 -0
  157. package/dist/tests/unit/utils/line-reader.test.d.ts.map +1 -0
  158. package/dist/tests/unit/utils/line-reader.test.js +157 -0
  159. package/dist/tests/unit/utils/time-converter.test.d.ts +5 -0
  160. package/dist/tests/unit/utils/time-converter.test.d.ts.map +1 -0
  161. package/dist/tests/unit/utils/time-converter.test.js +190 -0
  162. package/dist/tests/unit/validator/media-validator.test.d.ts +5 -0
  163. package/dist/tests/unit/validator/media-validator.test.d.ts.map +1 -0
  164. package/dist/tests/unit/validator/media-validator.test.js +313 -0
  165. package/dist/tests/unit/validator/semantic-validator.test.d.ts +5 -0
  166. package/dist/tests/unit/validator/semantic-validator.test.d.ts.map +1 -0
  167. package/dist/tests/unit/validator/semantic-validator.test.js +262 -0
  168. package/dist/tests/unit/validator/session-validator.test.d.ts +5 -0
  169. package/dist/tests/unit/validator/session-validator.test.d.ts.map +1 -0
  170. package/dist/tests/unit/validator/session-validator.test.js +447 -0
  171. package/package.json +50 -0
@@ -0,0 +1,355 @@
1
+ "use strict";
2
+ /**
3
+ * Tests for field-parser.ts
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const scanner_1 = require("../../../src/parser/scanner");
7
+ const field_parser_1 = require("../../../src/parser/field-parser");
8
+ const errors_1 = require("../../../src/types/errors");
9
+ describe('field-parser', () => {
10
+ describe('parseVersionField', () => {
11
+ it('should parse version 0', () => {
12
+ const scanner = new scanner_1.Scanner('0');
13
+ const version = (0, field_parser_1.parseVersionField)(scanner);
14
+ expect(version).toBe(0);
15
+ });
16
+ it('should throw for non-zero version', () => {
17
+ expect(() => (0, field_parser_1.parseVersionField)(new scanner_1.Scanner('1'))).toThrow(errors_1.ParseError);
18
+ expect(() => (0, field_parser_1.parseVersionField)(new scanner_1.Scanner('1'))).toThrow('Unsupported SDP version');
19
+ });
20
+ it('should throw for invalid version', () => {
21
+ const scanner = new scanner_1.Scanner('abc');
22
+ expect(() => (0, field_parser_1.parseVersionField)(scanner)).toThrow(errors_1.ParseError);
23
+ });
24
+ });
25
+ describe('parseOriginField', () => {
26
+ it('should parse valid origin field', () => {
27
+ const scanner = new scanner_1.Scanner('jdoe 2890844526 2890842807 IN IP4 10.47.16.5');
28
+ const origin = (0, field_parser_1.parseOriginField)(scanner);
29
+ expect(origin.username).toBe('jdoe');
30
+ expect(origin.sessId).toBe('2890844526');
31
+ expect(origin.sessVersion).toBe('2890842807');
32
+ expect(origin.netType).toBe('IN');
33
+ expect(origin.addrType).toBe('IP4');
34
+ expect(origin.unicastAddress).toBe('10.47.16.5');
35
+ });
36
+ it('should parse origin with hyphen username', () => {
37
+ const scanner = new scanner_1.Scanner('- 123456 789012 IN IP4 192.168.1.1');
38
+ const origin = (0, field_parser_1.parseOriginField)(scanner);
39
+ expect(origin.username).toBe('-');
40
+ expect(origin.sessId).toBe('123456');
41
+ expect(origin.sessVersion).toBe('789012');
42
+ });
43
+ it('should parse origin with IPv6', () => {
44
+ const scanner = new scanner_1.Scanner('alice 123 456 IN IP6 2001:db8::1');
45
+ const origin = (0, field_parser_1.parseOriginField)(scanner);
46
+ expect(origin.username).toBe('alice');
47
+ expect(origin.netType).toBe('IN');
48
+ expect(origin.addrType).toBe('IP6');
49
+ expect(origin.unicastAddress).toBe('2001:db8::1');
50
+ });
51
+ it('should parse origin with FQDN', () => {
52
+ const scanner = new scanner_1.Scanner('bob 999 888 IN IP4 example.com');
53
+ const origin = (0, field_parser_1.parseOriginField)(scanner);
54
+ expect(origin.username).toBe('bob');
55
+ expect(origin.unicastAddress).toBe('example.com');
56
+ });
57
+ it('should throw for missing fields', () => {
58
+ expect(() => (0, field_parser_1.parseOriginField)(new scanner_1.Scanner('jdoe'))).toThrow(errors_1.ParseError);
59
+ expect(() => (0, field_parser_1.parseOriginField)(new scanner_1.Scanner('jdoe 123'))).toThrow(errors_1.ParseError);
60
+ expect(() => (0, field_parser_1.parseOriginField)(new scanner_1.Scanner('jdoe 123 456'))).toThrow(errors_1.ParseError);
61
+ });
62
+ });
63
+ describe('parseSessionNameField', () => {
64
+ it('should parse session name', () => {
65
+ const scanner = new scanner_1.Scanner('SDP Seminar');
66
+ const name = (0, field_parser_1.parseSessionNameField)(scanner);
67
+ expect(name).toBe('SDP Seminar');
68
+ });
69
+ it('should parse single space as session name', () => {
70
+ const scanner = new scanner_1.Scanner(' ');
71
+ const name = (0, field_parser_1.parseSessionNameField)(scanner);
72
+ expect(name).toBe(' ');
73
+ });
74
+ it('should parse session name with special characters', () => {
75
+ const scanner = new scanner_1.Scanner('My Conference Call #123');
76
+ const name = (0, field_parser_1.parseSessionNameField)(scanner);
77
+ expect(name).toBe('My Conference Call #123');
78
+ });
79
+ it('should throw for empty session name', () => {
80
+ const scanner = new scanner_1.Scanner('');
81
+ expect(() => (0, field_parser_1.parseSessionNameField)(scanner)).toThrow(errors_1.ParseError);
82
+ expect(() => (0, field_parser_1.parseSessionNameField)(scanner)).toThrow('cannot be empty');
83
+ });
84
+ });
85
+ describe('parseInformationField', () => {
86
+ it('should parse information text', () => {
87
+ const scanner = new scanner_1.Scanner('A Seminar on the session description protocol');
88
+ const info = (0, field_parser_1.parseInformationField)(scanner);
89
+ expect(info).toBe('A Seminar on the session description protocol');
90
+ });
91
+ it('should parse empty information', () => {
92
+ const scanner = new scanner_1.Scanner('');
93
+ const info = (0, field_parser_1.parseInformationField)(scanner);
94
+ expect(info).toBe('');
95
+ });
96
+ it('should stop at newline', () => {
97
+ const scanner = new scanner_1.Scanner('Session info\r\nnext line');
98
+ const info = (0, field_parser_1.parseInformationField)(scanner);
99
+ expect(info).toBe('Session info');
100
+ });
101
+ });
102
+ describe('parseUriField', () => {
103
+ it('should parse HTTP URI', () => {
104
+ const scanner = new scanner_1.Scanner('http://example.com/session');
105
+ const uri = (0, field_parser_1.parseUriField)(scanner);
106
+ expect(uri).toBe('http://example.com/session');
107
+ });
108
+ it('should parse HTTPS URI', () => {
109
+ const scanner = new scanner_1.Scanner('https://example.com:8080/path?query=value');
110
+ const uri = (0, field_parser_1.parseUriField)(scanner);
111
+ expect(uri).toBe('https://example.com:8080/path?query=value');
112
+ });
113
+ it('should stop at newline', () => {
114
+ const scanner = new scanner_1.Scanner('http://example.com\r\n');
115
+ const uri = (0, field_parser_1.parseUriField)(scanner);
116
+ expect(uri).toBe('http://example.com');
117
+ });
118
+ });
119
+ describe('parseEmailField', () => {
120
+ it('should parse email address', () => {
121
+ const scanner = new scanner_1.Scanner('user@example.com');
122
+ const email = (0, field_parser_1.parseEmailField)(scanner);
123
+ expect(email).toBe('user@example.com');
124
+ });
125
+ it('should parse email with display name', () => {
126
+ const scanner = new scanner_1.Scanner('John Doe <john@example.com>');
127
+ const email = (0, field_parser_1.parseEmailField)(scanner);
128
+ expect(email).toBe('John Doe <john@example.com>');
129
+ });
130
+ it('should stop at newline', () => {
131
+ const scanner = new scanner_1.Scanner('user@example.com\r\n');
132
+ const email = (0, field_parser_1.parseEmailField)(scanner);
133
+ expect(email).toBe('user@example.com');
134
+ });
135
+ });
136
+ describe('parsePhoneField', () => {
137
+ it('should parse phone number', () => {
138
+ const scanner = new scanner_1.Scanner('+1-555-1234');
139
+ const phone = (0, field_parser_1.parsePhoneField)(scanner);
140
+ expect(phone).toBe('+1-555-1234');
141
+ });
142
+ it('should parse phone with display name', () => {
143
+ const scanner = new scanner_1.Scanner('John Doe +1-555-1234');
144
+ const phone = (0, field_parser_1.parsePhoneField)(scanner);
145
+ expect(phone).toBe('John Doe +1-555-1234');
146
+ });
147
+ it('should stop at newline', () => {
148
+ const scanner = new scanner_1.Scanner('+1-555-1234\r\n');
149
+ const phone = (0, field_parser_1.parsePhoneField)(scanner);
150
+ expect(phone).toBe('+1-555-1234');
151
+ });
152
+ });
153
+ describe('parseConnectionField', () => {
154
+ it('should parse unicast IPv4 connection', () => {
155
+ const scanner = new scanner_1.Scanner('IN IP4 192.168.1.100');
156
+ const conn = (0, field_parser_1.parseConnectionField)(scanner);
157
+ expect(conn.netType).toBe('IN');
158
+ expect(conn.addrType).toBe('IP4');
159
+ expect(conn.connectionAddress).toBe('192.168.1.100');
160
+ });
161
+ it('should parse unicast IPv6 connection', () => {
162
+ const scanner = new scanner_1.Scanner('IN IP6 2001:db8::1');
163
+ const conn = (0, field_parser_1.parseConnectionField)(scanner);
164
+ expect(conn.netType).toBe('IN');
165
+ expect(conn.addrType).toBe('IP6');
166
+ expect(conn.connectionAddress).toBe('2001:db8::1');
167
+ });
168
+ it('should parse unicast FQDN connection', () => {
169
+ const scanner = new scanner_1.Scanner('IN IP4 example.com');
170
+ const conn = (0, field_parser_1.parseConnectionField)(scanner);
171
+ expect(conn.netType).toBe('IN');
172
+ expect(conn.addrType).toBe('IP4');
173
+ expect(conn.connectionAddress).toBe('example.com');
174
+ });
175
+ it('should parse IPv4 multicast with TTL', () => {
176
+ const scanner = new scanner_1.Scanner('IN IP4 224.2.36.42/127');
177
+ const conn = (0, field_parser_1.parseConnectionField)(scanner);
178
+ expect(conn.netType).toBe('IN');
179
+ expect(conn.addrType).toBe('IP4');
180
+ expect(conn.connectionAddress).toEqual({
181
+ kind: 'ip4-multicast',
182
+ address: '224.2.36.42',
183
+ ttl: 127,
184
+ });
185
+ });
186
+ it('should parse IPv4 multicast with TTL and numAddresses', () => {
187
+ const scanner = new scanner_1.Scanner('IN IP4 224.2.1.1/127/3');
188
+ const conn = (0, field_parser_1.parseConnectionField)(scanner);
189
+ expect(conn.connectionAddress).toEqual({
190
+ kind: 'ip4-multicast',
191
+ address: '224.2.1.1',
192
+ ttl: 127,
193
+ numAddresses: 3,
194
+ });
195
+ });
196
+ it('should parse IPv6 multicast with numAddresses', () => {
197
+ const scanner = new scanner_1.Scanner('IN IP6 ff15::101/3');
198
+ const conn = (0, field_parser_1.parseConnectionField)(scanner);
199
+ expect(conn.connectionAddress).toEqual({
200
+ kind: 'ip6-multicast',
201
+ address: 'ff15::101',
202
+ numAddresses: 3,
203
+ });
204
+ });
205
+ it('should parse FQDN multicast with TTL', () => {
206
+ const scanner = new scanner_1.Scanner('IN IP4 example.com/127');
207
+ const conn = (0, field_parser_1.parseConnectionField)(scanner);
208
+ expect(conn.connectionAddress).toEqual({
209
+ kind: 'fqdn-multicast',
210
+ address: 'example.com',
211
+ ttl: 127,
212
+ });
213
+ });
214
+ it('should parse FQDN multicast with TTL and numAddresses', () => {
215
+ const scanner = new scanner_1.Scanner('IN IP4 example.com/64/5');
216
+ const conn = (0, field_parser_1.parseConnectionField)(scanner);
217
+ expect(conn.connectionAddress).toEqual({
218
+ kind: 'fqdn-multicast',
219
+ address: 'example.com',
220
+ ttl: 64,
221
+ numAddresses: 5,
222
+ });
223
+ });
224
+ it('should throw for IPv6 multicast with TTL', () => {
225
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP6 ff15::101/127/3'))).toThrow(errors_1.ParseError);
226
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP6 ff15::101/127/3'))).toThrow('cannot have TTL');
227
+ });
228
+ it('should throw for missing address', () => {
229
+ const scanner = new scanner_1.Scanner('IN IP4 ');
230
+ expect(() => (0, field_parser_1.parseConnectionField)(scanner)).toThrow(errors_1.ParseError);
231
+ });
232
+ it('should throw for invalid IPv4', () => {
233
+ const scanner = new scanner_1.Scanner('IN IP4 -invalid-');
234
+ expect(() => (0, field_parser_1.parseConnectionField)(scanner)).toThrow(errors_1.ParseError);
235
+ });
236
+ it('should throw for unicast IPv4 address with TTL (RFC 8866 Section 5.7)', () => {
237
+ // 192.168.1.1 is a unicast address, not multicast
238
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP4 192.168.1.1/127'))).toThrow(errors_1.ParseError);
239
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP4 192.168.1.1/127'))).toThrow('must be a multicast address');
240
+ });
241
+ it('should throw for unicast IPv4 address with TTL and numAddresses', () => {
242
+ // 10.0.0.1 is a unicast address, not multicast
243
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP4 10.0.0.1/64/3'))).toThrow(errors_1.ParseError);
244
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP4 10.0.0.1/64/3'))).toThrow('must be a multicast address');
245
+ });
246
+ it('should throw for unicast IPv6 address with numAddresses (RFC 8866 Section 5.7)', () => {
247
+ // 2001:db8::1 is a unicast address, not multicast
248
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP6 2001:db8::1/3'))).toThrow(errors_1.ParseError);
249
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP6 2001:db8::1/3'))).toThrow('must be a multicast address');
250
+ });
251
+ it('should accept valid IPv4 multicast address with TTL', () => {
252
+ // 224.0.0.1 is a valid multicast address
253
+ const scanner = new scanner_1.Scanner('IN IP4 224.0.0.1/127');
254
+ const conn = (0, field_parser_1.parseConnectionField)(scanner);
255
+ expect(conn.connectionAddress).toEqual({
256
+ kind: 'ip4-multicast',
257
+ address: '224.0.0.1',
258
+ ttl: 127,
259
+ });
260
+ });
261
+ it('should accept valid IPv4 multicast address in range 224-239', () => {
262
+ // 239.255.255.255 is at the top of the multicast range
263
+ const scanner = new scanner_1.Scanner('IN IP4 239.255.255.255/64');
264
+ const conn = (0, field_parser_1.parseConnectionField)(scanner);
265
+ expect(conn.connectionAddress).toEqual({
266
+ kind: 'ip4-multicast',
267
+ address: '239.255.255.255',
268
+ ttl: 64,
269
+ });
270
+ });
271
+ it('should accept valid IPv6 multicast address with numAddresses', () => {
272
+ // FF02::1 is a valid IPv6 multicast address
273
+ const scanner = new scanner_1.Scanner('IN IP6 FF02::1/5');
274
+ const conn = (0, field_parser_1.parseConnectionField)(scanner);
275
+ expect(conn.connectionAddress).toEqual({
276
+ kind: 'ip6-multicast',
277
+ address: 'FF02::1',
278
+ numAddresses: 5,
279
+ });
280
+ });
281
+ it('should reject IPv4 address just below multicast range with TTL', () => {
282
+ // 223.255.255.255 is just below the multicast range (224-239)
283
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP4 223.255.255.255/127'))).toThrow(errors_1.ParseError);
284
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP4 223.255.255.255/127'))).toThrow('must be a multicast address');
285
+ });
286
+ it('should reject IPv4 address just above multicast range with TTL', () => {
287
+ // 240.0.0.0 is just above the multicast range (224-239)
288
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP4 240.0.0.0/127'))).toThrow(errors_1.ParseError);
289
+ expect(() => (0, field_parser_1.parseConnectionField)(new scanner_1.Scanner('IN IP4 240.0.0.0/127'))).toThrow('must be a multicast address');
290
+ });
291
+ });
292
+ describe('parseBandwidthField', () => {
293
+ it('should parse CT bandwidth', () => {
294
+ const scanner = new scanner_1.Scanner('CT:1000');
295
+ const bw = (0, field_parser_1.parseBandwidthField)(scanner);
296
+ expect(bw.type).toBe('CT');
297
+ expect(bw.value).toBe(1000);
298
+ });
299
+ it('should parse AS bandwidth', () => {
300
+ const scanner = new scanner_1.Scanner('AS:128');
301
+ const bw = (0, field_parser_1.parseBandwidthField)(scanner);
302
+ expect(bw.type).toBe('AS');
303
+ expect(bw.value).toBe(128);
304
+ });
305
+ it('should parse extension bandwidth type', () => {
306
+ const scanner = new scanner_1.Scanner('TIAS:64000');
307
+ const bw = (0, field_parser_1.parseBandwidthField)(scanner);
308
+ expect(bw.type).toBe('TIAS');
309
+ expect(bw.value).toBe(64000);
310
+ });
311
+ it('should parse zero bandwidth', () => {
312
+ const scanner = new scanner_1.Scanner('AS:0');
313
+ const bw = (0, field_parser_1.parseBandwidthField)(scanner);
314
+ expect(bw.value).toBe(0);
315
+ });
316
+ it('should throw for missing colon', () => {
317
+ expect(() => (0, field_parser_1.parseBandwidthField)(new scanner_1.Scanner('CT 1000'))).toThrow(errors_1.ParseError);
318
+ expect(() => (0, field_parser_1.parseBandwidthField)(new scanner_1.Scanner('CT 1000'))).toThrow('Expected \':\'');
319
+ });
320
+ it('should throw for missing value', () => {
321
+ const scanner = new scanner_1.Scanner('CT:');
322
+ expect(() => (0, field_parser_1.parseBandwidthField)(scanner)).toThrow(errors_1.ParseError);
323
+ });
324
+ it('should throw for negative bandwidth', () => {
325
+ expect(() => (0, field_parser_1.parseBandwidthField)(new scanner_1.Scanner('CT:-100'))).toThrow();
326
+ });
327
+ });
328
+ describe('parseKeyField', () => {
329
+ it('should parse clear key', () => {
330
+ const scanner = new scanner_1.Scanner('clear:mypassword123');
331
+ const key = (0, field_parser_1.parseKeyField)(scanner);
332
+ expect(key).toBe('clear:mypassword123');
333
+ });
334
+ it('should parse base64 key', () => {
335
+ const scanner = new scanner_1.Scanner('base64:dGVzdGtleQ==');
336
+ const key = (0, field_parser_1.parseKeyField)(scanner);
337
+ expect(key).toBe('base64:dGVzdGtleQ==');
338
+ });
339
+ it('should parse uri key', () => {
340
+ const scanner = new scanner_1.Scanner('uri:http://example.com/key');
341
+ const key = (0, field_parser_1.parseKeyField)(scanner);
342
+ expect(key).toBe('uri:http://example.com/key');
343
+ });
344
+ it('should parse prompt key', () => {
345
+ const scanner = new scanner_1.Scanner('prompt');
346
+ const key = (0, field_parser_1.parseKeyField)(scanner);
347
+ expect(key).toBe('prompt');
348
+ });
349
+ it('should stop at newline', () => {
350
+ const scanner = new scanner_1.Scanner('clear:key123\r\n');
351
+ const key = (0, field_parser_1.parseKeyField)(scanner);
352
+ expect(key).toBe('clear:key123');
353
+ });
354
+ });
355
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tests for media-parser.ts
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=media-parser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-parser.test.d.ts","sourceRoot":"","sources":["../../../../tests/unit/parser/media-parser.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+ /**
3
+ * Tests for media-parser.ts
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const scanner_1 = require("../../../src/parser/scanner");
7
+ const media_parser_1 = require("../../../src/parser/media-parser");
8
+ const errors_1 = require("../../../src/types/errors");
9
+ describe('media-parser', () => {
10
+ describe('parseMediaField', () => {
11
+ it('should parse audio media with simple port', () => {
12
+ const scanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0 8 101');
13
+ const media = (0, media_parser_1.parseMediaField)(scanner);
14
+ expect(media.type).toBe('audio');
15
+ expect(media.port.kind).toBe('simple');
16
+ if (media.port.kind === 'simple') {
17
+ expect(media.port.value).toBe(49170);
18
+ }
19
+ expect(media.proto).toBe('RTP/AVP');
20
+ expect(media.formats).toEqual(['0', '8', '101']);
21
+ });
22
+ it('should parse video media', () => {
23
+ const scanner = new scanner_1.Scanner('video 51372 RTP/AVP 99');
24
+ const media = (0, media_parser_1.parseMediaField)(scanner);
25
+ expect(media.type).toBe('video');
26
+ expect(media.port.kind).toBe('simple');
27
+ if (media.port.kind === 'simple') {
28
+ expect(media.port.value).toBe(51372);
29
+ }
30
+ expect(media.proto).toBe('RTP/AVP');
31
+ expect(media.formats).toEqual(['99']);
32
+ });
33
+ it('should parse media with port range', () => {
34
+ const scanner = new scanner_1.Scanner('audio 49170/2 RTP/AVP 0');
35
+ const media = (0, media_parser_1.parseMediaField)(scanner);
36
+ expect(media.type).toBe('audio');
37
+ expect(media.port.kind).toBe('range');
38
+ if (media.port.kind === 'range') {
39
+ expect(media.port.base).toBe(49170);
40
+ expect(media.port.count).toBe(2);
41
+ }
42
+ expect(media.proto).toBe('RTP/AVP');
43
+ expect(media.formats).toEqual(['0']);
44
+ });
45
+ it('should parse text media', () => {
46
+ const scanner = new scanner_1.Scanner('text 49174 RTP/AVP 96');
47
+ const media = (0, media_parser_1.parseMediaField)(scanner);
48
+ expect(media.type).toBe('text');
49
+ });
50
+ it('should parse application media', () => {
51
+ const scanner = new scanner_1.Scanner('application 49175 udp wb');
52
+ const media = (0, media_parser_1.parseMediaField)(scanner);
53
+ expect(media.type).toBe('application');
54
+ expect(media.proto).toBe('udp');
55
+ expect(media.formats).toEqual(['wb']);
56
+ });
57
+ it('should parse message media', () => {
58
+ const scanner = new scanner_1.Scanner('message 49176 RTP/AVP 97');
59
+ const media = (0, media_parser_1.parseMediaField)(scanner);
60
+ expect(media.type).toBe('message');
61
+ });
62
+ it('should parse image media', () => {
63
+ const scanner = new scanner_1.Scanner('image 49177 udp t38');
64
+ const media = (0, media_parser_1.parseMediaField)(scanner);
65
+ expect(media.type).toBe('image');
66
+ expect(media.formats).toEqual(['t38']);
67
+ });
68
+ it('should parse media with port 0 (disabled)', () => {
69
+ const scanner = new scanner_1.Scanner('audio 0 RTP/AVP 0');
70
+ const media = (0, media_parser_1.parseMediaField)(scanner);
71
+ expect(media.port.kind).toBe('simple');
72
+ if (media.port.kind === 'simple') {
73
+ expect(media.port.value).toBe(0);
74
+ }
75
+ });
76
+ it('should parse media with multiple formats', () => {
77
+ const scanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0 8 3 18 101');
78
+ const media = (0, media_parser_1.parseMediaField)(scanner);
79
+ expect(media.formats).toHaveLength(5);
80
+ expect(media.formats).toEqual(['0', '8', '3', '18', '101']);
81
+ });
82
+ it('should parse media with RTP/SAVP protocol', () => {
83
+ const scanner = new scanner_1.Scanner('audio 49170 RTP/SAVP 0');
84
+ const media = (0, media_parser_1.parseMediaField)(scanner);
85
+ expect(media.proto).toBe('RTP/SAVP');
86
+ });
87
+ it('should parse media with RTP/SAVPF protocol', () => {
88
+ const scanner = new scanner_1.Scanner('audio 49170 RTP/SAVPF 111');
89
+ const media = (0, media_parser_1.parseMediaField)(scanner);
90
+ expect(media.proto).toBe('RTP/SAVPF');
91
+ });
92
+ it('should throw for invalid port number', () => {
93
+ expect(() => (0, media_parser_1.parseMediaField)(new scanner_1.Scanner('audio abc RTP/AVP 0'))).toThrow(errors_1.ParseError);
94
+ expect(() => (0, media_parser_1.parseMediaField)(new scanner_1.Scanner('audio abc RTP/AVP 0'))).toThrow('Invalid port number');
95
+ });
96
+ it('should throw for invalid port range', () => {
97
+ expect(() => (0, media_parser_1.parseMediaField)(new scanner_1.Scanner('audio 49170/abc RTP/AVP 0'))).toThrow(errors_1.ParseError);
98
+ expect(() => (0, media_parser_1.parseMediaField)(new scanner_1.Scanner('audio 49170/abc RTP/AVP 0'))).toThrow('Invalid port range');
99
+ });
100
+ it('should throw for missing formats', () => {
101
+ expect(() => (0, media_parser_1.parseMediaField)(new scanner_1.Scanner('audio 49170 RTP/AVP'))).toThrow(errors_1.ParseError);
102
+ expect(() => (0, media_parser_1.parseMediaField)(new scanner_1.Scanner('audio 49170 RTP/AVP'))).toThrow('at least one media format');
103
+ });
104
+ it('should throw for port out of range', () => {
105
+ expect(() => (0, media_parser_1.parseMediaField)(new scanner_1.Scanner('audio 70000 RTP/AVP 0'))).toThrow();
106
+ });
107
+ it('should throw for negative port', () => {
108
+ expect(() => (0, media_parser_1.parseMediaField)(new scanner_1.Scanner('audio -1 RTP/AVP 0'))).toThrow();
109
+ });
110
+ it('should throw for invalid port range format', () => {
111
+ expect(() => (0, media_parser_1.parseMediaField)(new scanner_1.Scanner('audio 49170/2/3 RTP/AVP 0'))).toThrow(errors_1.ParseError);
112
+ expect(() => (0, media_parser_1.parseMediaField)(new scanner_1.Scanner('audio 49170/2/3 RTP/AVP 0'))).toThrow('Invalid port range format');
113
+ });
114
+ });
115
+ describe('parseMediaDescription', () => {
116
+ it('should parse minimal media description (m= only)', () => {
117
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0');
118
+ const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, []);
119
+ expect(md.media.type).toBe('audio');
120
+ expect(md.information).toBeUndefined();
121
+ expect(md.connections).toHaveLength(0);
122
+ expect(md.bandwidths).toHaveLength(0);
123
+ expect(md.key).toBeUndefined();
124
+ expect(md.attributes).toHaveLength(0);
125
+ });
126
+ it('should parse media description with information', () => {
127
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0');
128
+ const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
129
+ { type: 'i', value: 'Audio Stream' },
130
+ ]);
131
+ expect(md.information).toBe('Audio Stream');
132
+ });
133
+ it('should parse media description with connection', () => {
134
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0');
135
+ const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
136
+ { type: 'c', value: 'IN IP4 192.168.1.100' },
137
+ ]);
138
+ expect(md.connections).toHaveLength(1);
139
+ expect(md.connections[0].netType).toBe('IN');
140
+ expect(md.connections[0].addrType).toBe('IP4');
141
+ });
142
+ it('should parse media description with multiple connections', () => {
143
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0');
144
+ const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
145
+ { type: 'c', value: 'IN IP4 192.168.1.100' },
146
+ { type: 'c', value: 'IN IP4 192.168.1.101' },
147
+ ]);
148
+ expect(md.connections).toHaveLength(2);
149
+ });
150
+ it('should parse media description with bandwidth', () => {
151
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0');
152
+ const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
153
+ { type: 'b', value: 'AS:64' },
154
+ ]);
155
+ expect(md.bandwidths).toHaveLength(1);
156
+ expect(md.bandwidths[0].type).toBe('AS');
157
+ expect(md.bandwidths[0].value).toBe(64);
158
+ });
159
+ it('should parse media description with multiple bandwidths', () => {
160
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0');
161
+ const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
162
+ { type: 'b', value: 'AS:64' },
163
+ { type: 'b', value: 'CT:128' },
164
+ ]);
165
+ expect(md.bandwidths).toHaveLength(2);
166
+ });
167
+ it('should parse media description with key', () => {
168
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0');
169
+ const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
170
+ { type: 'k', value: 'clear:mykey123' },
171
+ ]);
172
+ expect(md.key).toBe('clear:mykey123');
173
+ });
174
+ it('should parse media description with attributes', () => {
175
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0 8');
176
+ const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
177
+ { type: 'a', value: 'rtpmap:0 PCMU/8000' },
178
+ { type: 'a', value: 'rtpmap:8 PCMA/8000' },
179
+ { type: 'a', value: 'ptime:20' },
180
+ { type: 'a', value: 'sendrecv' },
181
+ ]);
182
+ expect(md.attributes).toHaveLength(4);
183
+ });
184
+ it('should parse complete media description with all fields', () => {
185
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0');
186
+ const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
187
+ { type: 'i', value: 'Voice' },
188
+ { type: 'c', value: 'IN IP4 192.168.1.100' },
189
+ { type: 'b', value: 'AS:64' },
190
+ { type: 'k', value: 'clear:key' },
191
+ { type: 'a', value: 'rtpmap:0 PCMU/8000' },
192
+ { type: 'a', value: 'sendrecv' },
193
+ ]);
194
+ expect(md.information).toBe('Voice');
195
+ expect(md.connections).toHaveLength(1);
196
+ expect(md.bandwidths).toHaveLength(1);
197
+ expect(md.key).toBe('clear:key');
198
+ expect(md.attributes).toHaveLength(2);
199
+ });
200
+ it('should parse media description with correct field ordering', () => {
201
+ const mediaScanner = new scanner_1.Scanner('video 51372 RTP/AVP 99');
202
+ const md = (0, media_parser_1.parseMediaDescription)(mediaScanner, [
203
+ { type: 'i', value: 'Video Stream' },
204
+ { type: 'c', value: 'IN IP4 192.168.1.100' },
205
+ { type: 'b', value: 'AS:384' },
206
+ { type: 'k', value: 'prompt' },
207
+ { type: 'a', value: 'rtpmap:99 H264/90000' },
208
+ { type: 'a', value: 'fmtp:99 profile-level-id=42e01f' },
209
+ ]);
210
+ expect(md.media.type).toBe('video');
211
+ expect(md.information).toBe('Video Stream');
212
+ });
213
+ it('should throw for duplicate information field', () => {
214
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0');
215
+ expect(() => (0, media_parser_1.parseMediaDescription)(mediaScanner, [
216
+ { type: 'i', value: 'First' },
217
+ { type: 'i', value: 'Second' },
218
+ ])).toThrow(errors_1.ParseError);
219
+ expect(() => (0, media_parser_1.parseMediaDescription)(new scanner_1.Scanner('audio 49170 RTP/AVP 0'), [
220
+ { type: 'i', value: 'First' },
221
+ { type: 'i', value: 'Second' },
222
+ ])).toThrow('Duplicate information field');
223
+ });
224
+ it('should throw for duplicate key field', () => {
225
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0');
226
+ expect(() => (0, media_parser_1.parseMediaDescription)(mediaScanner, [
227
+ { type: 'k', value: 'clear:key1' },
228
+ { type: 'k', value: 'clear:key2' },
229
+ ])).toThrow(errors_1.ParseError);
230
+ expect(() => (0, media_parser_1.parseMediaDescription)(new scanner_1.Scanner('audio 49170 RTP/AVP 0'), [
231
+ { type: 'k', value: 'clear:key1' },
232
+ { type: 'k', value: 'clear:key2' },
233
+ ])).toThrow('Duplicate key field');
234
+ });
235
+ it('should throw for unexpected field type', () => {
236
+ const mediaScanner = new scanner_1.Scanner('audio 49170 RTP/AVP 0');
237
+ expect(() => (0, media_parser_1.parseMediaDescription)(mediaScanner, [{ type: 'v', value: '0' }])).toThrow(errors_1.ParseError);
238
+ expect(() => (0, media_parser_1.parseMediaDescription)(new scanner_1.Scanner('audio 49170 RTP/AVP 0'), [{ type: 'v', value: '0' }])).toThrow('Unexpected field type');
239
+ });
240
+ });
241
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tests for primitive-parser.ts
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=primitive-parser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitive-parser.test.d.ts","sourceRoot":"","sources":["../../../../tests/unit/parser/primitive-parser.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}