diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0836858..64ac8d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,5 +43,6 @@ jobs: with: test_service_port: ${{ env.TEST_SERVICE_PORT }} token: ${{ secrets.GITHUB_TOKEN }} + extra_params: '--skip-from=${{ github.workspace }}/contract-tests/suppressions.txt' - name: Build Docs run: npm run doc diff --git a/contract-tests/TestHook.js b/contract-tests/TestHook.js new file mode 100644 index 0000000..5a371e9 --- /dev/null +++ b/contract-tests/TestHook.js @@ -0,0 +1,61 @@ +let postChain = Promise.resolve(); + +function safePost(url, body) { + postChain = postChain.then(() => + fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }).catch(() => {}) + ); +} + +class TestHook { + constructor(name, endpoint, data, errors) { + this.hookName = name; + this.endpoint = endpoint; + this.hookData = data || {}; + this.hookErrors = errors || {}; + } + + getMetadata() { + return { name: this.hookName }; + } + + beforeEvaluation(hookContext, data) { + if (this.hookErrors.beforeEvaluation) { + throw new Error(this.hookErrors.beforeEvaluation); + } + safePost(this.endpoint, { + evaluationSeriesContext: hookContext, + evaluationSeriesData: data, + stage: 'beforeEvaluation', + }); + return { ...data, ...(this.hookData.beforeEvaluation || {}) }; + } + + afterEvaluation(hookContext, data, detail) { + if (this.hookErrors.afterEvaluation) { + throw new Error(this.hookErrors.afterEvaluation); + } + safePost(this.endpoint, { + evaluationSeriesContext: hookContext, + evaluationSeriesData: data, + evaluationDetail: detail, + stage: 'afterEvaluation', + }); + return { ...data, ...(this.hookData.afterEvaluation || {}) }; + } + + afterTrack(hookContext) { + if (this.hookErrors.afterTrack) { + throw new Error(this.hookErrors.afterTrack); + } + safePost(this.endpoint, { + trackSeriesContext: hookContext, + stage: 'afterTrack', + }); + } +} + +module.exports = { TestHook }; diff --git a/contract-tests/index.js b/contract-tests/index.js index 2b7124c..95fd057 100644 --- a/contract-tests/index.js +++ b/contract-tests/index.js @@ -25,8 +25,11 @@ app.get('/', (req, res) => { 'tags', 'user-type', 'inline-context', - 'anonymous-redaction', + 'inline-context-all', 'client-prereq-events', + 'client-per-context-summaries', + 'evaluation-hooks', + 'track-hooks', ], }); }); diff --git a/contract-tests/sdkClientEntity.js b/contract-tests/sdkClientEntity.js index 8aacb62..1947f2f 100644 --- a/contract-tests/sdkClientEntity.js +++ b/contract-tests/sdkClientEntity.js @@ -1,6 +1,7 @@ const ld = require('launchdarkly-node-client-sdk'); const { Log, sdkLogger } = require('./log'); +const { TestHook } = require('./TestHook'); const badCommandError = new Error('unsupported command'); @@ -60,6 +61,12 @@ function makeSdkConfig(options, tag) { }; } + if (options.hooks) { + cf.hooks = options.hooks.hooks.map( + hook => new TestHook(hook.name, hook.callbackUri, hook.data, hook.errors) + ); + } + return cf; } diff --git a/contract-tests/suppressions.txt b/contract-tests/suppressions.txt new file mode 100644 index 0000000..140061f --- /dev/null +++ b/contract-tests/suppressions.txt @@ -0,0 +1,2 @@ +hooks/evaluation/executes beforeEvaluation hooks in registration order +hooks/track/executes afterTrack hooks in registration order diff --git a/package.json b/package.json index 6985d8d..9f6c48c 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ }, "dependencies": { "launchdarkly-eventsource": "2.0.3", - "launchdarkly-js-sdk-common": "5.4.0", + "launchdarkly-js-sdk-common": "5.8.1", "node-localstorage": "^1.3.1" }, "repository": { diff --git a/src/__tests__/LDClient-events-test.js b/src/__tests__/LDClient-events-test.js index c69ace7..d6833a9 100644 --- a/src/__tests__/LDClient-events-test.js +++ b/src/__tests__/LDClient-events-test.js @@ -30,7 +30,7 @@ describe('LDClient', () => { const trackEvent = events[1]; expect(trackEvent.kind).toEqual('custom'); expect(trackEvent.key).toEqual('eventkey'); - expect(trackEvent.contextKeys).toEqual({ user: 'user' }); + expect(trackEvent.context).toEqual({ kind: 'user', key: 'user' }); expect(trackEvent.data).toEqual(data); expect(trackEvent.url).toEqual(null); });