Skip to content

Commit

Permalink
chore: Migrate block of unit tests to node:test (#2570)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsumners-nr authored Sep 16, 2024
1 parent 5e6825b commit 5cd1d8a
Show file tree
Hide file tree
Showing 4 changed files with 479 additions and 581 deletions.
260 changes: 124 additions & 136 deletions test/unit/adaptive-sampler.test.js
Original file line number Diff line number Diff line change
@@ -1,163 +1,151 @@
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

const tap = require('tap')
const test = require('node:test')
const assert = require('node:assert')
const sinon = require('sinon')

const helper = require('../lib/agent_helper')
const AdaptiveSampler = require('../../lib/adaptive-sampler')
const sinon = require('sinon')

tap.test('AdaptiveSampler', (t) => {
let sampler = null
const shared = {
'should count the number of traces sampled': (t) => {
t.equal(sampler.sampled, 0)
t.ok(sampler.shouldSample(0.1234))
t.equal(sampler.sampled, 1)
t.end()
},

'should not sample transactions with priorities lower than the min': (t) => {
t.equal(sampler.sampled, 0)
sampler._samplingThreshold = 0.5
t.notOk(sampler.shouldSample(0))
t.equal(sampler.sampled, 0)
t.ok(sampler.shouldSample(1))
t.equal(sampler.sampled, 1)
t.end()
},

'should adjust the min priority when throughput increases': (t) => {
sampler._reset(sampler.samplingTarget)
sampler._seen = 2 * sampler.samplingTarget
sampler._adjustStats(sampler.samplingTarget)
t.equal(sampler.samplingThreshold, 0.5)
t.end()
},

'should only take the first 10 on the first harvest': (t) => {
t.equal(sampler.samplingThreshold, 0)

// Change this to maxSampled if we change the way the back off works.
for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) {
sampler.shouldSample(0.99999999)
}

t.equal(sampler.sampled, 10)
t.equal(sampler.samplingThreshold, 1)
t.end()
},

'should backoff on sampling after reaching the sampled target': (t) => {
sampler._seen = 10 * sampler.samplingTarget

// Flag the sampler as not in the first period
sampler._reset()

// The minimum sampled priority is not adjusted until the `target` number of
// transactions have been sampled, this is why the first 10 checks are all
// 0.9. At that point the current count of seen transactions should be close
// to the previous period's transaction count.
//
// In this test, however, the seen for this period is small compared the
// previous period (10 vs 100). This causes the MSP to drop to 0.3 but
// quickly normalizes again. This is an artifact of the test's use of infinite
// priority transactions in order to make the test predictable.
const epsilon = 0.000001
const expectedMSP = [
0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.316227766016838, 0.5500881229337736,
0.6957797474657306, 0.7910970452225743, 0.8559144986383691, 0.9013792551037068,
0.9340820391176599, 0.9580942670418969, 0.976025777575764, 0.9896031249412947, 1.0
]

// Change this to maxSampled if we change the way the back off works.
for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) {
const expected = expectedMSP[i]
t.ok(
sampler.samplingThreshold >= expected - epsilon &&
sampler.samplingThreshold <= expected + epsilon
)

sampler.shouldSample(Infinity)
}
t.end()
const shared = {
'should count the number of traces sampled': (t) => {
const { sampler } = t.nr
assert.equal(sampler.sampled, 0)
assert.ok(sampler.shouldSample(0.1234))
assert.equal(sampler.sampled, 1)
},

'should not sample transactions with priorities lower than the min': (t) => {
const { sampler } = t.nr
assert.equal(sampler.sampled, 0)
sampler._samplingThreshold = 0.5
assert.equal(sampler.shouldSample(0), false)
assert.equal(sampler.sampled, 0)
assert.ok(sampler.shouldSample(1))
assert.equal(sampler.sampled, 1)
},

'should adjust the min priority when throughput increases': (t) => {
const { sampler } = t.nr
sampler._reset(sampler.samplingTarget)
sampler._seen = 2 * sampler.samplingTarget
sampler._adjustStats(sampler.samplingTarget)
assert.equal(sampler.samplingThreshold, 0.5)
},

'should only take the first 10 on the first harvest': (t) => {
const { sampler } = t.nr
assert.equal(sampler.samplingThreshold, 0)

// Change this to maxSampled if we change the way the back off works.
for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) {
sampler.shouldSample(0.99999999)
}
}

t.test('in serverless mode', (t) => {
let agent = null
t.beforeEach(() => {
agent = helper.loadMockedAgent({
serverless_mode: {
enabled: true
}
})
sampler = agent.transactionSampler
})

t.afterEach(() => {
helper.unloadAgent(agent)
sampler = null
})
assert.equal(sampler.sampled, 10)
assert.equal(sampler.samplingThreshold, 1)
},

'should backoff on sampling after reaching the sampled target': (t) => {
const { sampler } = t.nr
sampler._seen = 10 * sampler.samplingTarget

// Flag the sampler as not in the first period
sampler._reset()

// The minimum sampled priority is not adjusted until the `target` number of
// transactions have been sampled, this is why the first 10 checks are all
// 0.9. At that point the current count of seen transactions should be close
// to the previous period's transaction count.
//
// In this test, however, the seen for this period is small compared the
// previous period (10 vs 100). This causes the MSP to drop to 0.3 but
// quickly normalizes again. This is an artifact of the test's use of infinite
// priority transactions in order to make the test predictable.
const epsilon = 0.000001
const expectedMSP = [
0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.316227766016838, 0.5500881229337736,
0.6957797474657306, 0.7910970452225743, 0.8559144986383691, 0.9013792551037068,
0.9340820391176599, 0.9580942670418969, 0.976025777575764, 0.9896031249412947, 1.0
]

// Change this to maxSampled if we change the way the back off works.
for (let i = 0; i <= 2 * sampler.samplingTarget; ++i) {
const expected = expectedMSP[i]
assert.ok(
sampler.samplingThreshold >= expected - epsilon &&
sampler.samplingThreshold <= expected + epsilon
)

sampler.shouldSample(Infinity)
}
}
}

Object.getOwnPropertyNames(shared).forEach((testName) => {
t.test(testName, shared[testName])
test('in serverless mode', async (t) => {
t.beforeEach((ctx) => {
ctx.nr = {}
ctx.nr.agent = helper.loadMockedAgent({
serverless_mode: { enabled: true }
})

t.test(
'should reset itself after a transaction outside the window has been created',
async (t) => {
const spy = sinon.spy(sampler, '_reset')
sampler.samplingPeriod = 50
t.equal(spy.callCount, 0)
agent.emit('transactionStarted', { timer: { start: Date.now() } })
t.equal(spy.callCount, 1)

return new Promise((resolve) => {
setTimeout(() => {
t.equal(spy.callCount, 1)
agent.emit('transactionStarted', { timer: { start: Date.now() } })
t.equal(spy.callCount, 2)
resolve()
}, 100)
})
}
)
t.end()
ctx.nr.sampler = ctx.nr.agent.transactionSampler
})

t.test('in standard mode', (t) => {
t.beforeEach(() => {
sampler = new AdaptiveSampler({
period: 100,
target: 10
})
})

t.afterEach(() => {
sampler.samplePeriod = 0 // Clear sample interval.
})
t.afterEach((ctx) => {
helper.unloadAgent(ctx.nr.agent)
})

Object.getOwnPropertyNames(shared).forEach((testName) => {
t.test(testName, shared[testName])
})
for (const [name, fn] of Object.entries(shared)) {
await t.test(name, fn)
}

t.test('should reset itself according to the period', async (t) => {
await t.test(
'should reset itself after a transaction outside the window has been created',
async (t) => {
const { agent, sampler } = t.nr
const spy = sinon.spy(sampler, '_reset')
sampler.samplingPeriod = 50
assert.equal(spy.callCount, 0)
agent.emit('transactionStarted', { timer: { start: Date.now() } })
assert.equal(spy.callCount, 1)

return new Promise((resolve) => {
await new Promise((resolve) => {
setTimeout(() => {
t.equal(spy.callCount, 4)
assert.equal(spy.callCount, 1)
agent.emit('transactionStarted', { timer: { start: Date.now() } })
assert.equal(spy.callCount, 2)
resolve()
}, 235)
}, 100)
})
}
)
})

test('in standard mode', async (t) => {
t.beforeEach((ctx) => {
ctx.nr = {}
ctx.nr.sampler = new AdaptiveSampler({ period: 100, target: 10 })
})

for (const [name, fn] of Object.entries(shared)) {
await t.test(name, fn)
}

await t.test('should reset itself according to the period', async (t) => {
const { sampler } = t.nr
const spy = sinon.spy(sampler, '_reset')
sampler.samplingPeriod = 50

await new Promise((resolve) => {
setTimeout(() => {
assert.equal(spy.callCount, 4)
resolve()
}, 235)
})
t.end()
})
t.end()
})
Loading

0 comments on commit 5cd1d8a

Please sign in to comment.