util.test.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import { describe, expect, it } from 'vitest'
  2. import { fixSqlBackslashEscapes, selectWeightedKey } from './util'
  3. describe('fixSqlBackslashEscapes', () => {
  4. it('converts backslash-apostrophe to double-apostrophe', () => {
  5. expect(fixSqlBackslashEscapes("INSERT INTO t (c) VALUES ('We\\'ll be in touch')")).toBe(
  6. "INSERT INTO t (c) VALUES ('We''ll be in touch')"
  7. )
  8. })
  9. it('handles multiple backslash-apostrophes in one string', () => {
  10. expect(fixSqlBackslashEscapes("VALUES ('Don\\'t stop, it\\'s fine')")).toBe(
  11. "VALUES ('Don''t stop, it''s fine')"
  12. )
  13. })
  14. it('handles multiple string literals in one query', () => {
  15. expect(fixSqlBackslashEscapes("INSERT INTO t (a, b) VALUES ('We\\'ll', 'It\\'s ready')")).toBe(
  16. "INSERT INTO t (a, b) VALUES ('We''ll', 'It''s ready')"
  17. )
  18. })
  19. it('leaves already-correct double apostrophes unchanged', () => {
  20. const sql = "INSERT INTO t (c) VALUES ('We''ll be in touch')"
  21. expect(fixSqlBackslashEscapes(sql)).toBe(sql)
  22. })
  23. it('leaves SQL with no apostrophes unchanged', () => {
  24. const sql = 'SELECT 1 + 1'
  25. expect(fixSqlBackslashEscapes(sql)).toBe(sql)
  26. })
  27. it('leaves backslash-apostrophe inside dollar-quoted strings unchanged', () => {
  28. const sql = "SELECT $$We\\'ll do it$$ AS greeting"
  29. expect(fixSqlBackslashEscapes(sql)).toBe(sql)
  30. })
  31. it('fixes regular strings but leaves dollar-quoted strings unchanged', () => {
  32. expect(
  33. fixSqlBackslashEscapes("INSERT INTO t (a, b) VALUES ('We\\'ll', $$Don\\'t escape$$)")
  34. ).toBe("INSERT INTO t (a, b) VALUES ('We''ll', $$Don\\'t escape$$)")
  35. })
  36. })
  37. describe('selectWeightedKey', () => {
  38. it('should return a valid key from the weights object', async () => {
  39. const weights = { a: 10, b: 20, c: 30 }
  40. const result = await selectWeightedKey('test-input', weights)
  41. expect(Object.keys(weights)).toContain(result)
  42. })
  43. it('should return consistent results for the same input', async () => {
  44. const weights = { region1: 40, region2: 10, region3: 20 }
  45. const input = 'consistent-key'
  46. const result1 = await selectWeightedKey(input, weights)
  47. const result2 = await selectWeightedKey(input, weights)
  48. const result3 = await selectWeightedKey(input, weights)
  49. expect(result1).toBe(result2)
  50. expect(result2).toBe(result3)
  51. })
  52. it('should distribute keys according to weights', async () => {
  53. const weights = { a: 80, b: 10, c: 10 }
  54. const numSamples = 10000
  55. const samples = Array.from({ length: numSamples }, (_, i) => `sample-${i}`)
  56. const results = await Promise.all(samples.map((sample) => selectWeightedKey(sample, weights)))
  57. const counts = results.reduce<Record<string, number>>((acc, key) => {
  58. acc[key] = (acc[key] ?? 0) + 1
  59. return acc
  60. }, {})
  61. expect(counts.a / numSamples).toBeCloseTo(0.8, 1)
  62. expect(counts.b / numSamples).toBeCloseTo(0.1, 1)
  63. expect(counts.c / numSamples).toBeCloseTo(0.1, 1)
  64. })
  65. it('should handle equal weights', async () => {
  66. const weights = { x: 25, y: 25, z: 25, w: 25 }
  67. const numSamples = 8000
  68. const samples = Array.from({ length: numSamples }, (_, i) => `equal-${i}`)
  69. const results = await Promise.all(samples.map((sample) => selectWeightedKey(sample, weights)))
  70. const counts = results.reduce<Record<string, number>>((acc, key) => {
  71. acc[key] = (acc[key] ?? 0) + 1
  72. return acc
  73. }, {})
  74. // Each key should get roughly 25% of the samples
  75. Object.values(counts).forEach((count) => {
  76. expect(count / numSamples).toBeCloseTo(0.25, 1)
  77. })
  78. })
  79. it('should handle single key', async () => {
  80. const weights = { only: 100 }
  81. const result = await selectWeightedKey('any-input', weights)
  82. expect(result).toBe('only')
  83. })
  84. it('should handle empty string input', async () => {
  85. const weights = { a: 10, b: 20 }
  86. const result = await selectWeightedKey('', weights)
  87. expect(Object.keys(weights)).toContain(result)
  88. })
  89. it('should handle unicode characters in input', async () => {
  90. const weights = { option1: 50, option2: 50 }
  91. const unicodeInput = '🔑-unicode-key-测试'
  92. const result1 = await selectWeightedKey(unicodeInput, weights)
  93. const result2 = await selectWeightedKey(unicodeInput, weights)
  94. expect(result1).toBe(result2)
  95. expect(Object.keys(weights)).toContain(result1)
  96. })
  97. })