Skip to content

Commit e098151

Browse files
authored
Upgrade to latest node-server openfeature SDK and provider timeout test (#6907)
* Upgrade to latest node-server openfeature SDK and provider timeout test
1 parent 6d86211 commit e098151

File tree

4 files changed

+193
-17
lines changed

4 files changed

+193
-17
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@
127127
"@datadog/native-appsec": "10.3.0",
128128
"@datadog/native-iast-taint-tracking": "4.0.0",
129129
"@datadog/native-metrics": "3.1.1",
130-
"@datadog/openfeature-node-server": "0.1.0-preview.15",
130+
"@datadog/openfeature-node-server": "^0.2.0",
131131
"@datadog/pprof": "5.12.0",
132132
"@datadog/sketches-js": "2.1.1",
133133
"@datadog/wasm-js-rewriter": "5.0.1",
@@ -167,7 +167,7 @@
167167
"@eslint/js": "^9.39.0",
168168
"@msgpack/msgpack": "^3.1.2",
169169
"@openfeature/core": "^1.9.0",
170-
"@openfeature/server-sdk": "^1.20.0",
170+
"@openfeature/server-sdk": "~1.20.0",
171171
"@stylistic/eslint-plugin": "^5.5.0",
172172
"@types/chai": "^4.3.16",
173173
"@types/mocha": "^10.0.10",
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
'use strict'
2+
3+
const { expect } = require('chai')
4+
const { describe, it, beforeEach, afterEach } = require('mocha')
5+
const sinon = require('sinon')
6+
const proxyquire = require('proxyquire')
7+
const { ProviderEvents } = require('@openfeature/server-sdk')
8+
9+
require('../setup/mocha')
10+
11+
describe('FlaggingProvider Initialization Timeout', () => {
12+
let FlaggingProvider
13+
let mockTracer
14+
let mockConfig
15+
let mockChannel
16+
let log
17+
let channelStub
18+
let clock
19+
20+
beforeEach(() => {
21+
// Use fake timers to control setTimeout
22+
clock = sinon.useFakeTimers()
23+
24+
mockTracer = {
25+
_config: { service: 'test-service' }
26+
}
27+
28+
mockConfig = {
29+
service: 'test-service',
30+
version: '1.0.0',
31+
env: 'test'
32+
}
33+
34+
mockChannel = {
35+
publish: sinon.spy(),
36+
hasSubscribers: false
37+
}
38+
39+
channelStub = sinon.stub().returns(mockChannel)
40+
41+
log = {
42+
debug: sinon.spy(),
43+
error: sinon.spy(),
44+
warn: sinon.spy()
45+
}
46+
47+
FlaggingProvider = proxyquire('../../src/openfeature/flagging_provider', {
48+
'dc-polyfill': {
49+
channel: channelStub
50+
},
51+
'../log': log
52+
})
53+
})
54+
55+
afterEach(() => {
56+
// Restore real timers
57+
clock.restore()
58+
})
59+
60+
it('should timeout after 30 seconds when configuration is not set', async () => {
61+
const provider = new FlaggingProvider(mockTracer, mockConfig)
62+
63+
// Start initialization (returns a promise)
64+
const initPromise = provider.initialize()
65+
66+
// Attach catch handler BEFORE ticking clock to prevent unhandled rejection
67+
initPromise.catch(() => {
68+
// Expected to reject on timeout
69+
})
70+
71+
// Verify initialization is in progress
72+
expect(provider.initController).to.exist
73+
expect(provider.initController.isInitializing()).to.be.true
74+
75+
// Advance time by 30 seconds (default timeout) and run pending promises
76+
await clock.tickAsync(30000)
77+
78+
// Wait for promise to settle
79+
await initPromise.catch(() => {})
80+
81+
// Verify initialization is no longer in progress
82+
expect(provider.initController.isInitializing()).to.be.false
83+
})
84+
85+
it('should not timeout if configuration is set before 30 seconds', async () => {
86+
const provider = new FlaggingProvider(mockTracer, mockConfig)
87+
88+
// Start initialization
89+
const initPromise = provider.initialize()
90+
91+
// Verify initialization is in progress
92+
expect(provider.initController.isInitializing()).to.be.true
93+
94+
// Advance time by 20 seconds (before timeout)
95+
await clock.tickAsync(20000)
96+
97+
// Set configuration before timeout
98+
const ufc = {
99+
flags: {
100+
'test-flag': {
101+
key: 'test-flag',
102+
variations: [
103+
{ key: 'on', value: true },
104+
{ key: 'off', value: false }
105+
]
106+
}
107+
}
108+
}
109+
provider._setConfiguration(ufc)
110+
111+
// Wait for initialization to complete
112+
await initPromise
113+
114+
// Verify initialization completed successfully
115+
expect(provider.initController.isInitializing()).to.be.false
116+
expect(provider.getConfiguration()).to.equal(ufc)
117+
})
118+
119+
it('should call setError with timeout message after 30 seconds', async () => {
120+
const provider = new FlaggingProvider(mockTracer, mockConfig)
121+
122+
// Spy on setError method
123+
const setErrorSpy = sinon.spy(provider, 'setError')
124+
125+
const initPromise = provider.initialize()
126+
127+
// Attach catch handler BEFORE ticking clock to prevent unhandled rejection
128+
initPromise.catch(() => {
129+
// Expected to reject
130+
})
131+
132+
// Advance time to trigger timeout
133+
await clock.tickAsync(30000)
134+
135+
await initPromise.catch(() => {})
136+
137+
// Verify setError was called with timeout error
138+
expect(setErrorSpy).to.have.been.calledOnce
139+
const errorArg = setErrorSpy.firstCall.args[0]
140+
expect(errorArg).to.be.instanceOf(Error)
141+
expect(errorArg.message).to.include('Initialization timeout')
142+
expect(errorArg.message).to.include('30000ms')
143+
})
144+
145+
it('should allow recovery if configuration is set after timeout', async () => {
146+
const provider = new FlaggingProvider(mockTracer, mockConfig)
147+
148+
// Spy on event emitter
149+
const readyEventSpy = sinon.spy()
150+
provider.events.addHandler(ProviderEvents.Ready, readyEventSpy)
151+
152+
const initPromise = provider.initialize()
153+
154+
// Attach catch handler BEFORE ticking clock to prevent unhandled rejection
155+
initPromise.catch(() => {
156+
// Expected to reject
157+
})
158+
159+
// Trigger timeout
160+
await clock.tickAsync(30000)
161+
162+
// Wait for initialization to complete/fail
163+
await initPromise.catch(() => {})
164+
165+
// Configuration is still not set
166+
expect(provider.getConfiguration()).to.be.undefined
167+
168+
// Now set configuration after timeout
169+
const ufc = { flags: { 'recovery-flag': {} } }
170+
provider._setConfiguration(ufc)
171+
172+
// Should emit READY event to signal recovery
173+
expect(readyEventSpy).to.have.been.calledOnce
174+
expect(provider.getConfiguration()).to.equal(ufc)
175+
})
176+
})

packages/dd-trace/test/plugins/versions/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"@azure/service-bus": "7.9.5",
2727
"@confluentinc/kafka-javascript": "1.6.0",
2828
"@cucumber/cucumber": "12.2.0",
29-
"@datadog/openfeature-node-server": "0.1.0-preview.15",
29+
"@datadog/openfeature-node-server": "0.2.0",
3030
"@elastic/elasticsearch": "9.2.0",
3131
"@elastic/transport": "9.2.1",
3232
"@fastify/cookie": "11.0.2",

yarn.lock

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,10 @@
204204
"@babel/helper-string-parser" "^7.27.1"
205205
"@babel/helper-validator-identifier" "^7.27.1"
206206

207-
"@datadog/flagging-core@0.1.0-preview.15":
208-
version "0.1.0-preview.15"
209-
resolved "https://registry.yarnpkg.com/@datadog/flagging-core/-/flagging-core-0.1.0-preview.15.tgz#bd8c21a7a7e96428c584d5b6f103b8011fa4f847"
210-
integrity sha512-p6YhX7lHOlD3IhyBerL2OM7EEmOLA0MmjgIvEUX61cbRiK983m51rF8b8zXyiCCp/tdfBSmmsujtPwjMwXGiug==
207+
"@datadog/flagging-core@0.2.0":
208+
version "0.2.0"
209+
resolved "https://registry.yarnpkg.com/@datadog/flagging-core/-/flagging-core-0.2.0.tgz#d29d81486068b870c41cebb2524721a71184eb16"
210+
integrity sha512-DocYjr8NFJZfsnqzu3MuBUNqgbJquq1doimkKEXI5UImLmz/tR0LO09dd651pBhoz1Pa4SKcnPaxWFLTqxWtsA==
211211
dependencies:
212212
spark-md5 "^3.0.2"
213213

@@ -238,12 +238,12 @@
238238
node-addon-api "^6.1.0"
239239
node-gyp-build "^3.9.0"
240240

241-
"@datadog/openfeature-node-server@0.1.0-preview.15":
242-
version "0.1.0-preview.15"
243-
resolved "https://registry.yarnpkg.com/@datadog/openfeature-node-server/-/openfeature-node-server-0.1.0-preview.15.tgz#f9ec9f9614aefd9c03e83c62ea57c6163843a053"
244-
integrity sha512-SXWseSmtEg+mKB2WzCzT5f/m61GEmFNBLchBaPO5w6dECk4jz4g6BXmfLTVdFZXi26/M644niZWHpv9cJVhoqg==
241+
"@datadog/openfeature-node-server@^0.2.0":
242+
version "0.2.0"
243+
resolved "https://registry.yarnpkg.com/@datadog/openfeature-node-server/-/openfeature-node-server-0.2.0.tgz#905032cc6d7d1eb1d6620aabe323ed552366d65a"
244+
integrity sha512-Jn8lShFyqNji0tiKntLcCRwu3xrhg93fTTJS3gHSPKMfBDBKxjB9uF8Hu5Ayn2k0QuwJexx5atN9GSiWX0h5qg==
245245
dependencies:
246-
"@datadog/flagging-core" "0.1.0-preview.15"
246+
"@datadog/flagging-core" "0.2.0"
247247
"@openfeature/server-sdk" "~1.18.0"
248248

249249
"@datadog/pprof@5.12.0":
@@ -681,16 +681,16 @@
681681
resolved "https://registry.yarnpkg.com/@openfeature/core/-/core-1.9.1.tgz#9925a04ed0745e92dd7b3793b35cff1ed89d54c1"
682682
integrity sha512-YySPtH4s/rKKnHRU0xyFGrqMU8XA+OIPNWDrlEFxE6DCVWCIrxE5YpiB94YD2jMFn6SSdA0cwQ8vLkCkl8lm8A==
683683

684-
"@openfeature/server-sdk@^1.20.0":
685-
version "1.20.0"
686-
resolved "https://registry.yarnpkg.com/@openfeature/server-sdk/-/server-sdk-1.20.0.tgz#43a5d3cffd915742ff64eeef53b7132279df4823"
687-
integrity sha512-95L9CCaGVKC6+7rNCmDxjthFvUyPLOUkoYyjg9Y/Cmy0G9D63xrJBsmH6fqHtLifzAu4+E7fWAZ3TIBnnCtr7A==
688-
689684
"@openfeature/server-sdk@~1.18.0":
690685
version "1.18.0"
691686
resolved "https://registry.yarnpkg.com/@openfeature/server-sdk/-/server-sdk-1.18.0.tgz#5fed5f1d0900b2535878db18a78295bbaebb997b"
692687
integrity sha512-uP8nqEGBS58s3iWXx6d8vnJ6ZVt3DACJL4PWADOAuwIS4xXpID91783e9f6zQ0i1ijQpj3yx+3ZuCB2LfQMUMA==
693688

689+
"@openfeature/server-sdk@~1.20.0":
690+
version "1.20.0"
691+
resolved "https://registry.yarnpkg.com/@openfeature/server-sdk/-/server-sdk-1.20.0.tgz#43a5d3cffd915742ff64eeef53b7132279df4823"
692+
integrity sha512-95L9CCaGVKC6+7rNCmDxjthFvUyPLOUkoYyjg9Y/Cmy0G9D63xrJBsmH6fqHtLifzAu4+E7fWAZ3TIBnnCtr7A==
693+
694694
"@opentelemetry/api-logs@<1.0.0":
695695
version "0.208.0"
696696
resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.208.0.tgz#56d3891010a1fa1cf600ba8899ed61b43ace511c"

0 commit comments

Comments
 (0)