Files
cloud-host/backend/src/kubernetes/kubernetes.service.spec.ts
T
keyhan 22359be40e fix(platform): apply production hardening from audit plan
Close billing, tenancy, migration, build, and CI/CD gaps identified in the
audit: wallet/gateway guards, full-UUID namespaces, idempotent migrations with
base schema, stateful service stability, safer Dockerfiles/git builds, and
platform chart hardening (Redis auth, RollingUpdate, backups, Swagger off).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 19:35:07 +03:30

190 lines
6.6 KiB
TypeScript

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<string, any> {
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<string>();
for (let i = 0; i < 50; i++) {
passwords.add(generatePassword());
}
// With 62^24 possibilities, all 50 should be unique
expect(passwords.size).toBe(50);
});
});