Files
Charon/tests/integration/proxy-dns-integration.spec.ts

430 lines
14 KiB
TypeScript

/**
* Proxy + DNS Provider Integration E2E Tests
*
* Tests for proxy host and DNS provider integration workflows.
* Covers DNS provider configuration, ACME DNS-01 challenges, and validation.
*
* Test Categories (10-12 tests):
* - Group A: DNS Provider Assignment (3 tests)
* - Group B: DNS Challenge Integration (4 tests)
* - Group C: Provider Management (3 tests)
*
* API Endpoints:
* - GET/POST/PUT/DELETE /api/v1/dns-providers
* - GET/POST/PUT/DELETE /api/v1/proxy-hosts
* - POST /api/v1/dns-providers/:id/test
*/
import { test, expect, loginUser, TEST_PASSWORD } from '../fixtures/auth-fixtures';
import { generateProxyHost } from '../fixtures/proxy-hosts';
import {
waitForLoadingComplete,
waitForAPIResponse,
waitForResourceInUI,
} from '../utils/wait-helpers';
/**
* DNS Provider types supported by the system
*/
type DNSProviderType = 'manual' | 'cloudflare' | 'route53' | 'webhook' | 'rfc2136';
async function getAuthToken(page: import('@playwright/test').Page): Promise<string> {
const storageState = await page.request.storageState();
const origins = Array.isArray(storageState.origins) ? storageState.origins : [];
for (const originEntry of origins) {
const localStorageEntries = Array.isArray(originEntry?.localStorage)
? originEntry.localStorage
: [];
const authEntry = localStorageEntries.find((entry) => entry.name === 'auth');
if (authEntry?.value) {
try {
const parsed = JSON.parse(authEntry.value) as { token?: string };
if (parsed?.token) {
return parsed.token;
}
} catch {
}
}
const tokenEntry = localStorageEntries.find(
(entry) => entry.name === 'token' || entry.name === 'charon_auth_token'
);
if (tokenEntry?.value) {
return tokenEntry.value;
}
}
return '';
}
function buildAuthHeaders(token: string): Record<string, string> | undefined {
return token ? { Authorization: `Bearer ${token}` } : undefined;
}
async function navigateToDnsProviders(page: import('@playwright/test').Page): Promise<void> {
const providersResponse = waitForAPIResponse(page, /\/api\/v1\/dns-providers/);
await page.goto('/dns/providers');
await providersResponse;
await waitForLoadingComplete(page);
}
async function navigateToCertificates(page: import('@playwright/test').Page): Promise<void> {
const certsResponse = waitForAPIResponse(page, /\/api\/v1\/certificates/);
await page.goto('/certificates');
await certsResponse;
await waitForLoadingComplete(page);
}
/**
* Selectors for DNS Provider and Proxy Host pages
*/
const SELECTORS = {
// DNS Provider Page
dnsPageTitle: 'h1',
createDnsButton: 'button:has-text("Create DNS Provider"), button:has-text("Add DNS Provider")',
dnsTable: '[data-testid="dns-provider-table"], table',
dnsRow: '[data-testid="dns-provider-row"], tbody tr',
dnsDeleteBtn: '[data-testid="dns-delete-btn"], button[aria-label*="Delete"]',
dnsEditBtn: '[data-testid="dns-edit-btn"], button[aria-label*="Edit"]',
dnsTestBtn: '[data-testid="dns-test-btn"], button:has-text("Test")',
// Proxy Host Page
proxyPageTitle: 'h1',
createProxyButton: 'button:has-text("Create Proxy Host"), button:has-text("Add Proxy Host")',
proxyTable: '[data-testid="proxy-host-table"], table',
proxyRow: '[data-testid="proxy-host-row"], tbody tr',
proxyEditBtn: '[data-testid="proxy-edit-btn"], button[aria-label*="Edit"]',
// Form Fields
dnsTypeSelect: 'select[name="type"], #dns-type, [data-testid="dns-type-select"]',
dnsNameInput: 'input[name="name"], #dns-name',
apiTokenInput: 'input[name="api_token"], #api-token',
apiKeyInput: 'input[name="api_key"], #api-key',
webhookUrlInput: 'input[name="webhook_url"], #webhook-url',
// Dialog/Modal
confirmDialog: '[role="dialog"], [role="alertdialog"]',
confirmButton: 'button:has-text("Confirm"), button:has-text("Delete"), button:has-text("Yes")',
cancelButton: 'button:has-text("Cancel"), button:has-text("No")',
saveButton: 'button:has-text("Save"), button[type="submit"]',
// Status/State
loadingSkeleton: '[data-testid="loading-skeleton"], .loading',
statusBadge: '[data-testid="status-badge"], .badge',
};
test.describe('Proxy + DNS Provider Integration', () => {
// ===========================================================================
// Group A: DNS Provider Assignment (3 tests)
// ===========================================================================
test.describe('Group A: DNS Provider Assignment', () => {
test('should create manual DNS provider successfully', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create manual DNS provider via API', async () => {
const { id, name } = await testData.createDNSProvider({
providerType: 'manual',
name: 'Manual-DNS-Test',
credentials: {},
});
expect(id).toBeTruthy();
});
await test.step('Navigate to DNS providers page', async () => {
await navigateToDnsProviders(page);
});
await test.step('Verify DNS provider appears in list', async () => {
await waitForResourceInUI(page, /Manual-DNS-Test/i);
});
});
test('should create Cloudflare DNS provider', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create Cloudflare DNS provider via API', async () => {
const { id, name } = await testData.createDNSProvider({
providerType: 'cloudflare',
name: 'Cloudflare-DNS-Test',
credentials: {
api_token: 'test-cloudflare-token-placeholder',
},
});
expect(id).toBeTruthy();
});
await test.step('Navigate to DNS providers', async () => {
await navigateToDnsProviders(page);
});
await test.step('Verify provider is listed', async () => {
await waitForResourceInUI(page, /Cloudflare-DNS-Test/i);
});
});
test('should assign DNS provider to wildcard certificate request', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create DNS provider', async () => {
await testData.createDNSProvider({
providerType: 'manual',
name: 'Wildcard-DNS-Provider',
credentials: {},
});
});
await test.step('Navigate to certificates page', async () => {
await navigateToCertificates(page);
});
await test.step('Verify certificates page loads', async () => {
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
});
});
// ===========================================================================
// Group B: DNS Challenge Integration (4 tests)
// ===========================================================================
test.describe('Group B: DNS Challenge Integration', () => {
test('should test DNS provider connectivity', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create DNS provider for testing', async () => {
await testData.createDNSProvider({
providerType: 'manual',
name: 'Connectivity-Test-DNS',
credentials: {},
});
});
await test.step('Navigate to DNS providers', async () => {
await navigateToDnsProviders(page);
});
await test.step('Verify DNS providers page loads', async () => {
await waitForResourceInUI(page, /Connectivity-Test-DNS/i);
});
});
test('should display DNS challenge instructions for manual provider', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create manual DNS provider', async () => {
await testData.createDNSProvider({
providerType: 'manual',
name: 'Manual-Challenge-DNS',
credentials: {},
});
});
await test.step('Navigate to DNS providers', async () => {
await navigateToDnsProviders(page);
});
await test.step('Verify page content', async () => {
await waitForResourceInUI(page, /Manual-Challenge-DNS/i);
});
});
test('should handle DNS propagation delay gracefully', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create DNS provider', async () => {
await testData.createDNSProvider({
providerType: 'manual',
name: 'Propagation-Test-DNS',
credentials: {},
});
});
await test.step('Navigate to certificates', async () => {
await navigateToCertificates(page);
});
await test.step('Verify page loads', async () => {
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
});
test('should support webhook-based DNS provider', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create webhook DNS provider', async () => {
await testData.createDNSProvider({
providerType: 'webhook',
name: 'Webhook-DNS-Test',
credentials: {
create_url: 'https://example.com/webhook/create',
delete_url: 'https://example.com/webhook/delete',
},
});
});
await test.step('Navigate to DNS providers', async () => {
await navigateToDnsProviders(page);
});
await test.step('Verify provider in list', async () => {
await waitForResourceInUI(page, /Webhook-DNS-Test/i);
});
});
});
// ===========================================================================
// Group C: Provider Management (3 tests)
// ===========================================================================
test.describe('Group C: Provider Management', () => {
test('should update DNS provider credentials', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const { id: providerId } = await testData.createDNSProvider({
providerType: 'cloudflare',
name: 'Update-Credentials-DNS',
credentials: {
api_token: 'initial-token',
},
});
const updatedName = 'Update-Credentials-DNS-Updated';
await test.step('Update provider credentials via API', async () => {
const token = await getAuthToken(page);
expect(token).toBeTruthy();
const response = await page.request.put(`/api/v1/dns-providers/${providerId}`, {
data: {
provider_type: 'cloudflare',
name: updatedName,
credentials: {
api_token: 'updated-token',
},
},
headers: buildAuthHeaders(token),
});
expect(response.ok()).toBeTruthy();
});
await test.step('Navigate to DNS providers', async () => {
await navigateToDnsProviders(page);
});
await test.step('Verify updated provider', async () => {
await waitForResourceInUI(page, updatedName);
});
});
test('should delete DNS provider with confirmation', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const { id: providerId } = await testData.createDNSProvider({
providerType: 'manual',
name: 'Delete-Test-DNS',
credentials: {},
});
await test.step('Navigate to DNS providers', async () => {
await navigateToDnsProviders(page);
});
await test.step('Verify provider exists before deletion', async () => {
await waitForResourceInUI(page, /Delete-Test-DNS/i);
});
await test.step('Delete provider via API', async () => {
const token = await getAuthToken(page);
const response = await page.request.delete(`/api/v1/dns-providers/${providerId}`, {
headers: buildAuthHeaders(token),
});
expect(response.ok()).toBeTruthy();
});
await test.step('Verify provider removed from list', async () => {
await navigateToDnsProviders(page);
await expect(page.getByText(/Delete-Test-DNS/i)).toHaveCount(0);
});
});
test('should list all configured DNS providers', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create multiple DNS providers
await testData.createDNSProvider({
providerType: 'manual',
name: 'List-Test-DNS-1',
credentials: {},
});
await testData.createDNSProvider({
providerType: 'cloudflare',
name: 'List-Test-DNS-2',
credentials: { api_token: 'test-token' },
});
await test.step('Navigate to DNS providers', async () => {
await navigateToDnsProviders(page);
});
await test.step('Verify providers list', async () => {
await waitForResourceInUI(page, /List-Test-DNS-1/i);
await waitForResourceInUI(page, /List-Test-DNS-2/i);
});
await test.step('Verify API returns providers', async () => {
const token = await getAuthToken(page);
const response = await page.request.get('/api/v1/dns-providers', {
headers: buildAuthHeaders(token),
});
expect(response.ok()).toBeTruthy();
const data = await response.json();
const providers = data.providers || data.items || data;
expect(Array.isArray(providers)).toBe(true);
});
});
});
});