gcp-monorepo-secret-manager
Version:
A Google Cloud Secret Manager utility for managing environment variables across different environments and services within a monorepo.
283 lines • 12.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const GcpMonorepoSecretManager_1 = require("../GcpMonorepoSecretManager");
const ConfigManager_1 = require("../ConfigManager");
const fs = __importStar(require("fs"));
// Mock dependencies
jest.mock('fs');
jest.mock('../ConfigManager');
jest.mock('firebase-admin/app');
jest.mock('@google-cloud/secret-manager');
const mockFs = fs;
const MockConfigManager = ConfigManager_1.ConfigManager;
describe('GcpMonorepoSecretManager', () => {
const mockServiceConfig = {
name: 'app',
envPath: '.environments/.app.{env}.env',
targetPath: 'services/app/.env',
secretPrefix: 'app-env-vars'
};
const mockConfigManager = {
getProjectId: jest.fn(),
getServiceAccountPath: jest.fn(),
getServiceByName: jest.fn(),
getServiceNames: jest.fn(),
getServices: jest.fn(),
getDeletePolicy: jest.fn()
};
beforeEach(() => {
jest.clearAllMocks();
MockConfigManager.mockImplementation(() => mockConfigManager);
// Default mock implementations
mockConfigManager.getProjectId.mockReturnValue('test-project');
mockConfigManager.getServiceAccountPath.mockReturnValue('service-account.json');
mockConfigManager.getServiceByName.mockReturnValue(mockServiceConfig);
mockConfigManager.getServiceNames.mockReturnValue(['app', 'api']);
mockConfigManager.getDeletePolicy.mockReturnValue({
maxVersions: 10,
maxAgeDays: 30,
enabled: true
});
mockFs.existsSync.mockReturnValue(true);
mockFs.readFileSync.mockReturnValue(JSON.stringify({ project_id: 'test-project' }));
});
describe('constructor', () => {
it('should initialize with staging environment', () => {
const manager = new GcpMonorepoSecretManager_1.GcpMonorepoSecretManager({
environment: 'staging',
overrideSa: false
});
expect(MockConfigManager).toHaveBeenCalledWith(undefined);
expect(mockConfigManager.getProjectId).toHaveBeenCalledWith('staging');
});
it('should initialize with production environment', () => {
const manager = new GcpMonorepoSecretManager_1.GcpMonorepoSecretManager({
environment: 'production',
overrideSa: false
});
expect(mockConfigManager.getProjectId).toHaveBeenCalledWith('production');
});
it('should use custom config path when provided', () => {
const customPath = 'custom-config.json';
const manager = new GcpMonorepoSecretManager_1.GcpMonorepoSecretManager({
environment: 'staging',
overrideSa: false,
configPath: customPath
});
expect(MockConfigManager).toHaveBeenCalledWith(customPath);
});
it('should throw error when service account file does not exist', () => {
mockFs.existsSync.mockReturnValue(false);
expect(() => new GcpMonorepoSecretManager_1.GcpMonorepoSecretManager({
environment: 'staging',
overrideSa: false
})).toThrow('Service account file not found at service-account.json');
});
});
describe('getAvailableServices', () => {
it('should return list of available services', () => {
const manager = new GcpMonorepoSecretManager_1.GcpMonorepoSecretManager({
environment: 'staging',
overrideSa: true
});
const services = manager.getAvailableServices();
expect(services).toEqual(['app', 'api']);
expect(mockConfigManager.getServiceNames).toHaveBeenCalled();
});
});
describe('path resolution', () => {
let manager;
beforeEach(() => {
manager = new GcpMonorepoSecretManager_1.GcpMonorepoSecretManager({
environment: 'staging',
overrideSa: true
});
});
it('should resolve staging environment path correctly', () => {
// Access private method through any for testing
const envPath = manager.getEnvPath('app');
expect(envPath).toBe('.environments/.app.stg.env');
});
it('should resolve production environment path correctly', () => {
const prodManager = new GcpMonorepoSecretManager_1.GcpMonorepoSecretManager({
environment: 'production',
overrideSa: true
});
const envPath = prodManager.getEnvPath('app');
expect(envPath).toBe('.environments/.app.prod.env');
});
it('should resolve secret name correctly', () => {
const secretName = manager.getSecretName('app');
expect(secretName).toBe('app-env-vars_ENV_FILE');
});
it('should resolve target path correctly', () => {
const targetPath = manager.getTargetPath('app');
expect(targetPath).toBe('services/app/.env');
});
it('should throw error for non-existent service', () => {
mockConfigManager.getServiceByName.mockReturnValue(undefined);
expect(() => manager.getEnvPath('nonexistent')).toThrow("Service 'nonexistent' not found in configuration");
});
});
describe('uploadEnv', () => {
let manager;
const mockClient = {
getSecret: jest.fn(),
addSecretVersion: jest.fn(),
createSecret: jest.fn(),
listSecretVersions: jest.fn()
};
beforeEach(() => {
manager = new GcpMonorepoSecretManager_1.GcpMonorepoSecretManager({
environment: 'staging',
overrideSa: true
});
// Mock the client
manager.client = mockClient;
// Mock file operations
mockFs.readFileSync.mockReturnValue('NODE_ENV=staging\nPORT=3000');
});
it('should throw error when environment file does not exist', async () => {
mockFs.existsSync.mockReturnValue(false);
await expect(manager.uploadEnv('app')).rejects.toThrow('Environment file not found at .environments/.app.stg.env');
});
it('should handle "all" services', async () => {
mockFs.existsSync.mockReturnValue(true);
mockClient.getSecret.mockResolvedValue({});
mockClient.addSecretVersion.mockResolvedValue([{ name: 'version-1' }]);
mockClient.listSecretVersions = jest.fn().mockResolvedValue([[]]);
await manager.uploadEnv('all');
expect(mockConfigManager.getServiceNames).toHaveBeenCalled();
});
});
describe('downloadEnv', () => {
let manager;
const mockClient = {
accessSecretVersion: jest.fn()
};
beforeEach(() => {
manager = new GcpMonorepoSecretManager_1.GcpMonorepoSecretManager({
environment: 'staging',
overrideSa: true
});
manager.client = mockClient;
// Mock directory operations
mockFs.mkdirSync.mockImplementation(() => undefined);
mockFs.writeFileSync.mockImplementation(() => undefined);
});
it('should create directory and write file on download', async () => {
const mockPayload = {
payload: {
data: Buffer.from('NODE_ENV=staging\nPORT=3000')
}
};
mockClient.accessSecretVersion.mockResolvedValue([mockPayload]);
mockFs.existsSync.mockReturnValue(false); // Directory doesn't exist
await manager.downloadEnv('app');
expect(mockFs.mkdirSync).toHaveBeenCalledWith('.environments', { recursive: true });
expect(mockFs.writeFileSync).toHaveBeenCalledWith('.environments/.app.stg.env', 'NODE_ENV=staging\nPORT=3000');
});
it('should handle "all" services', async () => {
const mockPayload = {
payload: {
data: Buffer.from('NODE_ENV=staging')
}
};
mockClient.accessSecretVersion.mockResolvedValue([mockPayload]);
await manager.downloadEnv('all');
expect(mockConfigManager.getServiceNames).toHaveBeenCalled();
});
});
describe('setEnv', () => {
let manager;
const mockClient = {
accessSecretVersion: jest.fn()
};
beforeEach(() => {
manager = new GcpMonorepoSecretManager_1.GcpMonorepoSecretManager({
environment: 'staging',
overrideSa: true
});
manager.client = mockClient;
mockFs.mkdirSync.mockImplementation(() => undefined);
mockFs.writeFileSync.mockImplementation(() => undefined);
});
it('should create target directory and write file', async () => {
const mockPayload = {
payload: {
data: Buffer.from('NODE_ENV=staging\nPORT=3000')
}
};
mockClient.accessSecretVersion.mockResolvedValue([mockPayload]);
mockFs.existsSync.mockReturnValue(false); // Directory doesn't exist
await manager.setEnv('app');
expect(mockFs.mkdirSync).toHaveBeenCalledWith('services/app', { recursive: true });
expect(mockFs.writeFileSync).toHaveBeenCalledWith('services/app/.env', 'NODE_ENV=staging\nPORT=3000');
});
});
describe('cleanupVersions', () => {
let manager;
const mockClient = {
listSecretVersions: jest.fn(),
destroySecretVersion: jest.fn()
};
beforeEach(() => {
manager = new GcpMonorepoSecretManager_1.GcpMonorepoSecretManager({
environment: 'staging',
overrideSa: true
});
manager.client = mockClient;
});
it('should skip cleanup when delete policy is disabled', async () => {
mockConfigManager.getDeletePolicy.mockReturnValue({
enabled: false
});
await manager.cleanupVersions('app');
expect(mockClient.listSecretVersions).not.toHaveBeenCalled();
});
it('should handle "all" services cleanup', async () => {
mockClient.listSecretVersions.mockResolvedValue([[]]);
await manager.cleanupVersions('all');
expect(mockConfigManager.getServiceNames).toHaveBeenCalled();
});
it('should handle cleanup errors gracefully', async () => {
mockClient.listSecretVersions.mockRejectedValue(new Error('Access denied'));
// Should not throw
await expect(manager.cleanupVersions('app')).resolves.not.toThrow();
});
});
});
//# sourceMappingURL=GcpMonorepoSecretManager.test.js.map