this repo has no description
at main 348 lines 12 kB view raw
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})