Initial Commit
All checks were successful
Publish to Private NPM Registry / publish (push) Successful in 31s
All checks were successful
Publish to Private NPM Registry / publish (push) Successful in 31s
This commit is contained in:
commit
6895111f69
13 changed files with 4107 additions and 0 deletions
158
tests/BaseKeystore.test.ts
Normal file
158
tests/BaseKeystore.test.ts
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import type { Request, Response, NextFunction } from 'express';
|
||||
import { jest, describe, test, expect, beforeEach } from '@jest/globals';
|
||||
|
||||
import type { BaseKeystore as BaseKeystoreType } from '../src/BaseKeystore.js';
|
||||
|
||||
// Mock the logging library
|
||||
jest.unstable_mockModule('@BridgemanAccessible/ba-logging', () => ({
|
||||
logMessage: jest.fn(),
|
||||
LogLevel: {
|
||||
DEBUG: 'DEBUG',
|
||||
ERROR: 'ERROR'
|
||||
}
|
||||
}));
|
||||
|
||||
describe('BaseKeystore', () => {
|
||||
/** Env management pattern (save/restore existing environment variables) */
|
||||
const OLD_ENV = process.env;
|
||||
|
||||
/** Instance of the BaseKeystore class (needed to access static members like WELL_KNOWN_URI) */
|
||||
let BaseKeystore: typeof BaseKeystoreType;
|
||||
/** Instance of the concrete subclass for testing (Note, the typing gets kind of weird here because of loading mechanics - see `beforeEach` block) */
|
||||
let keystoreInstance: InstanceType<typeof BaseKeystore> & { setInternalKeystore: (ks: any) => void };
|
||||
/** Mock Express request object */
|
||||
let mockReq: Partial<Request & { /* Make path NOT read-only */ path: string }>;
|
||||
/** Mock Express response object */
|
||||
let mockRes: Partial<Response>;
|
||||
/** Mock Express next function */
|
||||
let mockNext: NextFunction;
|
||||
/** Mock Node-Jose keystore */
|
||||
let mockJoseStore: any;
|
||||
/** Mock logging module */
|
||||
let mockLogging: any;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Make a copy of the existing environment variables to restore after tests
|
||||
// (important for not affecting other tests that might run in the same process)
|
||||
process.env = { ...OLD_ENV };
|
||||
|
||||
// Reset Process Env
|
||||
process.env.DEBUG_INTERSERVICE_COMMS = 'false';
|
||||
|
||||
// Because of ESM stuff and mocking out dependencies,
|
||||
// we need to load the BaseKeystore class asynchronously in the beforeEach block.
|
||||
// Instead of at the top level of the test file.
|
||||
const module = await import('../src/BaseKeystore.js');
|
||||
BaseKeystore = module.BaseKeystore;
|
||||
|
||||
// Concrete implementation for testing abstract class
|
||||
// Note, the definition needs to be done here in the beforeEach block after we load the BaseKeystore class,
|
||||
// because it needs to extend the BaseKeystore class that we load here.
|
||||
class TestKeystore extends BaseKeystore {
|
||||
// Helper to manually set the internal keystore for testing
|
||||
public setInternalKeystore(ks: any) {
|
||||
this.keystore = ks;
|
||||
}
|
||||
}
|
||||
|
||||
keystoreInstance = new TestKeystore();
|
||||
|
||||
// Mock Express objects
|
||||
mockReq = { path: '/' };
|
||||
mockRes = {
|
||||
status: jest.fn<(...args: any[]) => Response>().mockReturnThis(),
|
||||
json: jest.fn<(...args: any[]) => Response>().mockReturnThis(),
|
||||
};
|
||||
mockNext = jest.fn();
|
||||
|
||||
// Mock Node-Jose Store
|
||||
mockJoseStore = {
|
||||
toJSON: jest.fn().mockReturnValue({ keys: [] }),
|
||||
get: jest.fn()
|
||||
};
|
||||
|
||||
mockLogging = await import('@BridgemanAccessible/ba-logging');
|
||||
});
|
||||
|
||||
describe('getKeystore()', () => {
|
||||
it('should throw error if keystore is null', () => {
|
||||
expect(() => keystoreInstance.getKeystore()).toThrow('Keystore not ready');
|
||||
});
|
||||
|
||||
it('should return the keystore if initialized', () => {
|
||||
keystoreInstance.setInternalKeystore(mockJoseStore);
|
||||
expect(keystoreInstance.getKeystore()).toBe(mockJoseStore);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getKey()', () => {
|
||||
it('should call get on the underlying node-jose store', () => {
|
||||
keystoreInstance.setInternalKeystore(mockJoseStore);
|
||||
keystoreInstance.getKey('key-id', { kty: 'RSA' });
|
||||
expect(mockJoseStore.get).toHaveBeenCalledWith('key-id', { kty: 'RSA' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('middleware()', () => {
|
||||
it('should call next() immediately if path is not .well-known URI', () => {
|
||||
mockReq.path = '/some/other/path';
|
||||
keystoreInstance.middleware(mockReq as Request, mockRes as Response, mockNext);
|
||||
|
||||
expect(mockNext).toHaveBeenCalled();
|
||||
expect(mockRes.status).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call next(Error) if accessing .well-known but keystore is not ready', () => {
|
||||
mockReq.path = BaseKeystore.WELL_KNOWN_URI;
|
||||
|
||||
keystoreInstance.middleware(mockReq as Request, mockRes as Response, mockNext);
|
||||
|
||||
expect(mockNext).toHaveBeenCalledWith(expect.any(Error));
|
||||
expect(mockNext).toHaveBeenCalledWith(expect.objectContaining({ message: 'Keystore not ready' }));
|
||||
});
|
||||
|
||||
it('should return 200 and JSON public keys if ready', () => {
|
||||
mockReq.path = BaseKeystore.WELL_KNOWN_URI;
|
||||
keystoreInstance.setInternalKeystore(mockJoseStore);
|
||||
|
||||
keystoreInstance.middleware(mockReq as Request, mockRes as Response, mockNext);
|
||||
|
||||
expect(mockRes.status).toHaveBeenCalledWith(200);
|
||||
expect(mockRes.json).toHaveBeenCalledWith({ keys: [] });
|
||||
|
||||
// Ensure private keys are NOT exported (toJSON should be called with false or no args)
|
||||
expect(mockJoseStore.toJSON).toHaveBeenCalled();
|
||||
expect(mockNext).not.toHaveBeenCalled(); // Should stop processing
|
||||
});
|
||||
|
||||
it('should log debug messages when env var is set', () => {
|
||||
process.env.DEBUG_INTERSERVICE_COMMS = 'true';
|
||||
|
||||
mockReq.path = BaseKeystore.WELL_KNOWN_URI;
|
||||
keystoreInstance.setInternalKeystore(mockJoseStore);
|
||||
|
||||
keystoreInstance.middleware(mockReq as Request, mockRes as Response, mockNext);
|
||||
|
||||
expect(mockLogging.logMessage).toHaveBeenCalledTimes(2); // Start of middleware + JSON log
|
||||
expect(mockLogging.logMessage).toHaveBeenCalledWith(expect.stringContaining('Keystore middleware called'), 'DEBUG');
|
||||
});
|
||||
|
||||
it('should log error messages when env var is set and keystore fails', () => {
|
||||
process.env.DEBUG_INTERSERVICE_COMMS = 'true';
|
||||
|
||||
mockReq.path = BaseKeystore.WELL_KNOWN_URI;
|
||||
// Keystore is NOT set here
|
||||
|
||||
keystoreInstance.middleware(mockReq as Request, mockRes as Response, mockNext);
|
||||
|
||||
expect(mockLogging.logMessage).toHaveBeenCalledWith('Keystore not ready', 'ERROR');
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
// Restore original environment variables after all tests are done
|
||||
process.env = OLD_ENV;
|
||||
})
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue