diff --git a/changelog.md b/changelog.md index c5eeaf0..9220155 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 2025-01-25 - 2.2.0 - feat(tests) +Added a new test script to demonstrate and validate AsyncContext functionality + +- Introduced jstrial.js in the test directory to ensure the correct working of AsyncContext. +- The new tests handle various scenarios, including the independence of child stores and data deletion. + ## 2025-01-23 - 2.1.8 - fix(core) Refactor and clean up class imports and exports diff --git a/test/jstrial.js b/test/jstrial.js new file mode 100644 index 0000000..749f2f4 --- /dev/null +++ b/test/jstrial.js @@ -0,0 +1,110 @@ +import { tap, expect } from '@push.rocks/tapbundle'; + +import * as asynccontext from '../dist_ts/index.js'; + + +if (typeof process !== 'undefined') { + // process.env.DEBUG = 'true'; +} + +/** + * This test file demonstrates how to use the AsyncContext and ensures + * that runScoped() properly creates child AsyncStore contexts and merges parent data. + */ + +const asyncContext = new asynccontext.AsyncContext(); + +tap.test('should run a scoped function and add data to a child store', async () => { + // add some default data to the parent store + asyncContext.store.add('parentKey', 'parentValue'); + expect(asyncContext.store.get('parentKey')).toEqual('parentValue'); + + // now run a child scope, add some data, and check that parent's data is still accessible + await asyncContext.runScoped(async () => { + asyncContext.store.add('childKey', 'childValue'); + + // child should see its own data + expect(asyncContext.store.get('childKey')).toEqual('childValue'); + // child should also see parent data + expect(asyncContext.store.get('parentKey')).toEqual('parentValue'); + }); +}); + +tap.test('should not contaminate the parent store with child-only data', async () => { + // create a new child scope + await asyncContext.runScoped(async () => { + asyncContext.store.add('temporaryKey', 'temporaryValue'); + expect(asyncContext.store.get('temporaryKey')).toEqual('temporaryValue'); + }); + // after the child scope finishes, 'temporaryKey' should not exist in the parent + expect(asyncContext.store.get('temporaryKey')).toBeUndefined(); +}); + +tap.test('should allow adding data in multiple scopes independently', async (toolsArg) => { + const done = toolsArg.cumulativeDefer(); + + // add data in first scope + asyncContext.runScoped(async () => { + const subDone = done.subDefer(); + asyncContext.store.add('childKey1', 'childValue1-v1'); + await toolsArg.delayFor(2000); + expect(asyncContext.store.get('childKey1')).toEqual('childValue1-v1'); + subDone.resolve(); + }); + + asyncContext.runScoped(async () => { + const subDone = done.subDefer(); + asyncContext.store.add('childKey1', 'childValue1-v2'); + await toolsArg.delayFor(1000); + expect(asyncContext.store.get('childKey1')).toEqual('childValue1-v2'); + subDone.resolve(); + }); + + // add data in second scope + asyncContext.runScoped(async () => { + asyncContext.store.add('childKey2', 'childValue2'); + expect(asyncContext.store.get('childKey2')).toEqual('childValue2'); + }); + + // neither childKey1 nor childKey2 should exist in the parent store + expect(asyncContext.store.get('childKey1')).toBeUndefined(); + expect(asyncContext.store.get('childKey2')).toBeUndefined(); + await done.promise; +}); + +tap.test( + 'should allow deleting data in a child store without removing it from the parent store', + async (toolsArg) => { + // ensure parent has some data + asyncContext.store.add('deletableKey', 'iShouldStayInParent'); + + await asyncContext.runScoped(async () => { + // child sees the parent's data + expect(asyncContext.store.get('deletableKey')).toEqual('iShouldStayInParent'); + // attempt to delete it in the child + asyncContext.store.delete('deletableKey'); + // child no longer sees it + expect(asyncContext.store.get('deletableKey')).toBeUndefined(); + // but parent still has it + }); + expect(asyncContext.store.get('deletableKey')).toEqual('iShouldStayInParent'); + } +); + +tap.test('should allow multiple child scopes to share the same parent store data', async () => { + // add a key to the parent store + asyncContext.store.add('sharedKey', 'sharedValue'); + expect(asyncContext.store.get('sharedKey')).toEqual('sharedValue'); + + // first child scope + await asyncContext.runScoped(async () => { + expect(asyncContext.store.get('sharedKey')).toEqual('sharedValue'); + }); + + // second child scope + await asyncContext.runScoped(async () => { + expect(asyncContext.store.get('sharedKey')).toEqual('sharedValue'); + }); +}); + +export default tap.start(); diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 48495b2..2f47eb9 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartcontext', - version: '2.1.8', + version: '2.2.0', description: 'A module providing advanced asynchronous context management to enrich logs with context and manage scope effectively in Node.js applications.' }