StatsManagerTests.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. const { expect } = require('chai')
  2. const { sampleByHash, sampleRequest } = require('../../../app/js/StatsManager')
  3. describe('StatsManager', function () {
  4. describe('sampleByHash', function () {
  5. it('should always return false for a sample percentage of 0', function () {
  6. for (let i = 0; i < 100; i++) {
  7. const key = `test-key-${i}`
  8. expect(sampleByHash(key, 0), `key ${key} should be false`).to.be.false
  9. }
  10. })
  11. it('should always return false for a negative sample percentage', function () {
  12. for (let i = 0; i < 100; i++) {
  13. const key = `test-key-${i}`
  14. expect(sampleByHash(key, -10), `key ${key} should be false`).to.be.false
  15. }
  16. })
  17. it('should always return true for a sample percentage of 100', function () {
  18. // This isn't strictly true, if the hash is exactly 0xffffffff, then the percentile is 100
  19. // and 100 < 100 is false. But the chances of that are 1 in 4 billion.
  20. for (let i = 0; i < 100; i++) {
  21. const key = `test-key-${i}`
  22. expect(sampleByHash(key, 100), `key ${key} should be true`).to.be.true
  23. }
  24. })
  25. it('should return the expected number of results for a sample percentage of 75', function () {
  26. // This isn't strictly true, if the hash is exactly 0xffffffff, then the percentile is 100
  27. // and 100 < 100 is false. But the chances of that are 1 in 4 billion.
  28. let count = 0
  29. for (let i = 0; i < 100; i++) {
  30. const key = `test-key-${i}`
  31. count += sampleByHash(key, 75) ? 1 : 0
  32. }
  33. // Actual result is 74, it's deterministic but the test allows the algorithm to change
  34. expect(count).to.be.within(70, 80)
  35. })
  36. it('should return true when the hash is within the sample percentage', function () {
  37. // The MD5 hash of 'test-key-in' gives a percentile of 13
  38. const key = 'test-key-in'
  39. const percentage = 40
  40. expect(sampleByHash(key, percentage)).to.be.true
  41. })
  42. it('should return false when the hash is outside the sample percentage', function () {
  43. // The MD5 hash of 'test-key-outer' gives a percentile of 47
  44. const key = 'test-key-outer'
  45. const percentage = 40
  46. expect(sampleByHash(key, percentage)).to.be.false
  47. })
  48. it('should produce consistent results for the same key', function () {
  49. const key = 'consistent-key'
  50. const percentage = 50
  51. const result1 = sampleByHash(key, percentage)
  52. const result2 = sampleByHash(key, percentage)
  53. expect(result1).to.equal(result2)
  54. })
  55. it('should handle different keys correctly', function () {
  56. // MD5('key1') => percentile 76
  57. // MD5('key2') => percentile 47
  58. expect(sampleByHash('key1', 80)).to.be.true
  59. expect(sampleByHash('key1', 70)).to.be.false
  60. expect(sampleByHash('key2', 50)).to.be.true
  61. expect(sampleByHash('key2', 40)).to.be.false
  62. })
  63. it('should be monotonic with respect to percentage', function () {
  64. const key = 'test-key'
  65. const percentile = 32
  66. for (let i = 0; i <= 100; i++) {
  67. const result = sampleByHash(key, i)
  68. if (i <= percentile) {
  69. expect(result, `percentage ${i} should be false`).to.be.false
  70. } else {
  71. expect(result, `percentage ${i} should be true`).to.be.true
  72. }
  73. }
  74. })
  75. })
  76. describe('sampleRequest', function () {
  77. it('should return undefined if there is no user_id', function () {
  78. const request = {}
  79. const percentage = 50
  80. expect(sampleRequest(request, percentage)).to.be.undefined
  81. })
  82. it('should return undefined if the path is health-check', function () {
  83. const request = {
  84. user_id: 'some-user',
  85. metricsOpts: { path: 'health-check' },
  86. }
  87. const percentage = 100
  88. expect(sampleRequest(request, percentage)).to.be.undefined
  89. })
  90. it('should return undefined if the path is clsi-perf', function () {
  91. const request = {
  92. user_id: 'some-user',
  93. metricsOpts: { path: 'clsi-perf' },
  94. }
  95. const percentage = 100
  96. expect(sampleRequest(request, percentage)).to.be.undefined
  97. })
  98. it('should return undefined for a health-check even if the user would be sampled', function () {
  99. const request = {
  100. user_id: 'test-key-in', // percentile 13
  101. metricsOpts: { path: 'health-check' },
  102. }
  103. const percentage = 40
  104. expect(sampleRequest(request, percentage)).to.be.undefined
  105. })
  106. it('should return undefined for clsi-perf even if the user would be sampled', function () {
  107. const request = {
  108. user_id: 'test-key-in', // percentile 13
  109. metricsOpts: { path: 'clsi-perf' },
  110. }
  111. const percentage = 40
  112. expect(sampleRequest(request, percentage)).to.be.undefined
  113. })
  114. it('should return undefined if the sampling percentage is 0', function () {
  115. const request = { user_id: 'some-user' }
  116. const percentage = 0
  117. expect(sampleRequest(request, percentage)).to.be.undefined
  118. })
  119. it('should return undefined if the sampling percentage is negative', function () {
  120. const request = { user_id: 'some-user' }
  121. const percentage = -10
  122. expect(sampleRequest(request, percentage)).to.be.undefined
  123. })
  124. it('should sample if there are no metricsOpts', function () {
  125. const request = { user_id: 'test-key-in' } // percentile 13
  126. const percentage = 40
  127. expect(sampleRequest(request, percentage)).to.be.true
  128. })
  129. it('should sample if metricsOpts has no path', function () {
  130. const request = { user_id: 'test-key-in', metricsOpts: {} } // percentile 13
  131. const percentage = 40
  132. expect(sampleRequest(request, percentage)).to.be.true
  133. })
  134. it('should return true for a request that should be sampled', function () {
  135. const request = { user_id: 'test-key-in' } // percentile 13
  136. const percentage = 40
  137. expect(sampleRequest(request, percentage)).to.be.true
  138. })
  139. it('should return false for a request that should not be sampled', function () {
  140. const request = { user_id: 'test-key-outer' } // percentile 47
  141. const percentage = 40
  142. expect(sampleRequest(request, percentage)).to.be.false
  143. })
  144. })
  145. })