diff --git a/src/auth/auth.spec.ts b/src/auth/auth.spec.ts index 4c4c4b9..72ca920 100644 --- a/src/auth/auth.spec.ts +++ b/src/auth/auth.spec.ts @@ -5,6 +5,7 @@ import { decodeToken } from './auth' import { DecodedToken } from '../types' +import { Logger, LogLevel } from '../logger' describe('isAccessTokenExpiring', () => { it('should return true if the token is expiring in an hour', () => { @@ -43,6 +44,56 @@ describe('isAccessTokenExpiring', () => { ).toBeFalsy() }) + it('should route opaque-token debug message through process.logger, not console.debug', () => { + const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {}) + const logger = new Logger(LogLevel.Debug) + const loggerSpy = jest.spyOn(logger, 'debug').mockImplementation(() => {}) + const originalLogger = process.logger + Object.defineProperty(process, 'logger', { + value: logger, + configurable: true, + writable: true + }) + + try { + isAccessTokenExpiring('1f8da55057bd4f50a6577f0bc2b38b1a-r') + expect(loggerSpy).toHaveBeenCalledWith( + 'isTokenExpiring: token is not a decodable JWT, treating it as not expiring.' + ) + expect(debugSpy).not.toHaveBeenCalled() + } finally { + Object.defineProperty(process, 'logger', { + value: originalLogger, + configurable: true, + writable: true + }) + debugSpy.mockRestore() + loggerSpy.mockRestore() + } + }) + + it('should not call console.debug for an opaque token when no logger is set', () => { + const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {}) + const originalLogger = process.logger + Object.defineProperty(process, 'logger', { + value: undefined, + configurable: true, + writable: true + }) + + try { + isAccessTokenExpiring('1f8da55057bd4f50a6577f0bc2b38b1a-r') + expect(debugSpy).not.toHaveBeenCalled() + } finally { + Object.defineProperty(process, 'logger', { + value: originalLogger, + configurable: true, + writable: true + }) + debugSpy.mockRestore() + } + }) + it('should return false for a JWT without an exp claim', () => { expect(isAccessTokenExpiring(generateTokenWithoutExp())).toBeFalsy() }) diff --git a/src/auth/auth.ts b/src/auth/auth.ts index 5629d4c..14cceb0 100644 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -61,7 +61,10 @@ function isTokenExpiring(token: string, timeToLiveSeconds: number) { if (!(err instanceof InvalidTokenError)) throw err // Opaque tokens cannot be expiry-checked client-side. // Assume the token is usable and let the server reject it if expired. - console.debug( + // Route through process.logger so the message respects the configured + // log level and doesn't pollute stdout in CI/test runs. Falls back to a + // no-op when no logger has been installed (e.g. library-only usage). + process.logger?.debug?.( 'isTokenExpiring: token is not a decodable JWT, treating it as not expiring.' ) return false diff --git a/src/types/system/process.d.ts b/src/types/system/process.d.ts new file mode 100644 index 0000000..dde24f8 --- /dev/null +++ b/src/types/system/process.d.ts @@ -0,0 +1,5 @@ +declare namespace NodeJS { + export interface Process { + logger?: import('../../logger/logger').Logger + } +} diff --git a/tsconfig.json b/tsconfig.json index 1104fcb..58d711d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,5 +14,8 @@ "sourceMap": true }, "exclude": ["node_modules", "**/*.spec.ts"], - "include": ["src"] + "include": ["src"], + "ts-node": { + "files": true + } }