Files
Charon/tests/fixtures/certificates.ts
2026-03-04 18:34:49 +00:00

398 lines
11 KiB
TypeScript

/**
* Certificate Test Fixtures
*
* Mock data for SSL Certificate E2E tests.
* Provides various certificate configurations for testing CRUD operations,
* ACME challenges, and validation scenarios.
*
* @example
* ```typescript
* import { letsEncryptCertificate, customCertificateMock, expiredCertificate } from './fixtures/certificates';
*
* test('upload custom certificate', async ({ testData }) => {
* const { id } = await testData.createCertificate(customCertificateMock);
* });
* ```
*/
import { generateDomain, generateUniqueId } from './test-data';
/**
* Certificate type
*/
export type CertificateType = 'letsencrypt' | 'custom' | 'self-signed';
/**
* ACME challenge type
*/
export type ChallengeType = 'http-01' | 'dns-01';
/**
* Certificate status
*/
export type CertificateStatus =
| 'pending'
| 'valid'
| 'expired'
| 'revoked'
| 'error';
/**
* Certificate configuration interface
*/
export interface CertificateConfig {
/** Domains covered by the certificate */
domains: string[];
/** Certificate type */
type: CertificateType;
/** PEM-encoded certificate (for custom certs) */
certificate?: string;
/** PEM-encoded private key (for custom certs) */
privateKey?: string;
/** PEM-encoded intermediate certificates */
intermediates?: string;
/** DNS provider ID (for dns-01 challenge) */
dnsProviderId?: string;
/** Force renewal even if not expiring */
forceRenewal?: boolean;
/** ACME email for notifications */
acmeEmail?: string;
}
/**
* Self-signed test certificate and key
* Valid for testing purposes only - DO NOT use in production
*/
export const selfSignedTestCert = {
certificate: `-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAJC1HiIAZAiUMA0GCSqGSIb3Qw0BBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMjQwMTAxMDAwMDAwWhcNMjkwMTAxMDAwMDAwWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAzUCFQIzxZqSU5LNHJ3m1R8fU3VpMfmTc1DJfKSBnBH4HKvC2vN7T9N9P
test-certificate-data-placeholder-for-testing-purposes-only
-----END CERTIFICATE-----`,
privateKey: `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDNQIVAjPFmpJTk
s0cnebVHx9TdWkx+ZNzUMl8pIGcEfgcq8La83tP030/0
test-private-key-data-placeholder-for-testing-purposes-only
-----END PRIVATE KEY-----`,
};
/**
* Let's Encrypt certificate mock
* Simulates a certificate obtained via ACME
*/
export const letsEncryptCertificate: CertificateConfig = {
domains: ['app.example.com'],
type: 'letsencrypt',
acmeEmail: 'admin@example.com',
};
/**
* Let's Encrypt certificate with multiple domains (SAN)
*/
export const multiDomainLetsEncrypt: CertificateConfig = {
domains: ['app.example.com', 'www.example.com', 'api.example.com'],
type: 'letsencrypt',
acmeEmail: 'admin@example.com',
};
/**
* Wildcard certificate mock
* Uses DNS-01 challenge (required for wildcards)
*/
export const wildcardCertificate: CertificateConfig = {
domains: ['*.example.com', 'example.com'],
type: 'letsencrypt',
acmeEmail: 'admin@example.com',
dnsProviderId: '', // Will be set dynamically in tests
};
/**
* Custom certificate mock
* Uses self-signed certificate for testing
*/
export const customCertificateMock: CertificateConfig = {
domains: ['custom.example.com'],
type: 'custom',
certificate: selfSignedTestCert.certificate,
privateKey: selfSignedTestCert.privateKey,
};
/**
* Custom certificate with intermediate chain
*/
export const customCertWithChain: CertificateConfig = {
domains: ['chain.example.com'],
type: 'custom',
certificate: selfSignedTestCert.certificate,
privateKey: selfSignedTestCert.privateKey,
intermediates: `-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
intermediate-certificate-placeholder-for-testing
-----END CERTIFICATE-----`,
};
/**
* Expired certificate mock
* For testing expiration handling
*/
export const expiredCertificate = {
domains: ['expired.example.com'],
type: 'custom' as CertificateType,
status: 'expired' as CertificateStatus,
expiresAt: new Date(Date.now() - 86400000).toISOString(), // Yesterday
certificate: `-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAJC1HiIAZAiUMA0GCSqGSIb3Qw0BBQUAMEUxCzAJBgNV
expired-certificate-placeholder
-----END CERTIFICATE-----`,
privateKey: selfSignedTestCert.privateKey,
};
/**
* Certificate expiring soon
* For testing renewal warnings
*/
export const expiringCertificate = {
domains: ['expiring.example.com'],
type: 'letsencrypt' as CertificateType,
status: 'valid' as CertificateStatus,
expiresAt: new Date(Date.now() + 7 * 86400000).toISOString(), // 7 days from now
};
/**
* Revoked certificate mock
*/
export const revokedCertificate = {
domains: ['revoked.example.com'],
type: 'letsencrypt' as CertificateType,
status: 'revoked' as CertificateStatus,
revokedAt: new Date().toISOString(),
revocationReason: 'Key compromise',
};
/**
* Invalid certificate configurations for validation testing
*/
export const invalidCertificates = {
/** Empty domains */
emptyDomains: {
domains: [],
type: 'letsencrypt' as CertificateType,
},
/** Invalid domain format */
invalidDomain: {
domains: ['not a valid domain!'],
type: 'letsencrypt' as CertificateType,
},
/** Missing certificate for custom type */
missingCertificate: {
domains: ['custom.example.com'],
type: 'custom' as CertificateType,
privateKey: selfSignedTestCert.privateKey,
},
/** Missing private key for custom type */
missingPrivateKey: {
domains: ['custom.example.com'],
type: 'custom' as CertificateType,
certificate: selfSignedTestCert.certificate,
},
/** Invalid certificate PEM format */
invalidCertificatePEM: {
domains: ['custom.example.com'],
type: 'custom' as CertificateType,
certificate: 'not a valid PEM certificate',
privateKey: selfSignedTestCert.privateKey,
},
/** Invalid private key PEM format */
invalidPrivateKeyPEM: {
domains: ['custom.example.com'],
type: 'custom' as CertificateType,
certificate: selfSignedTestCert.certificate,
privateKey: 'not a valid PEM private key',
},
/** Mismatched certificate and key */
mismatchedCertKey: {
domains: ['custom.example.com'],
type: 'custom' as CertificateType,
certificate: selfSignedTestCert.certificate,
privateKey: `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDifferent-key
-----END PRIVATE KEY-----`,
},
/** Wildcard without DNS provider */
wildcardWithoutDNS: {
domains: ['*.example.com'],
type: 'letsencrypt' as CertificateType,
// dnsProviderId is missing - required for wildcards
},
/** Too many domains */
tooManyDomains: {
domains: Array.from({ length: 150 }, (_, i) => `domain${i}.example.com`),
type: 'letsencrypt' as CertificateType,
},
/** XSS in domain */
xssInDomain: {
domains: ['<script>alert(1)</script>.example.com'],
type: 'letsencrypt' as CertificateType,
},
/** Invalid ACME email */
invalidAcmeEmail: {
domains: ['app.example.com'],
type: 'letsencrypt' as CertificateType,
acmeEmail: 'not-an-email',
},
};
/**
* Generate a unique certificate configuration
* @param overrides - Optional configuration overrides
* @returns CertificateConfig with unique domain
*
* @example
* ```typescript
* const cert = generateCertificate({ type: 'custom' });
* ```
*/
export function generateCertificate(
overrides: Partial<CertificateConfig> = {}
): CertificateConfig {
const baseCert: CertificateConfig = {
domains: [generateDomain('cert')],
type: 'letsencrypt',
acmeEmail: `admin-${generateUniqueId()}@test.local`,
...overrides,
};
// Add certificate/key for custom type
if (baseCert.type === 'custom' && !baseCert.certificate) {
baseCert.certificate = selfSignedTestCert.certificate;
baseCert.privateKey = selfSignedTestCert.privateKey;
}
return baseCert;
}
/**
* Generate wildcard certificate configuration
* @param baseDomain - Base domain for the wildcard
* @param dnsProviderId - DNS provider ID for DNS-01 challenge
* @returns CertificateConfig for wildcard
*/
export function generateWildcardCertificate(
baseDomain?: string,
dnsProviderId?: string
): CertificateConfig {
const domain = baseDomain || `${generateUniqueId()}.test.local`;
return {
domains: [`*.${domain}`, domain],
type: 'letsencrypt',
acmeEmail: `admin-${generateUniqueId()}@test.local`,
dnsProviderId,
};
}
/**
* Generate multiple unique certificates
* @param count - Number of certificates to generate
* @param overrides - Optional configuration overrides
* @returns Array of CertificateConfig
*/
export function generateCertificates(
count: number,
overrides: Partial<CertificateConfig> = {}
): CertificateConfig[] {
return Array.from({ length: count }, () => generateCertificate(overrides));
}
/**
* Expected API response for certificate creation
*/
export interface CertificateAPIResponse {
id: string;
domains: string[];
type: CertificateType;
status: CertificateStatus;
issuer?: string;
expires_at?: string;
issued_at?: string;
acme_email?: string;
dns_provider_id?: string;
created_at: string;
updated_at: string;
}
/**
* Mock API response for testing
*/
export function mockCertificateResponse(
config: Partial<CertificateConfig> = {}
): CertificateAPIResponse {
const id = generateUniqueId();
const domains = config.domains || [generateDomain('cert')];
const isLetsEncrypt = config.type !== 'custom';
return {
id,
domains,
type: config.type || 'letsencrypt',
status: 'valid',
issuer: isLetsEncrypt ? "Let's Encrypt Authority X3" : 'Self-Signed',
expires_at: new Date(Date.now() + 90 * 86400000).toISOString(), // 90 days
issued_at: new Date().toISOString(),
acme_email: config.acmeEmail,
dns_provider_id: config.dnsProviderId,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
};
}
/**
* Mock ACME challenge data
*/
export interface ACMEChallengeData {
type: ChallengeType;
token: string;
keyAuthorization: string;
domain: string;
status: 'pending' | 'processing' | 'valid' | 'invalid';
}
/**
* Generate mock ACME HTTP-01 challenge
*/
export function mockHTTP01Challenge(domain: string): ACMEChallengeData {
return {
type: 'http-01',
token: `mock-token-${generateUniqueId()}`,
keyAuthorization: `mock-auth-${generateUniqueId()}`,
domain,
status: 'pending',
};
}
/**
* Generate mock ACME DNS-01 challenge
*/
export function mockDNS01Challenge(domain: string): ACMEChallengeData {
return {
type: 'dns-01',
token: `mock-token-${generateUniqueId()}`,
keyAuthorization: `mock-dns-auth-${generateUniqueId()}`,
domain: `_acme-challenge.${domain}`,
status: 'pending',
};
}