this repo has no description
1import assert from 'node:assert'
2import { describe, test } from 'node:test'
3import { TaggedStringParser } from '../../src/TaggedStringParser.ts'
4
5// Helper to access private method for testing
6type ParserWithExtractQuotedString = {
7 extractQuotedString: (
8 message: string,
9 startPos: number,
10 ) => { content: string; endPosition: number } | null
11}
12
13describe('Quoted String Extraction', () => {
14 describe('basic extraction', () => {
15 test('should extract basic quoted string', () => {
16 const parser = new TaggedStringParser()
17 const extract = (
18 parser as unknown as ParserWithExtractQuotedString
19 ).extractQuotedString.bind(parser)
20
21 const result = extract('"hello world"', 0)
22 assert.notStrictEqual(result, null)
23 assert.strictEqual(result?.content, 'hello world')
24 assert.strictEqual(result?.endPosition, 13)
25 })
26
27 test('should extract quoted string with spaces', () => {
28 const parser = new TaggedStringParser()
29 const extract = (
30 parser as unknown as ParserWithExtractQuotedString
31 ).extractQuotedString.bind(parser)
32
33 const result = extract('"store order"', 0)
34 assert.notStrictEqual(result, null)
35 assert.strictEqual(result?.content, 'store order')
36 assert.strictEqual(result?.endPosition, 13)
37 })
38
39 test('should extract quoted string with special characters', () => {
40 const parser = new TaggedStringParser()
41 const extract = (
42 parser as unknown as ParserWithExtractQuotedString
43 ).extractQuotedString.bind(parser)
44
45 const result = extract('"hello:world=test"', 0)
46 assert.notStrictEqual(result, null)
47 assert.strictEqual(result?.content, 'hello:world=test')
48 assert.strictEqual(result?.endPosition, 18)
49 })
50
51 test('should extract quoted string from middle of larger string', () => {
52 const parser = new TaggedStringParser()
53 const extract = (
54 parser as unknown as ParserWithExtractQuotedString
55 ).extractQuotedString.bind(parser)
56
57 const result = extract('key="quoted value" rest', 4)
58 assert.notStrictEqual(result, null)
59 assert.strictEqual(result?.content, 'quoted value')
60 assert.strictEqual(result?.endPosition, 18)
61 })
62
63 test('should handle empty quoted string', () => {
64 const parser = new TaggedStringParser()
65 const extract = (
66 parser as unknown as ParserWithExtractQuotedString
67 ).extractQuotedString.bind(parser)
68
69 const result = extract('""', 0)
70 assert.notStrictEqual(result, null)
71 assert.strictEqual(result?.content, '')
72 assert.strictEqual(result?.endPosition, 2)
73 })
74 })
75
76 describe('escape sequences', () => {
77 test('should process escaped quote (\\") as literal quote', () => {
78 const parser = new TaggedStringParser()
79 const extract = (
80 parser as unknown as ParserWithExtractQuotedString
81 ).extractQuotedString.bind(parser)
82
83 const result = extract('"say \\"hello\\""', 0)
84 assert.notStrictEqual(result, null)
85 assert.strictEqual(result?.content, 'say "hello"')
86 })
87
88 test('should process escaped backslash (\\\\) as literal backslash', () => {
89 const parser = new TaggedStringParser()
90 const extract = (
91 parser as unknown as ParserWithExtractQuotedString
92 ).extractQuotedString.bind(parser)
93
94 const result = extract('"path\\\\to\\\\file"', 0)
95 assert.notStrictEqual(result, null)
96 assert.strictEqual(result?.content, 'path\\to\\file')
97 })
98
99 test('should process mixed escape sequences', () => {
100 const parser = new TaggedStringParser()
101 const extract = (
102 parser as unknown as ParserWithExtractQuotedString
103 ).extractQuotedString.bind(parser)
104
105 const result = extract('"test\\\\\\"mixed\\""', 0)
106 assert.notStrictEqual(result, null)
107 assert.strictEqual(result?.content, 'test\\"mixed"')
108 })
109
110 test('should handle backslash before non-escapable character', () => {
111 const parser = new TaggedStringParser()
112 const extract = (
113 parser as unknown as ParserWithExtractQuotedString
114 ).extractQuotedString.bind(parser)
115
116 const result = extract('"test\\nvalue"', 0)
117 assert.notStrictEqual(result, null)
118 assert.strictEqual(result?.content, 'test\\nvalue')
119 })
120 })
121
122 describe('error cases', () => {
123 test('should return null for unclosed quote', () => {
124 const parser = new TaggedStringParser()
125 const extract = (
126 parser as unknown as ParserWithExtractQuotedString
127 ).extractQuotedString.bind(parser)
128
129 const result = extract('"unclosed string', 0)
130 assert.strictEqual(result, null)
131 })
132
133 test('should return null for unclosed quote at end of string', () => {
134 const parser = new TaggedStringParser()
135 const extract = (
136 parser as unknown as ParserWithExtractQuotedString
137 ).extractQuotedString.bind(parser)
138
139 const result = extract('"no closing', 0)
140 assert.strictEqual(result, null)
141 })
142
143 test('should handle backslash at end of quoted string', () => {
144 const parser = new TaggedStringParser()
145 const extract = (
146 parser as unknown as ParserWithExtractQuotedString
147 ).extractQuotedString.bind(parser)
148
149 const result = extract('"ends with\\"', 0)
150 assert.strictEqual(result, null)
151 })
152
153 test('should return null when not starting at a quote', () => {
154 const parser = new TaggedStringParser()
155 const extract = (
156 parser as unknown as ParserWithExtractQuotedString
157 ).extractQuotedString.bind(parser)
158
159 const result = extract('not a quote', 0)
160 assert.strictEqual(result, null)
161 })
162 })
163
164 describe('quoted strings in delimited mode', () => {
165 test('should extract quoted key: ["linux server"=home]', () => {
166 const parser = new TaggedStringParser({ typeSeparator: '=' })
167 const result = parser.parse('["linux server"=home]')
168
169 assert.strictEqual(result.entities.length, 1)
170 assert.strictEqual(result.entities[0].type, 'linux server')
171 assert.strictEqual(result.entities[0].value, 'home')
172 assert.strictEqual(result.entities[0].parsedValue, 'home')
173 })
174
175 test('should extract quoted value: [server="web server"]', () => {
176 const parser = new TaggedStringParser({ typeSeparator: '=' })
177 const result = parser.parse('[server="web server"]')
178
179 assert.strictEqual(result.entities.length, 1)
180 assert.strictEqual(result.entities[0].type, 'server')
181 assert.strictEqual(result.entities[0].value, 'web server')
182 assert.strictEqual(result.entities[0].parsedValue, 'web server')
183 })
184
185 test('should extract both quoted key and quoted value', () => {
186 const parser = new TaggedStringParser({ typeSeparator: '=' })
187 const result = parser.parse('["linux server"="web server"]')
188
189 assert.strictEqual(result.entities.length, 1)
190 assert.strictEqual(result.entities[0].type, 'linux server')
191 assert.strictEqual(result.entities[0].value, 'web server')
192 assert.strictEqual(result.entities[0].parsedValue, 'web server')
193 })
194
195 test('should handle escape sequences in quoted key: ["key\\"name"=value]', () => {
196 const parser = new TaggedStringParser({ typeSeparator: '=' })
197 const result = parser.parse('["key\\"name"=value]')
198
199 assert.strictEqual(result.entities.length, 1)
200 assert.strictEqual(result.entities[0].type, 'key"name')
201 assert.strictEqual(result.entities[0].value, 'value')
202 })
203
204 test('should handle escape sequences in quoted value: [key="say \\"hello\\""]', () => {
205 const parser = new TaggedStringParser({ typeSeparator: '=' })
206 const result = parser.parse('[key="say \\"hello\\""]')
207
208 assert.strictEqual(result.entities.length, 1)
209 assert.strictEqual(result.entities[0].type, 'key')
210 assert.strictEqual(result.entities[0].value, 'say "hello"')
211 })
212
213 test('should handle backslash escape sequences: [path="C:\\\\Users\\\\file"]', () => {
214 const parser = new TaggedStringParser({ typeSeparator: '=' })
215 const result = parser.parse('[path="C:\\\\Users\\\\file"]')
216
217 assert.strictEqual(result.entities.length, 1)
218 assert.strictEqual(result.entities[0].type, 'path')
219 assert.strictEqual(result.entities[0].value, 'C:\\Users\\file')
220 })
221
222 test('should handle mixed escape sequences in delimited mode', () => {
223 const parser = new TaggedStringParser({ typeSeparator: '=' })
224 const result = parser.parse('[msg="test\\\\\\"mixed\\""]')
225
226 assert.strictEqual(result.entities.length, 1)
227 assert.strictEqual(result.entities[0].type, 'msg')
228 assert.strictEqual(result.entities[0].value, 'test\\"mixed"')
229 })
230
231 test('should skip malformed tag with unclosed quoted key', () => {
232 const parser = new TaggedStringParser({ typeSeparator: '=' })
233 const result = parser.parse('["unclosed key=value] [valid=123]')
234
235 assert.strictEqual(result.entities.length, 1)
236 assert.strictEqual(result.entities[0].type, 'valid')
237 assert.strictEqual(result.entities[0].value, '123')
238 })
239
240 test('should skip malformed tag with unclosed quoted value', () => {
241 const parser = new TaggedStringParser({ typeSeparator: '=' })
242 const result = parser.parse('[key="unclosed value] [valid=123]')
243
244 assert.strictEqual(result.entities.length, 1)
245 assert.strictEqual(result.entities[0].type, 'valid')
246 assert.strictEqual(result.entities[0].value, '123')
247 })
248
249 test('should handle quoted keys with spaces and special characters', () => {
250 const parser = new TaggedStringParser({ typeSeparator: ':' })
251 const result = parser.parse('["store order":"number 42"]')
252
253 assert.strictEqual(result.entities.length, 1)
254 assert.strictEqual(result.entities[0].type, 'store order')
255 assert.strictEqual(result.entities[0].value, 'number 42')
256 })
257
258 test('should handle quoted values with separator character', () => {
259 const parser = new TaggedStringParser({ typeSeparator: '=' })
260 const result = parser.parse('[formula="a=b+c"]')
261
262 assert.strictEqual(result.entities.length, 1)
263 assert.strictEqual(result.entities[0].type, 'formula')
264 assert.strictEqual(result.entities[0].value, 'a=b+c')
265 })
266
267 test('should work with custom delimiters and quoted strings', () => {
268 const parser = new TaggedStringParser({
269 delimiters: ['{{', '}}'],
270 typeSeparator: '=',
271 })
272 const result = parser.parse('{{"linux server"="web server"}}')
273
274 assert.strictEqual(result.entities.length, 1)
275 assert.strictEqual(result.entities[0].type, 'linux server')
276 assert.strictEqual(result.entities[0].value, 'web server')
277 })
278
279 test('should handle multiple entities with quoted strings', () => {
280 const parser = new TaggedStringParser({ typeSeparator: '=' })
281 const result = parser.parse(
282 '["server name"="web server"] [count=5] ["status"="active"]',
283 )
284
285 assert.strictEqual(result.entities.length, 3)
286 assert.strictEqual(result.entities[0].type, 'server name')
287 assert.strictEqual(result.entities[0].value, 'web server')
288 assert.strictEqual(result.entities[1].type, 'count')
289 assert.strictEqual(result.entities[1].value, '5')
290 assert.strictEqual(result.entities[2].type, 'status')
291 assert.strictEqual(result.entities[2].value, 'active')
292 })
293 })
294})