From 8b032c38901323b736876710d26c50af3596eadc Mon Sep 17 00:00:00 2001 From: lorentz Date: Wed, 15 Jul 2026 14:27:47 -0400 Subject: [PATCH] feat(17-01): add isMimecastConfigured() config gate + test seam - Add isMimecastConfigured() to lib/services/mimecast-client.ts mirroring the pax8-factory.ts isConfigured() convention - Add _resetMimecastClient() test seam so tests can isolate env-var state - Add lib/services/mimecast-client.test.ts covering config gate + throw/cache behavior of getMimecastClient() --- lib/services/mimecast-client.test.ts | 63 ++++++++++++++++++++++++++++ lib/services/mimecast-client.ts | 10 +++++ 2 files changed, 73 insertions(+) create mode 100644 lib/services/mimecast-client.test.ts diff --git a/lib/services/mimecast-client.test.ts b/lib/services/mimecast-client.test.ts new file mode 100644 index 0000000..c48d74e --- /dev/null +++ b/lib/services/mimecast-client.test.ts @@ -0,0 +1,63 @@ +/** + * mimecast-client.ts — isMimecastConfigured()/getMimecastClient() factory tests. + * + * Mirrors pax8-factory.test.ts's beforeEach env-var-delete + reset seam + * pattern. Does not test MimecastClient's other 20+ methods — out of scope + * for this phase (Phase 17, Plan 01, Task 1). + */ + +import { describe, it, expect, beforeEach } from 'vitest'; +import { isMimecastConfigured, getMimecastClient, _resetMimecastClient } from './mimecast-client'; + +beforeEach(() => { + delete process.env.MIMECAST_CLIENT_ID; + delete process.env.MIMECAST_CLIENT_SECRET; + _resetMimecastClient(); +}); + +describe('isMimecastConfigured', () => { + it('returns false when neither env var is set', () => { + expect(isMimecastConfigured()).toBe(false); + }); + + it('returns false when only MIMECAST_CLIENT_ID is set', () => { + process.env.MIMECAST_CLIENT_ID = 'id1'; + expect(isMimecastConfigured()).toBe(false); + }); + + it('returns false when only MIMECAST_CLIENT_SECRET is set', () => { + process.env.MIMECAST_CLIENT_SECRET = 'secret1'; + expect(isMimecastConfigured()).toBe(false); + }); + + it('returns true when both env vars are set', () => { + process.env.MIMECAST_CLIENT_ID = 'id1'; + process.env.MIMECAST_CLIENT_SECRET = 'secret1'; + expect(isMimecastConfigured()).toBe(true); + }); +}); + +describe('getMimecastClient', () => { + it('throws the exact configuration error when not configured', () => { + expect(() => getMimecastClient()).toThrow( + 'MIMECAST_CLIENT_ID and MIMECAST_CLIENT_SECRET must be set' + ); + }); + + it('returns the same cached instance on repeated calls', () => { + process.env.MIMECAST_CLIENT_ID = 'id1'; + process.env.MIMECAST_CLIENT_SECRET = 'secret1'; + const first = getMimecastClient(); + const second = getMimecastClient(); + expect(second).toBe(first); + }); + + it('rebuilds a new instance after _resetMimecastClient()', () => { + process.env.MIMECAST_CLIENT_ID = 'id1'; + process.env.MIMECAST_CLIENT_SECRET = 'secret1'; + const first = getMimecastClient(); + _resetMimecastClient(); + const second = getMimecastClient(); + expect(second).not.toBe(first); + }); +}); diff --git a/lib/services/mimecast-client.ts b/lib/services/mimecast-client.ts index aa5b03a..f04b77c 100644 --- a/lib/services/mimecast-client.ts +++ b/lib/services/mimecast-client.ts @@ -644,6 +644,16 @@ export class MimecastClient { let _client: MimecastClient | null = null; +export function isMimecastConfigured(): boolean { + return !!(process.env.MIMECAST_CLIENT_ID && process.env.MIMECAST_CLIENT_SECRET); +} + +// Test seam — reset the cached client (e.g. after rotating credentials, or +// to isolate env-var state between test cases). +export function _resetMimecastClient(): void { + _client = null; +} + export function getMimecastClient(): MimecastClient { if (!_client) { const clientId = process.env.MIMECAST_CLIENT_ID;