Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions packages/node/jest.config.js
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use vitest instead of Jest.
Lets utilize that instead.
https://github.com/asgardeo/javascript/blob/52d5d7b987856e4896ea61f58d8d0f37115c82f8/packages/node/vitest.config.ts

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also check the Build failure and the Type Check.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

export default {
preset: 'ts-jest/presets/default-esm',
testEnvironment: 'node',
extensionsToTreatAsEsm: ['.ts'],
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
useESM: true,
tsconfig: {
module: 'ESNext',
moduleResolution: 'node',
esModuleInterop: true,
},
},
],
},
testMatch: ['**/__tests__/**/*.test.ts', '**/__tests__/**/*.spec.ts'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.test.ts',
'!src/**/*.spec.ts',
'!src/__tests__/**',
'!src/__legacy__/**',
'!src/index.ts',
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'html', 'lcov'],
coverageThreshold: {
global: {
lines: 80,
functions: 60,
branches: 80,
statements: 80,
},
},
};
9 changes: 6 additions & 3 deletions packages/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,22 @@
"clean": "rimraf dist",
"fix:lint": "eslint . --ext .js,.jsx,.ts,.tsx,.cjs,.mjs",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx,.cjs,.mjs",
"test": "vitest",
"test": "jest",
"test:coverage": "jest --coverage",
"typecheck": "tsc -p tsconfig.lib.json"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/node": "^22.15.3",
"@wso2/eslint-plugin": "catalog:",
"@wso2/prettier-config": "catalog:",
"esbuild": "^0.25.9",
"eslint": "8.57.0",
"jest": "^29.7.0",
"prettier": "^2.6.2",
"rimraf": "^6.0.1",
"typescript": "~5.7.2",
"vitest": "^3.1.3"
"ts-jest": "^29.2.5",
"typescript": "~5.7.2"
},
"dependencies": {
"@asgardeo/javascript": "workspace:^",
Expand Down
53 changes: 53 additions & 0 deletions packages/node/src/__tests__/constants/CookieConfig.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import CookieConfig from '../../constants/CookieConfig';

/**
* Test suite for CookieConfig constants
* Verifies that all cookie configuration constants are correctly defined
*/
describe('CookieConfig', () => {
describe('cookie name constants', () => {
it('should have correct SESSION_COOKIE_NAME', () => {
expect(CookieConfig.SESSION_COOKIE_NAME).toBeDefined();
expect(CookieConfig.SESSION_COOKIE_NAME).toContain('session');
expect(typeof CookieConfig.SESSION_COOKIE_NAME).toBe('string');
});

it('should have correct TEMP_SESSION_COOKIE_NAME', () => {
expect(CookieConfig.TEMP_SESSION_COOKIE_NAME).toBeDefined();
expect(CookieConfig.TEMP_SESSION_COOKIE_NAME).toContain('temp.session');
expect(typeof CookieConfig.TEMP_SESSION_COOKIE_NAME).toBe('string');
});
});

describe('default configuration values', () => {
it('should have DEFAULT_MAX_AGE of 3600', () => {
expect(CookieConfig.DEFAULT_MAX_AGE).toBe(3600);
expect(typeof CookieConfig.DEFAULT_MAX_AGE).toBe('number');
});

it('should have DEFAULT_HTTP_ONLY as true', () => {
expect(CookieConfig.DEFAULT_HTTP_ONLY).toBe(true);
expect(typeof CookieConfig.DEFAULT_HTTP_ONLY).toBe('boolean');
});

it('should have DEFAULT_SAME_SITE as lax', () => {
expect(CookieConfig.DEFAULT_SAME_SITE).toBe('lax');
expect(typeof CookieConfig.DEFAULT_SAME_SITE).toBe('string');
});

it('should have DEFAULT_SECURE as true', () => {
expect(CookieConfig.DEFAULT_SECURE).toBe(true);
expect(typeof CookieConfig.DEFAULT_SECURE).toBe('boolean');
});
});

describe('class design', () => {
it('should not be instantiable due to private constructor', () => {
// This test documents the design intent that CookieConfig cannot be instantiated
// TypeScript prevents instantiation at compile time with: new CookieConfig()
// We verify the constructor exists and the class is properly defined
expect(CookieConfig.constructor).toBeDefined();
expect(typeof CookieConfig).toBe('function');
});
});
});
33 changes: 33 additions & 0 deletions packages/node/src/__tests__/fixtures/cookieOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {CookieOptions} from '../../models/cookies';

/**
* Default cookie options matching CookieConfig defaults
*/
export const defaultCookieOptions: CookieOptions = {
httpOnly: true,
maxAge: 3600,
sameSite: 'lax',
secure: true,
};

/**
* Custom cookie options with non-default values
*/
export const customCookieOptions: CookieOptions = {
httpOnly: false,
maxAge: 7200,
sameSite: 'strict',
secure: false,
};

/**
* Partial cookie options for testing merging behavior
*/
export const partialCookieOptions: Partial<CookieOptions> = {
maxAge: 1800,
};

/**
* All possible sameSite variations for testing
*/
export const sameSiteVariations = ['strict' as const, 'lax' as const, 'none' as const, true, false];
59 changes: 59 additions & 0 deletions packages/node/src/__tests__/helpers/mockHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {jest} from '@jest/globals';

type SpyInstance = ReturnType<typeof jest.spyOn>;

/**
* Mocks Date constructor to return a specific timestamp
*
* @param timestamp - The timestamp value to return from new Date().getTime()
* @returns Jest spy object for the mocked Date constructor
*
* @example
* ```ts
* const spy = mockDateNow(1234567890);
* expect(new Date().getTime()).toBe(1234567890);
* spy.mockRestore();
* ```
*/
export const mockDateNow = (timestamp: number): SpyInstance => {
const spy = jest.spyOn(global, 'Date').mockImplementation(
() =>
({
getTime: () => timestamp,
} as any),
);
return spy;
};

/**
* Mocks Math.random() to return a specific value
*
* @param value - The value to return from Math.random() (should be between 0 and 1)
* @returns Jest spy object for the mocked Math.random()
*
* @example
* ```ts
* const spy = mockMathRandom(0.5);
* expect(Math.random()).toBe(0.5);
* spy.mockRestore();
* ```
*/
export const mockMathRandom = (value: number): SpyInstance => {
const spy = jest.spyOn(Math, 'random');
spy.mockReturnValue(value);
return spy;
};

/**
* Restores all mocks to their original implementations
*
* @example
* ```ts
* afterEach(() => {
* restoreAllMocks();
* });
* ```
*/
export const restoreAllMocks = (): void => {
jest.restoreAllMocks();
};
81 changes: 81 additions & 0 deletions packages/node/src/__tests__/utils/generateSessionId.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import generateSessionId from '../../utils/generateSessionId';
import {mockDateNow, mockMathRandom, restoreAllMocks} from '../helpers/mockHelpers';

/**
* Test suite for generateSessionId utility function
* Tests the generation of unique session identifiers
*/
describe('generateSessionId', () => {
afterEach(() => {
restoreAllMocks();
});

describe('basic functionality', () => {
it('should return a string', () => {
const sessionId = generateSessionId();

expect(typeof sessionId).toBe('string');
expect(sessionId.length).toBeGreaterThan(0);
});
});

describe('uniqueness', () => {
it('should generate unique IDs on multiple calls', () => {
const ids = new Set<string>();
const iterations = 1000;

for (let i = 0; i < iterations; i++) {
ids.add(generateSessionId());
}

expect(ids.size).toBe(iterations);
});

it('should generate different IDs in rapid succession', () => {
const id1 = generateSessionId();
const id2 = generateSessionId();
const id3 = generateSessionId();

expect(id1).not.toBe(id2);
expect(id2).not.toBe(id3);
expect(id1).not.toBe(id3);
});
});

describe('format and composition', () => {
it('should contain timestamp component', () => {
const mockTimestamp = 1234567890000;
const expectedTimestampPart = mockTimestamp.toString(36);
mockDateNow(mockTimestamp);
mockMathRandom(0.5);

const sessionId = generateSessionId();

expect(sessionId).toContain(expectedTimestampPart);
});

it('should contain random component', () => {
const mockRandomValue = 0.123456789;
const expectedRandomPart = mockRandomValue.toString(36).substring(2);
mockDateNow(1000000);
mockMathRandom(mockRandomValue);

const sessionId = generateSessionId();

expect(sessionId).toContain(expectedRandomPart);
});

it('should combine timestamp and random components', () => {
const mockTimestamp = 1609459200000;
const mockRandomValue = 0.987654321;
const expectedTimestampPart = mockTimestamp.toString(36);
const expectedRandomPart = mockRandomValue.toString(36).substring(2);
mockDateNow(mockTimestamp);
mockMathRandom(mockRandomValue);

const sessionId = generateSessionId();

expect(sessionId).toBe(expectedTimestampPart + expectedRandomPart);
});
});
});
Loading
Loading