this repo has no description
1import assert from 'node:assert'
2import { describe, test } from 'node:test'
3import { TaggedStringGenerator } from '../src/TaggedStringGenerator.ts'
4import { TaggedStringParser } from '../src/TaggedStringParser.ts'
5
6describe('TaggedStringGenerator', () => {
7 describe('basic generation with defaults', () => {
8 test('should generate tag with default delimiters', () => {
9 const generator = new TaggedStringGenerator()
10 const result = generator.tag('operation', 'deploy')
11
12 assert.strictEqual(result, '[operation:deploy]')
13 })
14
15 test('should separate type and value correctly', () => {
16 const generator = new TaggedStringGenerator()
17 const result = generator.tag('count', '5')
18
19 assert.strictEqual(result, '[count:5]')
20 })
21
22 test('should embed tag in message', () => {
23 const generator = new TaggedStringGenerator()
24 const result = generator.embed('Starting ', 'operation', 'deploy')
25
26 assert.strictEqual(result, 'Starting [operation:deploy]')
27 })
28
29 test('should generate multiple independent tags', () => {
30 const generator = new TaggedStringGenerator()
31 const tag1 = generator.tag('operation', 'OP-123')
32 const tag2 = generator.tag('stack', 'ST-456')
33
34 assert.strictEqual(tag1, '[operation:OP-123]')
35 assert.strictEqual(tag2, '[stack:ST-456]')
36 })
37
38 test('should embed multiple tags in message', () => {
39 const generator = new TaggedStringGenerator()
40 const message = generator.embed('', 'operation', 'OP-123')
41 const fullMessage = `${message} started with ${generator.tag('changes', '5')}`
42
43 assert.strictEqual(
44 fullMessage,
45 '[operation:OP-123] started with [changes:5]',
46 )
47 })
48 })
49
50 describe('custom delimiter configurations', () => {
51 test('should use custom openDelimiter', () => {
52 const generator = new TaggedStringGenerator({
53 openDelimiter: '{',
54 })
55 const result = generator.tag('operation', 'deploy')
56
57 assert.strictEqual(result, '{operation:deploy]')
58 })
59
60 test('should use custom closeDelimiter', () => {
61 const generator = new TaggedStringGenerator({
62 closeDelimiter: '}',
63 })
64 const result = generator.tag('operation', 'deploy')
65
66 assert.strictEqual(result, '[operation:deploy}')
67 })
68
69 test('should use custom typeSeparator', () => {
70 const generator = new TaggedStringGenerator({
71 typeSeparator: '=',
72 })
73 const result = generator.tag('operation', 'deploy')
74
75 assert.strictEqual(result, '[operation=deploy]')
76 })
77
78 test('should use all custom delimiters together', () => {
79 const generator = new TaggedStringGenerator({
80 openDelimiter: '{{',
81 closeDelimiter: '}}',
82 typeSeparator: '|',
83 })
84 const result = generator.tag('operation', 'deploy')
85
86 assert.strictEqual(result, '{{operation|deploy}}')
87 })
88
89 test('should use custom single-character delimiters', () => {
90 const generator = new TaggedStringGenerator({
91 openDelimiter: '<',
92 closeDelimiter: '>',
93 })
94 const result = generator.tag('operation', 'OP-123')
95
96 assert.strictEqual(result, '<operation:OP-123>')
97 })
98
99 test('should use custom multi-character delimiters', () => {
100 const generator = new TaggedStringGenerator({
101 openDelimiter: '<<<',
102 closeDelimiter: '>>>',
103 })
104 const result = generator.tag('operation', 'OP-123')
105
106 assert.strictEqual(result, '<<<operation:OP-123>>>')
107 })
108 })
109
110 describe('value type conversion', () => {
111 test('should convert number to string', () => {
112 const generator = new TaggedStringGenerator()
113 const result = generator.tag('count', 42)
114
115 assert.strictEqual(result, '[count:42]')
116 })
117
118 test('should convert boolean to string', () => {
119 const generator = new TaggedStringGenerator()
120 const resultTrue = generator.tag('enabled', true)
121 const resultFalse = generator.tag('disabled', false)
122
123 assert.strictEqual(resultTrue, '[enabled:true]')
124 assert.strictEqual(resultFalse, '[disabled:false]')
125 })
126
127 test('should convert object to string', () => {
128 const generator = new TaggedStringGenerator()
129 const result = generator.tag('data', { key: 'value' })
130
131 assert.strictEqual(result, '[data:[object Object]]')
132 })
133
134 test('should convert array to string', () => {
135 const generator = new TaggedStringGenerator()
136 const result = generator.tag('items', [1, 2, 3])
137
138 assert.strictEqual(result, '[items:1,2,3]')
139 })
140
141 test('should handle null value', () => {
142 const generator = new TaggedStringGenerator()
143 const result = generator.tag('value', null)
144
145 assert.strictEqual(result, '[value:null]')
146 })
147
148 test('should handle undefined value', () => {
149 const generator = new TaggedStringGenerator()
150 const result = generator.tag('value', undefined)
151
152 assert.strictEqual(result, '[value:undefined]')
153 })
154
155 test('should convert negative numbers', () => {
156 const generator = new TaggedStringGenerator()
157 const result = generator.tag('temp', -5)
158
159 assert.strictEqual(result, '[temp:-5]')
160 })
161
162 test('should convert decimal numbers', () => {
163 const generator = new TaggedStringGenerator()
164 const result = generator.tag('price', 19.99)
165
166 assert.strictEqual(result, '[price:19.99]')
167 })
168 })
169
170 describe('edge cases', () => {
171 test('should handle empty type string', () => {
172 const generator = new TaggedStringGenerator()
173 const result = generator.tag('', 'value')
174
175 assert.strictEqual(result, '[:value]')
176 })
177
178 test('should handle empty value string', () => {
179 const generator = new TaggedStringGenerator()
180 const result = generator.tag('type', '')
181
182 assert.strictEqual(result, '[type:]')
183 })
184
185 test('should handle special characters in values', () => {
186 const generator = new TaggedStringGenerator()
187 const result1 = generator.tag('message', 'hello world!')
188 const result2 = generator.tag('path', '/usr/bin/node')
189 const result3 = generator.tag('email', 'user@example.com')
190
191 assert.strictEqual(result1, '[message:hello world!]')
192 assert.strictEqual(result2, '[path:/usr/bin/node]')
193 assert.strictEqual(result3, '[email:user@example.com]')
194 })
195
196 test('should preserve whitespace in values', () => {
197 const generator = new TaggedStringGenerator()
198 const result = generator.tag('message', ' spaces ')
199
200 assert.strictEqual(result, '[message: spaces ]')
201 })
202
203 test('should handle delimiter characters in values', () => {
204 const generator = new TaggedStringGenerator()
205 const result = generator.tag('message', 'value [with] brackets')
206
207 assert.strictEqual(result, '[message:value [with] brackets]')
208 })
209
210 test('should handle type separator in values', () => {
211 const generator = new TaggedStringGenerator()
212 const result = generator.tag('ratio', '3:2')
213
214 assert.strictEqual(result, '[ratio:3:2]')
215 })
216
217 test('should handle newlines in values', () => {
218 const generator = new TaggedStringGenerator()
219 const result = generator.tag('message', 'line1\nline2')
220
221 assert.strictEqual(result, '[message:line1\nline2]')
222 })
223 })
224
225 describe('parser compatibility', () => {
226 test('should generate tags parseable with default config', () => {
227 const generator = new TaggedStringGenerator()
228 const parser = new TaggedStringParser()
229
230 const tagged = generator.tag('operation', 'deploy')
231 const result = parser.parse(tagged)
232
233 assert.strictEqual(result.entities.length, 1)
234 assert.strictEqual(result.entities[0].type, 'operation')
235 assert.strictEqual(result.entities[0].value, 'deploy')
236 })
237
238 test('should generate tags parseable with custom delimiters', () => {
239 const config = {
240 openDelimiter: '{{',
241 closeDelimiter: '}}',
242 typeSeparator: '|',
243 }
244 const generator = new TaggedStringGenerator(config)
245 const parser = new TaggedStringParser(config)
246
247 const tagged = generator.tag('operation', 'deploy')
248 const result = parser.parse(tagged)
249
250 assert.strictEqual(result.entities.length, 1)
251 assert.strictEqual(result.entities[0].type, 'operation')
252 assert.strictEqual(result.entities[0].value, 'deploy')
253 })
254
255 test('should verify parsed entities match original type and value', () => {
256 const generator = new TaggedStringGenerator()
257 const parser = new TaggedStringParser()
258
259 const type = 'count'
260 const value = 42
261 const tagged = generator.tag(type, value)
262 const result = parser.parse(tagged)
263
264 assert.strictEqual(result.entities[0].type, type)
265 assert.strictEqual(result.entities[0].value, String(value))
266 assert.strictEqual(result.entities[0].parsedValue, value)
267 })
268
269 test('should support round-trip with multiple entities', () => {
270 const generator = new TaggedStringGenerator()
271 const parser = new TaggedStringParser()
272
273 const message =
274 generator.embed('', 'operation', 'OP-123') +
275 ' started with ' +
276 generator.tag('changes', 5)
277 const result = parser.parse(message)
278
279 assert.strictEqual(result.entities.length, 2)
280 assert.strictEqual(result.entities[0].type, 'operation')
281 assert.strictEqual(result.entities[0].value, 'OP-123')
282 assert.strictEqual(result.entities[1].type, 'changes')
283 assert.strictEqual(result.entities[1].value, '5')
284 })
285
286 test('should support round-trip with custom single-character delimiters', () => {
287 const config = {
288 openDelimiter: '<',
289 closeDelimiter: '>',
290 }
291 const generator = new TaggedStringGenerator(config)
292 const parser = new TaggedStringParser(config)
293
294 const tagged = generator.tag('operation', 'OP-123')
295 const result = parser.parse(tagged)
296
297 assert.strictEqual(result.entities.length, 1)
298 assert.strictEqual(result.entities[0].type, 'operation')
299 assert.strictEqual(result.entities[0].value, 'OP-123')
300 })
301
302 test('should support round-trip with boolean values', () => {
303 const generator = new TaggedStringGenerator()
304 const parser = new TaggedStringParser()
305
306 const tagged = generator.tag('enabled', true)
307 const result = parser.parse(tagged)
308
309 assert.strictEqual(result.entities[0].type, 'enabled')
310 assert.strictEqual(result.entities[0].value, 'true')
311 assert.strictEqual(result.entities[0].parsedValue, true)
312 })
313 })
314
315 describe('configuration validation', () => {
316 test('should throw error for empty openDelimiter', () => {
317 assert.throws(
318 () => new TaggedStringGenerator({ openDelimiter: '' }),
319 /openDelimiter cannot be empty/,
320 )
321 })
322
323 test('should throw error for empty closeDelimiter', () => {
324 assert.throws(
325 () => new TaggedStringGenerator({ closeDelimiter: '' }),
326 /closeDelimiter cannot be empty/,
327 )
328 })
329
330 test('should throw error for identical delimiters', () => {
331 assert.throws(
332 () =>
333 new TaggedStringGenerator({
334 openDelimiter: '|',
335 closeDelimiter: '|',
336 }),
337 /openDelimiter and closeDelimiter must be different/,
338 )
339 })
340
341 test('should allow empty typeSeparator', () => {
342 const generator = new TaggedStringGenerator({ typeSeparator: '' })
343 const result = generator.tag('operation', 'deploy')
344
345 assert.strictEqual(result, '[operationdeploy]')
346 })
347 })
348})