Files
claude-task-master/packages/tm-core/src/auth/auth-manager.test.ts
2025-09-09 03:32:48 +02:00

151 lines
3.6 KiB
TypeScript

/**
* Tests for AuthManager singleton behavior
*/
import { describe, it, expect, beforeEach, vi } from 'vitest';
// Mock the logger to verify warnings (must be hoisted before SUT import)
const mockLogger = {
warn: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
error: vi.fn()
};
vi.mock('../logger/index.js', () => ({
getLogger: () => mockLogger
}));
// Spy on CredentialStore constructor to verify config propagation
const CredentialStoreSpy = vi.fn();
vi.mock('./credential-store.js', () => {
return {
CredentialStore: class {
constructor(config: any) {
CredentialStoreSpy(config);
this.getCredentials = vi.fn(() => null);
}
getCredentials() {
return null;
}
saveCredentials() {}
clearCredentials() {}
hasValidCredentials() {
return false;
}
}
};
});
// Mock OAuthService to avoid side effects
vi.mock('./oauth-service.js', () => {
return {
OAuthService: class {
constructor() {}
authenticate() {
return Promise.resolve({});
}
getAuthorizationUrl() {
return null;
}
}
};
});
// Mock SupabaseAuthClient to avoid side effects
vi.mock('../clients/supabase-client.js', () => {
return {
SupabaseAuthClient: class {
constructor() {}
refreshSession() {
return Promise.resolve({});
}
signOut() {
return Promise.resolve();
}
}
};
});
// Import SUT after mocks
import { AuthManager } from './auth-manager.js';
describe('AuthManager Singleton', () => {
beforeEach(() => {
// Reset singleton before each test
AuthManager.resetInstance();
vi.clearAllMocks();
CredentialStoreSpy.mockClear();
});
it('should return the same instance on multiple calls', () => {
const instance1 = AuthManager.getInstance();
const instance2 = AuthManager.getInstance();
expect(instance1).toBe(instance2);
});
it('should use config on first call', () => {
const config = {
baseUrl: 'https://test.auth.com',
configDir: '/test/config',
configFile: '/test/config/auth.json'
};
const instance = AuthManager.getInstance(config);
expect(instance).toBeDefined();
// Assert that CredentialStore was constructed with the provided config
expect(CredentialStoreSpy).toHaveBeenCalledTimes(1);
expect(CredentialStoreSpy).toHaveBeenCalledWith(config);
// Verify the config is passed to internal components through observable behavior
// getCredentials would look in the configured file path
const credentials = instance.getCredentials();
expect(credentials).toBeNull(); // File doesn't exist, but config was propagated correctly
});
it('should warn when config is provided after initialization', () => {
// Clear previous calls
mockLogger.warn.mockClear();
// First call with config
AuthManager.getInstance({ baseUrl: 'https://first.auth.com' });
// Second call with different config
AuthManager.getInstance({ baseUrl: 'https://second.auth.com' });
// Verify warning was logged
expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringMatching(/config.*after initialization.*ignored/i)
);
});
it('should not warn when no config is provided after initialization', () => {
// Clear previous calls
mockLogger.warn.mockClear();
// First call with config
AuthManager.getInstance({ configDir: '/test/config' });
// Second call without config
AuthManager.getInstance();
// Verify no warning was logged
expect(mockLogger.warn).not.toHaveBeenCalled();
});
it('should allow resetting the instance', () => {
const instance1 = AuthManager.getInstance();
// Reset the instance
AuthManager.resetInstance();
// Get new instance
const instance2 = AuthManager.getInstance();
// They should be different instances
expect(instance1).not.toBe(instance2);
});
});