import { AppRuntime, DatabaseType } from '../common/enums'; /** * Tests for KubernetesService.buildHelmValues (private method). * We extract and test the logic directly since it's critical for Helm deployments. */ describe('buildHelmValues logic', () => { const domain = 'apps.cloudhost.local'; function buildHelmValues(app: any, imageUri: string): Record { const isWordPress = app.runtime === AppRuntime.WORDPRESS; const hasDb = app.databaseType !== DatabaseType.NONE; const isPostgres = app.databaseType === DatabaseType.POSTGRESQL; return { app: { name: app.name, namespace: `user-${app.userId.replace(/-/g, '')}`, runtime: app.runtime, image: imageUri, port: app.port, replicas: app.replicas, }, resources: { cpuRequest: app.cpuRequest, cpuLimit: app.cpuLimit, memoryRequest: app.memoryRequest, memoryLimit: app.memoryLimit, }, envVars: app.envVars || {}, ingress: { enabled: true, subdomain: app.subdomain || app.name, domain: domain, clusterIssuer: 'letsencrypt-prod', }, database: { enabled: hasDb, type: app.databaseType, version: app.dbVersion || (isPostgres ? '16' : '8.0'), username: app.dbUsername || 'appuser', password: app.dbPassword || 'generated-password', storageSize: app.dbStorageSize || '1Gi', resources: { cpuRequest: '100m', cpuLimit: '500m', memoryRequest: '256Mi', memoryLimit: '512Mi', }, }, wordpress: { enabled: isWordPress, }, changeCause: `Deploy ${imageUri} at 2024-01-01T00:00:00.000Z`, }; } const baseApp = { name: 'my-app', userId: 'abc123-def456', runtime: AppRuntime.NODEJS, port: 3000, replicas: 1, cpuRequest: '100m', cpuLimit: '500m', memoryRequest: '128Mi', memoryLimit: '512Mi', databaseType: DatabaseType.NONE, envVars: {}, subdomain: 'my-app-abc123', }; it('should set correct namespace from userId', () => { const values = buildHelmValues(baseApp, 'registry/my-app:123'); expect(values.app.namespace).toBe('user-abc123def456'); }); it('should disable database when type is NONE', () => { const values = buildHelmValues(baseApp, 'registry/my-app:123'); expect(values.database.enabled).toBe(false); }); it('should enable database for PostgreSQL', () => { const app = { ...baseApp, databaseType: DatabaseType.POSTGRESQL, dbUsername: 'pguser', dbPassword: 'secret' }; const values = buildHelmValues(app, 'registry/my-app:123'); expect(values.database.enabled).toBe(true); expect(values.database.type).toBe('postgresql'); expect(values.database.version).toBe('16'); expect(values.database.username).toBe('pguser'); }); it('should enable database for MySQL with correct default version', () => { const app = { ...baseApp, databaseType: DatabaseType.MYSQL }; const values = buildHelmValues(app, 'registry/my-app:123'); expect(values.database.enabled).toBe(true); expect(values.database.version).toBe('8.0'); }); it('should enable wordpress flags for wordpress runtime', () => { const app = { ...baseApp, runtime: AppRuntime.WORDPRESS, databaseType: DatabaseType.MYSQL }; const values = buildHelmValues(app, 'registry/wp:1'); expect(values.wordpress.enabled).toBe(true); expect(values.database.enabled).toBe(true); }); it('should not enable wordpress for nodejs runtime', () => { const values = buildHelmValues(baseApp, 'registry/my-app:123'); expect(values.wordpress.enabled).toBe(false); }); it('should use subdomain from app if provided', () => { const values = buildHelmValues(baseApp, 'registry/my-app:123'); expect(values.ingress.subdomain).toBe('my-app-abc123'); }); it('should fallback subdomain to app name', () => { const app = { ...baseApp, subdomain: undefined }; const values = buildHelmValues(app, 'registry/my-app:123'); expect(values.ingress.subdomain).toBe('my-app'); }); it('should use custom dbVersion when provided', () => { const app = { ...baseApp, databaseType: DatabaseType.POSTGRESQL, dbVersion: '15' }; const values = buildHelmValues(app, 'registry/my-app:123'); expect(values.database.version).toBe('15'); }); it('should default dbStorageSize to 1Gi', () => { const app = { ...baseApp, databaseType: DatabaseType.POSTGRESQL }; const values = buildHelmValues(app, 'registry/my-app:123'); expect(values.database.storageSize).toBe('1Gi'); }); it('should use custom dbStorageSize when provided', () => { const app = { ...baseApp, databaseType: DatabaseType.POSTGRESQL, dbStorageSize: '5Gi' }; const values = buildHelmValues(app, 'registry/my-app:123'); expect(values.database.storageSize).toBe('5Gi'); }); it('should pass envVars as empty object when not set', () => { const app = { ...baseApp, envVars: undefined }; const values = buildHelmValues(app, 'registry/my-app:123'); expect(values.envVars).toEqual({}); }); it('should pass envVars when set', () => { const app = { ...baseApp, envVars: { NODE_ENV: 'production', API_KEY: '12345' } }; const values = buildHelmValues(app, 'registry/my-app:123'); expect(values.envVars).toEqual({ NODE_ENV: 'production', API_KEY: '12345' }); }); it('should set image correctly in app values', () => { const values = buildHelmValues(baseApp, 'registry.local:5000/abc123/my-app:1700000000'); expect(values.app.image).toBe('registry.local:5000/abc123/my-app:1700000000'); }); }); describe('generatePassword', () => { function generatePassword(length = 24): string { const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; let password = ''; for (let i = 0; i < length; i++) { password += chars.charAt(Math.floor(Math.random() * chars.length)); } return password; } it('should generate password of specified length', () => { expect(generatePassword(16)).toHaveLength(16); expect(generatePassword(32)).toHaveLength(32); expect(generatePassword()).toHaveLength(24); }); it('should only contain alphanumeric characters (no shell-unsafe chars)', () => { for (let i = 0; i < 100; i++) { const pw = generatePassword(); expect(pw).toMatch(/^[a-zA-Z0-9]+$/); } }); it('should generate unique passwords', () => { const passwords = new Set(); for (let i = 0; i < 50; i++) { passwords.add(generatePassword()); } // With 62^24 possibilities, all 50 should be unique expect(passwords.size).toBe(50); }); });