chore: clean .gitignore cache

This commit is contained in:
GitHub Actions
2026-01-26 19:21:33 +00:00
parent 1b1b3a70b1
commit e5f0fec5db
1483 changed files with 0 additions and 472793 deletions

View File

@@ -1,526 +0,0 @@
/**
* Backup & Restore E2E Tests (Phase 6.5)
*
* Tests for complete backup and restore workflows including
* scheduling, verification, and disaster recovery scenarios.
*
* Test Categories (20-24 tests):
* - Group A: Backup Creation (5 tests)
* - Group B: Backup Scheduling (4 tests)
* - Group C: Restore Operations (5 tests)
* - Group D: Backup Verification (4 tests)
* - Group E: Error Handling (4 tests)
*
* API Endpoints:
* - GET /api/v1/backups
* - POST /api/v1/backups
* - DELETE /api/v1/backups/:id
* - POST /api/v1/backups/:id/restore
* - GET /api/v1/backups/:id/download
*/
import { test, expect, loginUser, TEST_PASSWORD } from '../fixtures/auth-fixtures';
import { generateProxyHost } from '../fixtures/proxy-hosts';
import { generateAccessList } from '../fixtures/access-lists';
import { generateDnsProvider } from '../fixtures/dns-providers';
import {
waitForToast,
waitForLoadingComplete,
waitForAPIResponse,
waitForModal,
clickAndWaitForResponse,
} from '../utils/wait-helpers';
/**
* Selectors for Backup pages
*/
const SELECTORS = {
// Backup List
backupTable: '[data-testid="backup-list"], table',
backupRow: '[data-testid="backup-row"], tbody tr',
createBackupBtn: 'button:has-text("Create Backup"), button:has-text("Backup Now")',
deleteBackupBtn: 'button:has-text("Delete"), [data-testid="delete-backup"]',
restoreBackupBtn: 'button:has-text("Restore"), [data-testid="restore-backup"]',
downloadBackupBtn: 'button:has-text("Download"), [data-testid="download-backup"]',
// Backup Form
backupNameInput: 'input[name="name"], #backup-name',
backupDescriptionInput: 'textarea[name="description"], #backup-description',
includeConfigCheckbox: 'input[name="include_config"], #include-config',
includeDataCheckbox: 'input[name="include_data"], #include-data',
// Schedule Configuration
scheduleEnabledToggle: 'input[name="schedule_enabled"], [data-testid="schedule-toggle"]',
scheduleFrequency: 'select[name="frequency"], #schedule-frequency',
scheduleTime: 'input[name="schedule_time"], #schedule-time',
retentionDays: 'input[name="retention_days"], #retention-days',
// Restore Modal
restoreModal: '[data-testid="restore-modal"], .modal',
confirmRestoreBtn: 'button:has-text("Confirm Restore"), button:has-text("Yes, Restore")',
restoreWarning: '[data-testid="restore-warning"], .warning',
// Status Indicators
backupStatus: '[data-testid="backup-status"], .backup-status',
progressBar: '[data-testid="progress-bar"], .progress',
backupSize: '[data-testid="backup-size"], .backup-size',
backupDate: '[data-testid="backup-date"], .backup-date',
// Common
saveButton: 'button:has-text("Save"), button[type="submit"]',
cancelButton: 'button:has-text("Cancel")',
loadingSkeleton: '[data-testid="loading-skeleton"], .loading',
};
test.describe('Backup & Restore E2E', () => {
// ===========================================================================
// Group A: Backup Creation (5 tests)
// ===========================================================================
test.describe('Group A: Backup Creation', () => {
test('should display backup list page', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups page', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify backups page loads', async () => {
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
});
test('should create manual backup via API', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create some data to back up
const proxyConfig = generateProxyHost();
await testData.createProxyHost({
domain: proxyConfig.domain,
forwardHost: proxyConfig.forwardHost,
forwardPort: proxyConfig.forwardPort,
});
await test.step('Verify proxy host was created', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
await expect(page.getByText(proxyConfig.domain)).toBeVisible();
});
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify backups page loads', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should create backup with configuration only', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify backup creation options', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should create backup with all data included', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create multiple resources
const proxy = generateProxyHost();
const acl = generateAccessList();
await testData.createProxyHost({
domain: proxy.domain,
forwardHost: proxy.forwardHost,
forwardPort: proxy.forwardPort,
});
await testData.createAccessList({
name: acl.name,
type: acl.type,
ipRules: acl.ipRules,
});
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify backup page content', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should show backup creation progress', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify page loads', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
});
// ===========================================================================
// Group B: Backup Scheduling (4 tests)
// ===========================================================================
test.describe('Group B: Backup Scheduling', () => {
test('should display backup schedule settings', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backup settings', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify settings page', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should configure daily backup schedule', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backup settings', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify schedule configuration options', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should configure weekly backup schedule', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backup settings', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify settings page loads', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should set backup retention policy', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backup settings', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify retention policy options', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
});
// ===========================================================================
// Group C: Restore Operations (5 tests)
// ===========================================================================
test.describe('Group C: Restore Operations', () => {
test('should display restore options for backup', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify backup list page', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should restore proxy hosts from backup', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create proxy host that would be in a backup
const proxyInput = generateProxyHost();
const createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await test.step('Verify proxy host exists', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
});
test('should restore access lists from backup', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create access list that would be in a backup
const acl = generateAccessList();
await testData.createAccessList({
name: acl.name,
type: acl.type,
ipRules: acl.ipRules,
});
await test.step('Verify access list exists', async () => {
await page.goto('/access-lists');
await waitForLoadingComplete(page);
await expect(page.getByText(acl.name)).toBeVisible();
});
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
});
test('should show restore confirmation warning', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify page content', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should perform full system restore', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create multiple resources
const proxyInput = generateProxyHost();
const acl = generateAccessList();
const createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await testData.createAccessList({
name: acl.name,
type: acl.type,
ipRules: acl.ipRules,
});
await test.step('Verify resources exist', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
});
});
// ===========================================================================
// Group D: Backup Verification (4 tests)
// ===========================================================================
test.describe('Group D: Backup Verification', () => {
test('should display backup details', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify backup list page', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should verify backup integrity', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify page loads', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should download backup file', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify download options exist', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should show backup size and date', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify backup metadata displayed', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
});
// ===========================================================================
// Group E: Error Handling (4 tests)
// ===========================================================================
test.describe('Group E: Error Handling', () => {
test('should handle backup creation failure gracefully', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify page loads', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should handle restore failure gracefully', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify page content', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should handle corrupted backup file', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify error handling UI', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should handle insufficient storage during backup', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to backup settings', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify settings page', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
});
});

View File

@@ -1,314 +0,0 @@
/**
* Import to Production E2E Tests (Phase 6.6)
*
* Tests for importing configurations from external sources
* (Caddyfile, NPM, JSON) into the production system.
*
* Test Categories (12-15 tests):
* - Group A: Caddyfile Import (4 tests)
* - Group B: NPM Import (4 tests)
* - Group C: JSON/Config Import (4 tests)
*
* API Endpoints:
* - POST /api/v1/import/caddyfile
* - POST /api/v1/import/npm
* - POST /api/v1/import/json
* - GET /api/v1/import/preview
*/
import { test, expect, loginUser, TEST_PASSWORD } from '../fixtures/auth-fixtures';
import { generateProxyHost } from '../fixtures/proxy-hosts';
import { generateAccessList } from '../fixtures/access-lists';
import {
waitForToast,
waitForLoadingComplete,
waitForAPIResponse,
waitForModal,
clickAndWaitForResponse,
} from '../utils/wait-helpers';
/**
* Selectors for Import pages
*/
const SELECTORS = {
// Import Page
importTitle: 'h1:has-text("Import"), h2:has-text("Import")',
importTypeSelect: 'select[name="import_type"], [data-testid="import-type"]',
fileUploadInput: 'input[type="file"], #file-upload',
textImportArea: 'textarea[name="config"], #config-input',
// Import Types
caddyfileTab: 'button:has-text("Caddyfile"), [data-testid="caddyfile-tab"]',
npmTab: 'button:has-text("NPM"), [data-testid="npm-tab"]',
jsonTab: 'button:has-text("JSON"), [data-testid="json-tab"]',
// Preview
previewSection: '[data-testid="import-preview"], .preview',
previewProxyHosts: '[data-testid="preview-proxy-hosts"], .preview-hosts',
previewAccessLists: '[data-testid="preview-access-lists"], .preview-acls',
previewCertificates: '[data-testid="preview-certificates"], .preview-certs',
// Actions
importButton: 'button:has-text("Import"), button[type="submit"]',
previewButton: 'button:has-text("Preview"), button:has-text("Validate")',
cancelButton: 'button:has-text("Cancel")',
// Status
importProgress: '[data-testid="import-progress"], .progress',
importStatus: '[data-testid="import-status"], .status',
importErrors: '[data-testid="import-errors"], .errors',
importWarnings: '[data-testid="import-warnings"], .warnings',
// Results
successMessage: '[data-testid="import-success"], .success',
importedCount: '[data-testid="imported-count"], .count',
skippedItems: '[data-testid="skipped-items"], .skipped',
};
/**
* Sample Caddyfile content for testing
*/
const SAMPLE_CADDYFILE = `
example.com {
reverse_proxy localhost:8080
}
api.example.com {
reverse_proxy localhost:3000
tls internal
}
`;
/**
* Sample NPM export JSON for testing
*/
const SAMPLE_NPM_EXPORT = {
proxy_hosts: [
{
domain_names: ['test.example.com'],
forward_host: '192.168.1.100',
forward_port: 80,
},
],
access_lists: [],
certificates: [],
};
test.describe('Import to Production E2E', () => {
// ===========================================================================
// Group A: Caddyfile Import (4 tests)
// ===========================================================================
test.describe('Group A: Caddyfile Import', () => {
test('should display Caddyfile import page', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to import page', async () => {
await page.goto('/tasks/import/caddyfile');
await waitForLoadingComplete(page);
});
await test.step('Verify import page loads', async () => {
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
});
test('should parse Caddyfile content', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to import page', async () => {
await page.goto('/tasks/import/caddyfile');
await waitForLoadingComplete(page);
});
await test.step('Verify import interface', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should preview Caddyfile import results', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to import page', async () => {
await page.goto('/tasks/import/caddyfile');
await waitForLoadingComplete(page);
});
await test.step('Verify Caddyfile import interface', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should import valid Caddyfile configuration', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to import page', async () => {
await page.goto('/tasks/import/caddyfile');
await waitForLoadingComplete(page);
});
await test.step('Verify import form exists', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
});
// ===========================================================================
// Group B: NPM Import (4 tests)
// ===========================================================================
test.describe('Group B: NPM Import', () => {
test('should display NPM import page', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to NPM import', async () => {
await page.goto('/tasks/import/npm');
await waitForLoadingComplete(page);
});
await test.step('Verify NPM import page', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should parse NPM export JSON', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to NPM import', async () => {
await page.goto('/tasks/import/npm');
await waitForLoadingComplete(page);
});
await test.step('Verify import interface exists', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should preview NPM import results', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to NPM import', async () => {
await page.goto('/tasks/import/npm');
await waitForLoadingComplete(page);
});
await test.step('Verify preview capability', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should import NPM proxy hosts and access lists', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to NPM import', async () => {
await page.goto('/tasks/import/npm');
await waitForLoadingComplete(page);
});
await test.step('Verify import form', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
});
// ===========================================================================
// Group C: JSON/Config Import (4 tests)
// ===========================================================================
test.describe('Group C: JSON/Config Import', () => {
test('should display JSON import page', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to JSON import', async () => {
await page.goto('/tasks/import/json');
await waitForLoadingComplete(page);
});
await test.step('Verify JSON import page', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should validate JSON schema before import', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to JSON import', async () => {
await page.goto('/tasks/import/json');
await waitForLoadingComplete(page);
});
await test.step('Verify validation interface', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should handle import conflicts gracefully', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create existing proxy host that might conflict
const existingProxy = generateProxyHost();
await testData.createProxyHost({
domain: existingProxy.domain,
forwardHost: existingProxy.forwardHost,
forwardPort: existingProxy.forwardPort,
});
await test.step('Navigate to import page', async () => {
await page.goto('/tasks/import/caddyfile');
await waitForLoadingComplete(page);
});
await test.step('Verify conflict handling UI exists', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
test('should import complete configuration bundle', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to import page', async () => {
await page.goto('/tasks/import/caddyfile');
await waitForLoadingComplete(page);
});
await test.step('Verify import interface', async () => {
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});
});
});
});

View File

@@ -1,495 +0,0 @@
/**
* Multi-Feature Workflows E2E Tests (Phase 6.7)
*
* Tests for complex workflows that span multiple features,
* testing real-world usage scenarios and feature interactions.
*
* Test Categories (15-18 tests):
* - Group A: Complete Host Setup Workflow (5 tests)
* - Group B: Security Configuration Workflow (4 tests)
* - Group C: Certificate + DNS Workflow (4 tests)
* - Group D: Admin Management Workflow (5 tests)
*
* These tests verify end-to-end user journeys across features.
*/
import { test, expect, loginUser, TEST_PASSWORD } from '../fixtures/auth-fixtures';
import { generateProxyHost } from '../fixtures/proxy-hosts';
import { generateAccessList, generateAllowListForIPs } from '../fixtures/access-lists';
import { generateCertificate } from '../fixtures/certificates';
import { generateDnsProvider } from '../fixtures/dns-providers';
import {
waitForToast,
waitForLoadingComplete,
waitForAPIResponse,
waitForModal,
clickAndWaitForResponse,
waitForResourceInUI,
} from '../utils/wait-helpers';
/**
* Selectors for multi-feature workflows
*/
const SELECTORS = {
// Navigation
sideNav: '[data-testid="sidebar"], nav, .sidebar',
proxyHostsLink: 'a[href*="proxy-hosts"], button:has-text("Proxy Hosts")',
accessListsLink: 'a[href*="access-lists"], button:has-text("Access Lists")',
certificatesLink: 'a[href*="certificates"], button:has-text("Certificates")',
dnsProvidersLink: 'a[href*="dns"], button:has-text("DNS")',
securityLink: 'a[href*="security"], button:has-text("Security")',
settingsLink: 'a[href*="settings"], button:has-text("Settings")',
// Common Actions
addButton: 'button:has-text("Add"), button:has-text("Create")',
saveButton: 'button:has-text("Save"), button[type="submit"]',
deleteButton: 'button:has-text("Delete")',
editButton: 'button:has-text("Edit")',
cancelButton: 'button:has-text("Cancel")',
// Status Indicators
activeStatus: '.badge:has-text("Active"), [data-testid="status-active"]',
errorStatus: '.badge:has-text("Error"), [data-testid="status-error"]',
pendingStatus: '.badge:has-text("Pending"), [data-testid="status-pending"]',
// Common Elements
table: 'table, [data-testid="data-table"]',
modal: '.modal, [data-testid="modal"], [role="dialog"]',
toast: '[data-testid="toast"], .toast, [role="alert"]',
loadingSpinner: '[data-testid="loading"], .loading, .spinner',
};
test.describe('Multi-Feature Workflows E2E', () => {
// ===========================================================================
// Group A: Complete Host Setup Workflow (5 tests)
// ===========================================================================
test.describe('Group A: Complete Host Setup Workflow', () => {
test('should complete full proxy host setup with all features', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Step 1: Create access list for the host', async () => {
const acl = generateAllowListForIPs(['192.168.1.0/24']);
await testData.createAccessList(acl);
await page.goto('/access-lists');
await waitForResourceInUI(page, acl.name);
});
await test.step('Step 2: Create proxy host', async () => {
const proxyInput = generateProxyHost();
const proxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await page.goto('/proxy-hosts');
await waitForResourceInUI(page, proxy.domain);
});
await test.step('Step 3: Verify dashboard shows the host', async () => {
await page.goto('/');
await waitForLoadingComplete(page);
const content = page.locator('main, .content, h1').first();
await expect(content).toBeVisible();
});
});
test('should create proxy host with SSL certificate', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create proxy host', async () => {
const proxyInput = generateProxyHost();
const proxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await page.goto('/proxy-hosts');
await waitForResourceInUI(page, proxy.domain);
});
await test.step('Navigate to certificates', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should create proxy host with access restrictions', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create access list', async () => {
const acl = generateAccessList();
await testData.createAccessList(acl);
await page.goto('/access-lists');
await waitForResourceInUI(page, acl.name);
});
await test.step('Create proxy host', async () => {
const proxyInput = generateProxyHost();
const proxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await page.goto('/proxy-hosts');
await waitForResourceInUI(page, proxy.domain);
});
});
test('should update proxy host configuration end-to-end', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost();
const proxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await test.step('Navigate to proxy hosts', async () => {
await page.goto('/proxy-hosts');
await waitForResourceInUI(page, proxy.domain);
});
await test.step('Verify proxy host is editable', async () => {
const row = page.getByText(proxy.domain).locator('..').first();
await expect(row).toBeVisible();
});
});
test('should delete proxy host and verify cleanup', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost();
const proxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await test.step('Verify proxy host exists', async () => {
await page.goto('/proxy-hosts');
await waitForResourceInUI(page, proxy.domain);
});
});
});
// ===========================================================================
// Group B: Security Configuration Workflow (4 tests)
// ===========================================================================
test.describe('Group B: Security Configuration Workflow', () => {
test('should configure complete security stack for host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create proxy host', async () => {
const proxyInput = generateProxyHost();
const proxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await page.goto('/proxy-hosts');
await waitForResourceInUI(page, proxy.domain);
});
await test.step('Navigate to security settings', async () => {
await page.goto('/security');
await waitForLoadingComplete(page);
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should enable WAF and verify protection', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to WAF configuration', async () => {
await page.goto('/security/waf');
await waitForLoadingComplete(page);
});
await test.step('Verify WAF configuration page', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should configure CrowdSec integration', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to CrowdSec configuration', async () => {
await page.goto('/security/crowdsec');
await waitForLoadingComplete(page);
});
await test.step('Verify CrowdSec page loads', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should setup access restrictions workflow', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create restrictive ACL', async () => {
const acl = generateAllowListForIPs(['10.0.0.0/8']);
await testData.createAccessList(acl);
await page.goto('/access-lists');
await waitForResourceInUI(page, acl.name);
});
await test.step('Create protected proxy host', async () => {
const proxyInput = generateProxyHost();
const proxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await page.goto('/proxy-hosts');
await waitForResourceInUI(page, proxy.domain);
});
});
});
// ===========================================================================
// Group C: Certificate + DNS Workflow (4 tests)
// ===========================================================================
test.describe('Group C: Certificate + DNS Workflow', () => {
test('should setup DNS provider for certificate validation', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create DNS provider', async () => {
const dnsProvider = generateDnsProvider();
await testData.createDNSProvider({
name: dnsProvider.name,
providerType: dnsProvider.provider_type,
credentials: dnsProvider.credentials,
});
await page.goto('/dns-providers');
await waitForLoadingComplete(page);
await expect(page.getByText(dnsProvider.name)).toBeVisible();
});
});
test('should request certificate with DNS challenge', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create DNS provider first', async () => {
const dnsProvider = generateDnsProvider();
await testData.createDNSProvider({
name: dnsProvider.name,
providerType: dnsProvider.provider_type,
credentials: dnsProvider.credentials,
});
await page.goto('/dns-providers');
await waitForLoadingComplete(page);
await expect(page.getByText(dnsProvider.name)).toBeVisible();
});
await test.step('Navigate to certificates', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should apply certificate to proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create proxy host', async () => {
const proxyInput = generateProxyHost();
const proxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await page.goto('/proxy-hosts');
await waitForResourceInUI(page, proxy.domain);
});
await test.step('Navigate to certificates', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should verify certificate renewal workflow', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to certificates', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
});
await test.step('Verify certificate management page', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
});
// ===========================================================================
// Group D: Admin Management Workflow (5 tests)
// ===========================================================================
test.describe('Group D: Admin Management Workflow', () => {
test('should complete user management workflow', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to user management', async () => {
await page.goto('/settings/account-management');
await waitForLoadingComplete(page);
});
await test.step('Verify user management page', async () => {
const content = page.locator('main, .content, table').first();
await expect(content).toBeVisible();
});
});
test('should configure system settings', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to settings', async () => {
await page.goto('/settings');
await waitForLoadingComplete(page);
});
await test.step('Verify settings page', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should view audit logs for all operations', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to security dashboard', async () => {
await page.goto('/security');
await waitForLoadingComplete(page);
});
await test.step('Verify security page', async () => {
const content = page.locator('main, table, .content').first();
await expect(content).toBeVisible();
});
});
test('should perform system health check', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to dashboard', async () => {
await page.goto('/dashboard');
await waitForLoadingComplete(page);
});
await test.step('Verify dashboard loads', async () => {
await page.goto('/');
await waitForLoadingComplete(page);
const content = page.locator('main, .content, h1').first();
await expect(content).toBeVisible();
});
});
test('should complete backup before major changes', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create some data first
const proxyInput = generateProxyHost();
const proxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await test.step('Navigate to backups', async () => {
await page.goto('/tasks/backups');
await waitForLoadingComplete(page);
});
await test.step('Verify backup page loads', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
});
});

View File

@@ -1,799 +0,0 @@
/**
* Proxy + ACL Integration E2E Tests (Phase 6.1)
*
* Tests for proxy host and access list integration workflows.
* Covers ACL assignment, rule enforcement, dynamic updates, and edge cases.
*
* Test Categories (18-22 tests):
* - Group A: Basic ACL Assignment (5 tests)
* - Group B: ACL Rule Enforcement (5 tests)
* - Group C: Dynamic ACL Updates (4 tests)
* - Group D: Edge Cases (4 tests)
*
* API Endpoints:
* - GET/POST/PUT/DELETE /api/v1/access-lists
* - POST /api/v1/access-lists/:id/test
* - GET/POST/PUT/DELETE /api/v1/proxy-hosts
* - PUT /api/v1/proxy-hosts/:uuid
*/
import { test, expect, loginUser, TEST_PASSWORD } from '../fixtures/auth-fixtures';
import {
generateAccessList,
generateAllowListForIPs,
generateDenyListForIPs,
ipv6AccessList,
mixedRulesAccessList,
} from '../fixtures/access-lists';
import { generateProxyHost } from '../fixtures/proxy-hosts';
import {
waitForToast,
waitForLoadingComplete,
waitForAPIResponse,
clickAndWaitForResponse,
waitForModal,
retryAction,
} from '../utils/wait-helpers';
/**
* Selectors for ACL and Proxy Host pages
*/
const SELECTORS = {
// ACL Page
aclPageTitle: 'h1',
createAclButton: 'button:has-text("Create Access List"), button:has-text("Add Access List")',
aclTable: '[data-testid="access-list-table"], table',
aclRow: '[data-testid="access-list-row"], tbody tr',
aclDeleteBtn: '[data-testid="acl-delete-btn"], button[aria-label*="Delete"]',
aclEditBtn: '[data-testid="acl-edit-btn"], button[aria-label*="Edit"]',
// 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"], button:has-text("Edit")',
// Form Fields
aclNameInput: 'input[name="name"], #acl-name',
aclRuleTypeSelect: 'select[name="rule-type"], #rule-type',
aclRuleValueInput: 'input[name="rule-value"], #rule-value',
aclSelectDropdown: '[data-testid="acl-select"], select[name="access_list_id"]',
addRuleButton: 'button:has-text("Add Rule")',
// 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',
emptyState: '[data-testid="empty-state"]',
};
test.describe('Proxy + ACL Integration', () => {
// ===========================================================================
// Group A: Basic ACL Assignment (5 tests)
// ===========================================================================
test.describe('Group A: Basic ACL Assignment', () => {
test('should assign IP whitelist ACL to proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create an access list with IP whitelist rules
const aclConfig = generateAllowListForIPs(['192.168.1.0/24', '10.0.0.0/8']);
await test.step('Create access list via API', async () => {
await testData.createAccessList(aclConfig);
});
// Create a proxy host
const proxyConfig = generateProxyHost();
let proxyId: string;
let createdDomain: string;
await test.step('Create proxy host via API', async () => {
const result = await testData.createProxyHost({
domain: proxyConfig.domain,
forwardHost: proxyConfig.forwardHost,
forwardPort: proxyConfig.forwardPort,
name: proxyConfig.name,
});
proxyId = result.id;
createdDomain = result.domain;
});
await test.step('Navigate to proxy hosts page', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
});
await test.step('Edit proxy host to assign ACL', async () => {
// Find the proxy host row and click edit using the API-returned domain
const proxyRow = page.locator(SELECTORS.proxyRow).filter({
hasText: createdDomain,
});
await expect(proxyRow).toBeVisible();
const editButton = proxyRow.locator(SELECTORS.proxyEditBtn).first();
await editButton.click();
await waitForModal(page, /edit|proxy/i);
});
await test.step('Select the ACL from dropdown', async () => {
// The ACL dropdown is a native select element. Find it by looking for Access Control label
// and then finding the adjacent combobox/select
const aclDropdown = page.locator(SELECTORS.aclSelectDropdown);
const aclCombobox = page.getByRole('combobox').filter({ hasText: /No Access Control|whitelist/i });
// Build the pattern to match the ACL name (which may have namespace prefix)
const aclNamePattern = aclConfig.name;
if (await aclDropdown.isVisible()) {
// Find option that contains the ACL name pattern
const option = aclDropdown.locator('option').filter({ hasText: aclNamePattern });
const optionValue = await option.getAttribute('value');
if (optionValue) {
await aclDropdown.selectOption({ value: optionValue });
}
} else if (await aclCombobox.first().isVisible()) {
// Find option that contains the ACL name pattern
const selectElement = aclCombobox.first();
const option = selectElement.locator('option').filter({ hasText: aclNamePattern });
const optionValue = await option.getAttribute('value');
if (optionValue) {
await selectElement.selectOption({ value: optionValue });
}
} else {
throw new Error('Could not find ACL dropdown');
}
});
await test.step('Save and verify success', async () => {
const saveButton = page.locator(SELECTORS.saveButton);
await saveButton.click();
// Proxy host edits don't show a toast - verify success by waiting for loading to complete
// and ensuring the edit panel is no longer visible
await waitForLoadingComplete(page);
// Verify the edit panel closed by checking the main table is visible without the edit form
await expect(page.locator('[role="dialog"], h2:has-text("Edit")')).not.toBeVisible({ timeout: 5000 });
});
});
test('should assign geo-based whitelist ACL to proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create access list with geo rules
const aclConfig = generateAccessList({
name: 'Geo-Whitelist-Test',
type: 'geo_whitelist',
countryCodes: 'US,GB',
});
await test.step('Create geo-based access list', async () => {
await testData.createAccessList(aclConfig);
});
const proxyInput = generateProxyHost();
await test.step('Create and link proxy host via API', async () => {
await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
});
await test.step('Navigate and verify ACL can be assigned', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
// Verify the proxy host is visible - note: domain includes namespace from createProxyHost
// We navigate to proxy-hosts and check content since domain has namespace prefix
const content = page.locator('main, table, .content').first();
await expect(content).toBeVisible();
});
});
test('should assign deny-all blacklist ACL to proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const aclConfig = generateDenyListForIPs(['203.0.113.0/24', '198.51.100.0/24']);
await test.step('Create deny list ACL', async () => {
await testData.createAccessList(aclConfig);
});
const proxyInput = generateProxyHost();
let createdProxy: { domain: string };
await test.step('Create proxy host', async () => {
createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
});
await test.step('Verify proxy host and ACL are created', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
await expect(page.getByText(createdProxy.domain)).toBeVisible();
// Navigate to access lists to verify
await page.goto('/access-lists');
await waitForLoadingComplete(page);
await expect(page.getByText(aclConfig.name)).toBeVisible();
});
});
test('should unassign ACL from proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create ACL and proxy
const aclConfig = generateAccessList();
await testData.createAccessList(aclConfig);
const proxyInput = generateProxyHost();
const createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
name: proxyInput.name,
});
await test.step('Navigate to proxy hosts', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
});
await test.step('Edit proxy host to unassign ACL', async () => {
const proxyRow = page.locator(SELECTORS.proxyRow).filter({
hasText: createdProxy.domain,
});
await expect(proxyRow).toBeVisible({ timeout: 10000 });
const editButton = proxyRow.locator(SELECTORS.proxyEditBtn).first();
await editButton.click();
await waitForModal(page, /edit|proxy/i);
});
await test.step('Clear ACL selection', async () => {
const aclDropdown = page.locator(SELECTORS.aclSelectDropdown);
const aclCombobox = page.getByRole('combobox').filter({ hasText: /No Access Control|whitelist/i });
if (await aclDropdown.isVisible()) {
await aclDropdown.selectOption({ value: '' });
} else if (await aclCombobox.first().isVisible()) {
// Select the "No Access Control (Public)" option (empty value)
await aclCombobox.first().selectOption({ index: 0 });
} else {
// Try clearing a combobox with a clear button
const clearButton = page.locator('[aria-label*="clear"], [data-testid="clear-acl"]');
if (await clearButton.isVisible()) {
await clearButton.click();
}
}
});
await test.step('Save changes', async () => {
const saveButton = page.locator(SELECTORS.saveButton);
await saveButton.click();
// Proxy host edits don't show a toast - verify success by waiting for loading to complete
// and ensuring the edit panel is no longer visible
await waitForLoadingComplete(page);
await expect(page.locator('[role="dialog"], h2:has-text("Edit")')).not.toBeVisible({ timeout: 5000 });
});
});
test('should display ACL assignment in proxy host details', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const aclConfig = generateAccessList({ name: 'Display-Test-ACL' });
const { id: aclId, name: aclName } = await testData.createAccessList(aclConfig);
const proxyInput = generateProxyHost();
const createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await test.step('Navigate to proxy hosts and verify display', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
// The proxy row should be visible
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
});
});
// ===========================================================================
// Group B: ACL Rule Enforcement (5 tests)
// ===========================================================================
test.describe('Group B: ACL Rule Enforcement', () => {
test('should test IP against ACL rules using test endpoint', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create ACL with specific allow rules
const aclConfig = generateAllowListForIPs(['192.168.1.0/24']);
const { id: aclId } = await testData.createAccessList(aclConfig);
await test.step('Navigate to access lists', async () => {
await page.goto('/access-lists');
await waitForLoadingComplete(page);
});
await test.step('Test allowed IP via API', async () => {
// Use API to test IP
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '192.168.1.50' },
});
expect(response.ok()).toBeTruthy();
const result = await response.json();
expect(result.allowed).toBe(true);
});
await test.step('Test denied IP via API', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '10.0.0.1' },
});
expect(response.ok()).toBeTruthy();
const result = await response.json();
expect(result.allowed).toBe(false);
});
});
test('should enforce CIDR range rules correctly', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const aclConfig = generateAccessList({
name: 'CIDR-Test-ACL',
type: 'whitelist',
ipRules: [{ cidr: '10.0.0.0/8' }],
});
const { id: aclId } = await testData.createAccessList(aclConfig);
await test.step('Test IP within CIDR range', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '10.50.100.200' },
});
expect(response.ok()).toBeTruthy();
const result = await response.json();
expect(result.allowed).toBe(true);
});
await test.step('Test IP outside CIDR range', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '11.0.0.1' },
});
expect(response.ok()).toBeTruthy();
const result = await response.json();
expect(result.allowed).toBe(false);
});
});
test('should enforce RFC1918 private network rules', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// RFC1918 ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
const aclConfig = generateAccessList({
name: 'RFC1918-ACL',
type: 'whitelist',
ipRules: [
{ cidr: '10.0.0.0/8' },
{ cidr: '172.16.0.0/12' },
{ cidr: '192.168.0.0/16' },
],
});
const { id: aclId } = await testData.createAccessList(aclConfig);
await test.step('Test 10.x.x.x private IP', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '10.1.1.1' },
});
const result = await response.json();
expect(result.allowed).toBe(true);
});
await test.step('Test 172.16.x.x private IP', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '172.20.5.5' },
});
const result = await response.json();
expect(result.allowed).toBe(true);
});
await test.step('Test 192.168.x.x private IP', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '192.168.10.100' },
});
const result = await response.json();
expect(result.allowed).toBe(true);
});
await test.step('Test public IP (should be denied)', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '8.8.8.8' },
});
const result = await response.json();
expect(result.allowed).toBe(false);
});
});
test('should block denied IP from deny-only list', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const aclConfig = generateDenyListForIPs(['203.0.113.50']);
const { id: aclId } = await testData.createAccessList(aclConfig);
await test.step('Test denied IP', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '203.0.113.50' },
});
const result = await response.json();
expect(result.allowed).toBe(false);
});
await test.step('Test non-denied IP', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '192.168.1.1' },
});
const result = await response.json();
expect(result.allowed).toBe(true);
});
});
test('should allow whitelisted IP from allow-only list', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const aclConfig = generateAllowListForIPs(['192.168.1.100', '192.168.1.101']);
const { id: aclId } = await testData.createAccessList(aclConfig);
await test.step('Test first whitelisted IP', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '192.168.1.100' },
});
const result = await response.json();
expect(result.allowed).toBe(true);
});
await test.step('Test second whitelisted IP', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '192.168.1.101' },
});
const result = await response.json();
expect(result.allowed).toBe(true);
});
await test.step('Test non-whitelisted IP', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '192.168.1.102' },
});
const result = await response.json();
expect(result.allowed).toBe(false);
});
});
});
// ===========================================================================
// Group C: Dynamic ACL Updates (4 tests)
// ===========================================================================
test.describe('Group C: Dynamic ACL Updates', () => {
test('should apply ACL changes immediately', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create initial ACL
const aclConfig = generateAllowListForIPs(['192.168.1.0/24']);
const { id: aclId } = await testData.createAccessList(aclConfig);
await test.step('Verify initial rule behavior', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '10.0.0.1' },
});
const result = await response.json();
expect(result.allowed).toBe(false);
});
await test.step('Update ACL to add new allowed range', async () => {
const updateResponse = await page.request.put(`/api/v1/access-lists/${aclId}`, {
data: {
name: aclConfig.name,
type: 'whitelist',
ip_rules: JSON.stringify([
{ cidr: '192.168.1.0/24' },
{ cidr: '10.0.0.0/8' },
]),
},
});
expect(updateResponse.ok()).toBeTruthy();
});
await test.step('Verify new rules are immediately effective', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '10.0.0.1' },
});
const result = await response.json();
expect(result.allowed).toBe(true);
});
});
test('should handle ACL enable/disable toggle', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const aclConfig = generateAccessList({ name: 'Toggle-Test-ACL' });
const { id: aclId } = await testData.createAccessList(aclConfig);
await test.step('Navigate to access lists', async () => {
await page.goto('/access-lists');
await waitForLoadingComplete(page);
});
await test.step('Verify ACL is in list', async () => {
await expect(page.getByText(aclConfig.name)).toBeVisible();
});
});
test('should handle ACL deletion with proxy host fallback', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create ACL
const aclConfig = generateAccessList({ name: 'Delete-Test-ACL' });
const { id: aclId } = await testData.createAccessList(aclConfig);
// Create proxy host
const proxyInput = generateProxyHost();
const createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await test.step('Navigate to access lists', async () => {
await page.goto('/access-lists');
await waitForLoadingComplete(page);
});
await test.step('Delete the ACL via API', async () => {
const deleteResponse = await page.request.delete(`/api/v1/access-lists/${aclId}`);
expect(deleteResponse.ok()).toBeTruthy();
});
await test.step('Verify proxy host still works without ACL', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
});
test('should handle bulk ACL update on multiple proxy hosts', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create ACL
const aclConfig = generateAccessList({ name: 'Bulk-Update-ACL' });
await testData.createAccessList(aclConfig);
// Create multiple proxy hosts
const proxy1Input = generateProxyHost();
const proxy2Input = generateProxyHost();
const createdProxy1 = await testData.createProxyHost({
domain: proxy1Input.domain,
forwardHost: proxy1Input.forwardHost,
forwardPort: proxy1Input.forwardPort,
});
const createdProxy2 = await testData.createProxyHost({
domain: proxy2Input.domain,
forwardHost: proxy2Input.forwardHost,
forwardPort: proxy2Input.forwardPort,
});
await test.step('Verify both proxy hosts are created', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
await expect(page.getByText(createdProxy1.domain)).toBeVisible();
await expect(page.getByText(createdProxy2.domain)).toBeVisible();
});
});
});
// ===========================================================================
// Group D: Edge Cases (4 tests)
// ===========================================================================
test.describe('Group D: Edge Cases', () => {
test('should handle IPv6 addresses in ACL rules', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const aclConfig = {
name: `IPv6-Test-ACL-${Date.now()}`,
type: 'whitelist' as const,
ipRules: [
{ cidr: '::1/128' },
{ cidr: 'fe80::/10' },
],
};
const { id: aclId } = await testData.createAccessList(aclConfig);
await test.step('Test IPv6 localhost', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '::1' },
});
expect(response.ok()).toBeTruthy();
const result = await response.json();
expect(result.allowed).toBe(true);
});
await test.step('Test link-local IPv6', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: 'fe80::1' },
});
expect(response.ok()).toBeTruthy();
const result = await response.json();
expect(result.allowed).toBe(true);
});
});
test('should preserve ACL assignment when updating other proxy host fields', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create ACL
const aclConfig = generateAccessList({ name: 'Preserve-ACL-Test' });
const { id: aclId } = await testData.createAccessList(aclConfig);
// Create proxy host
const proxyConfig = generateProxyHost();
const { id: proxyId } = await testData.createProxyHost({
domain: proxyConfig.domain,
forwardHost: proxyConfig.forwardHost,
forwardPort: proxyConfig.forwardPort,
});
await test.step('Navigate to proxy hosts', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
});
await test.step('Verify proxy host exists', async () => {
await expect(page.getByText(proxyConfig.domain)).toBeVisible();
});
// The test verifies that editing non-ACL fields doesn't clear ACL assignment
// This would be tested via API to ensure the ACL ID is preserved
});
test('should handle conflicting allow/deny rules with precedence', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// For whitelist: the IPs listed are the ONLY ones allowed
const aclConfig = {
name: `Conflict-Test-ACL-${Date.now()}`,
type: 'whitelist' as const,
ipRules: [
{ cidr: '192.168.1.100/32' },
],
};
const { id: aclId } = await testData.createAccessList(aclConfig);
await test.step('Specific allowed IP should be allowed', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '192.168.1.100' },
});
const result = await response.json();
expect(result.allowed).toBe(true);
});
await test.step('Other IPs in denied subnet should be denied', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '192.168.1.50' },
});
const result = await response.json();
expect(result.allowed).toBe(false);
});
await test.step('IPs outside whitelisted range should be denied', async () => {
const response = await page.request.post(`/api/v1/access-lists/${aclId}/test`, {
data: { ip_address: '10.0.0.1' },
});
const result = await response.json();
expect(result.allowed).toBe(false);
});
});
test('should log ACL enforcement decisions in audit log', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const aclConfig = generateAccessList({ name: 'Audit-Log-Test-ACL' });
await testData.createAccessList(aclConfig);
await test.step('Navigate to security dashboard', async () => {
await page.goto('/security');
await waitForLoadingComplete(page);
});
await test.step('Verify security page loads', async () => {
// Verify the page has content (heading or table)
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
});
});
});

View File

@@ -1,493 +0,0 @@
/**
* Proxy + Certificate Integration E2E Tests (Phase 6.2)
*
* Tests for proxy host and SSL certificate integration workflows.
* Covers certificate assignment, ACME challenges, renewal, and edge cases.
*
* Test Categories (15-18 tests):
* - Group A: Certificate Assignment (4 tests)
* - Group B: ACME Flow Integration (4 tests)
* - Group C: Certificate Lifecycle (4 tests)
* - Group D: Error Handling & Edge Cases (4 tests)
*
* API Endpoints:
* - GET/POST/DELETE /api/v1/certificates
* - GET/POST/PUT/DELETE /api/v1/proxy-hosts
* - GET/POST /api/v1/dns-providers
*/
import { test, expect, loginUser, TEST_PASSWORD } from '../fixtures/auth-fixtures';
import {
generateCertificate,
generateWildcardCertificate,
customCertificateMock,
selfSignedTestCert,
letsEncryptCertificate,
} from '../fixtures/certificates';
import { generateProxyHost } from '../fixtures/proxy-hosts';
import {
waitForToast,
waitForLoadingComplete,
waitForAPIResponse,
waitForModal,
clickAndWaitForResponse,
} from '../utils/wait-helpers';
/**
* Selectors for Certificate and Proxy Host pages
*/
const SELECTORS = {
// Certificate Page
certPageTitle: 'h1',
uploadCertButton: 'button:has-text("Upload Certificate"), button:has-text("Add Certificate")',
requestCertButton: 'button:has-text("Request Certificate")',
certTable: '[data-testid="certificate-table"], table',
certRow: '[data-testid="certificate-row"], tbody tr',
certDeleteBtn: '[data-testid="cert-delete-btn"], button[aria-label*="Delete"]',
certViewBtn: '[data-testid="cert-view-btn"], button[aria-label*="View"]',
// 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
domainInput: 'input[name="domains"], #domains, input[placeholder*="domain"]',
certTypeSelect: 'select[name="type"], #cert-type',
certSelectDropdown: '[data-testid="cert-select"], select[name="certificate_id"]',
certFileInput: 'input[type="file"][name="certificate"]',
keyFileInput: 'input[type="file"][name="privateKey"]',
forceSSLCheckbox: 'input[name="force_ssl"], input[type="checkbox"][id*="ssl"]',
// 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',
certStatusBadge: '[data-testid="cert-status"], .badge',
expiryWarning: '[data-testid="expiry-warning"], .warning',
};
test.describe('Proxy + Certificate Integration', () => {
// ===========================================================================
// Group A: Certificate Assignment (4 tests)
// ===========================================================================
test.describe('Group A: Certificate Assignment', () => {
test('should assign custom certificate to proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create a proxy host first
const proxyInput = generateProxyHost({ scheme: 'https' });
let createdProxy: { domain: string };
await test.step('Create proxy host via API', async () => {
createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
scheme: 'https',
});
});
await test.step('Navigate to certificates page', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
});
await test.step('Verify certificates page loads', async () => {
const heading = page.locator(SELECTORS.certPageTitle).first();
await expect(heading).toContainText(/certificate/i);
});
await test.step('Navigate to proxy hosts and verify', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
});
test('should assign Let\'s Encrypt certificate to proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost({ scheme: 'https' });
let createdProxy: { domain: string };
await test.step('Create proxy host', async () => {
createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
scheme: 'https',
});
});
await test.step('Navigate to proxy hosts', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
});
await test.step('Verify proxy host is visible with HTTPS scheme', async () => {
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
});
test('should display SSL status indicator on proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost({ scheme: 'https', forceSSL: true });
let createdProxy: { domain: string };
await test.step('Create HTTPS proxy host', async () => {
createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
scheme: 'https',
});
});
await test.step('Navigate to proxy hosts', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
});
await test.step('Verify SSL indicator is shown', async () => {
const proxyRow = page.locator(SELECTORS.proxyRow).filter({
hasText: createdProxy.domain,
});
await expect(proxyRow).toBeVisible();
// Look for HTTPS or SSL indicator (lock icon, badge, etc.)
const sslIndicator = proxyRow.locator('svg[data-testid*="lock"], .ssl-indicator, [aria-label*="SSL"], [aria-label*="HTTPS"]');
// This may or may not be present depending on UI implementation
});
});
test('should unassign certificate from proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost({ scheme: 'http' });
let createdProxy: { domain: string };
await test.step('Create HTTP proxy host', async () => {
createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
scheme: 'http',
});
});
await test.step('Navigate to proxy hosts', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
});
});
// ===========================================================================
// Group B: ACME Flow Integration (4 tests)
// ===========================================================================
test.describe('Group B: ACME Flow Integration', () => {
test('should trigger HTTP-01 challenge for new certificate request', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost({ scheme: 'https' });
await test.step('Create proxy host for ACME challenge', async () => {
await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
scheme: 'https',
});
});
await test.step('Navigate to certificates page', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
});
await test.step('Verify certificates page is accessible', async () => {
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
});
test('should handle DNS-01 challenge for wildcard certificate', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// DNS provider is required for DNS-01 challenges
await test.step('Create DNS provider', async () => {
await testData.createDNSProvider({
providerType: 'manual',
name: 'Wildcard-DNS-Provider',
credentials: {},
});
});
await test.step('Navigate to certificates', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
});
await test.step('Verify page loads', async () => {
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
});
test('should show ACME challenge status during certificate issuance', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to certificates page', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
});
await test.step('Verify page structure', async () => {
// Check for certificate list or empty state
const content = page.locator('main, .content, [role="main"]').first();
await expect(content).toBeVisible();
});
});
test('should link DNS provider for automated DNS-01 challenges', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
await test.step('Create DNS provider', async () => {
await testData.createDNSProvider({
providerType: 'cloudflare',
name: 'Cloudflare-DNS-Test',
credentials: {
api_token: 'test-token-placeholder',
},
});
});
await test.step('Navigate to DNS providers', async () => {
await page.goto('/dns-providers');
await waitForLoadingComplete(page);
});
await test.step('Verify DNS provider exists', async () => {
// The provider name contains namespace prefix
const content = page.locator('main, table, .content').first();
await expect(content).toBeVisible();
});
});
});
// ===========================================================================
// Group C: Certificate Lifecycle (4 tests)
// ===========================================================================
test.describe('Group C: Certificate Lifecycle', () => {
test('should display certificate expiry warning', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to certificates page', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
});
await test.step('Verify certificates page loads', async () => {
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
});
test('should show certificate renewal option', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to certificates', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
});
// The renewal option would be visible on a Let's Encrypt certificate row
await test.step('Verify page is accessible', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should handle certificate deletion with proxy host fallback', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost({ scheme: 'http' });
let createdProxy: { domain: string };
await test.step('Create proxy host', async () => {
createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
});
await test.step('Verify proxy host works without certificate', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
});
test('should auto-renew expiring certificates', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
// This test verifies the auto-renewal configuration is visible
await test.step('Navigate to settings', async () => {
await page.goto('/settings');
await waitForLoadingComplete(page);
});
await test.step('Verify settings page loads', async () => {
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
});
});
// ===========================================================================
// Group D: Error Handling & Edge Cases (4 tests)
// ===========================================================================
test.describe('Group D: Error Handling & Edge Cases', () => {
test('should handle invalid certificate upload gracefully', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to certificates', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
});
await test.step('Verify page loads without errors', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should handle mismatched certificate and private key', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to certificates', async () => {
await page.goto('/certificates');
await waitForLoadingComplete(page);
});
await test.step('Verify certificates page', async () => {
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
});
test('should prevent assigning expired certificate to proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost();
let createdProxy: { domain: string };
await test.step('Create proxy host', async () => {
createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
});
await test.step('Navigate to proxy hosts', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
});
test('should handle domain mismatch between certificate and proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost();
let createdProxy: { domain: string };
await test.step('Create proxy host', async () => {
createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
});
await test.step('Navigate to proxy hosts', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
});
await test.step('Verify proxy host exists', async () => {
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
});
});
});

View File

@@ -1,384 +0,0 @@
/**
* Proxy + DNS Provider Integration E2E Tests (Phase 6.3)
*
* 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 {
waitForToast,
waitForLoadingComplete,
waitForAPIResponse,
waitForModal,
clickAndWaitForResponse,
} from '../utils/wait-helpers';
/**
* DNS Provider types supported by the system
*/
type DNSProviderType = 'manual' | 'cloudflare' | 'route53' | 'webhook' | 'rfc2136';
/**
* 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 page.goto('/dns-providers');
await waitForLoadingComplete(page);
});
await test.step('Verify DNS provider appears in list', async () => {
// The name is namespaced by TestDataManager
const content = page.locator('main, table, .content').first();
await expect(content).toBeVisible();
});
});
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 page.goto('/dns-providers');
await waitForLoadingComplete(page);
});
await test.step('Verify provider is listed', async () => {
const content = page.locator('main, table').first();
await expect(content).toBeVisible();
});
});
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 page.goto('/certificates');
await waitForLoadingComplete(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 page.goto('/dns-providers');
await waitForLoadingComplete(page);
});
await test.step('Verify DNS providers page loads', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
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 page.goto('/dns-providers');
await waitForLoadingComplete(page);
});
await test.step('Verify page content', async () => {
// Manual providers show instructions for DNS record creation
const content = page.locator('main, table, .content').first();
await expect(content).toBeVisible();
});
});
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 page.goto('/certificates');
await waitForLoadingComplete(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 page.goto('/dns-providers');
await waitForLoadingComplete(page);
});
await test.step('Verify provider in list', async () => {
const content = page.locator('main, table').first();
await expect(content).toBeVisible();
});
});
});
// ===========================================================================
// 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',
},
});
await test.step('Update provider credentials via API', async () => {
const response = await page.request.put(`/api/v1/dns-providers/${providerId}`, {
data: {
type: 'cloudflare',
name: 'Update-Credentials-DNS-Updated',
credentials: {
api_token: 'updated-token',
},
},
});
expect(response.ok()).toBeTruthy();
});
await test.step('Navigate to DNS providers', async () => {
await page.goto('/dns-providers');
await waitForLoadingComplete(page);
});
await test.step('Verify updated provider', async () => {
const content = page.locator('main, table').first();
await expect(content).toBeVisible();
});
});
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 page.goto('/dns-providers');
await waitForLoadingComplete(page);
});
await test.step('Verify provider exists before deletion', async () => {
const content = page.locator('main, table').first();
await expect(content).toBeVisible();
});
await test.step('Delete provider via API', async () => {
const response = await page.request.delete(`/api/v1/dns-providers/${providerId}`);
expect(response.ok()).toBeTruthy();
});
});
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 page.goto('/dns-providers');
await waitForLoadingComplete(page);
});
await test.step('Verify providers list', async () => {
const content = page.locator('main, table').first();
await expect(content).toBeVisible();
});
await test.step('Verify API returns providers', async () => {
const response = await page.request.get('/api/v1/dns-providers');
expect(response.ok()).toBeTruthy();
const data = await response.json();
const providers = data.providers || data.items || data;
expect(Array.isArray(providers)).toBe(true);
});
});
});
});

View File

@@ -1,544 +0,0 @@
/**
* Security Suite Integration E2E Tests (Phase 6.4)
*
* Tests for Cerberus security suite integration including WAF, CrowdSec,
* ACLs, and security headers working together.
*
* Test Categories (23-28 tests):
* - Group A: Cerberus Dashboard (4 tests)
* - Group B: WAF + Proxy Integration (5 tests)
* - Group C: CrowdSec + Proxy Integration (6 tests)
* - Group D: Security Headers Integration (4 tests)
* - Group E: Combined Security Features (4 tests)
*
* API Endpoints:
* - GET/PUT /api/v1/cerberus/config
* - GET /api/v1/cerberus/status
* - GET/POST /api/v1/crowdsec/*
* - GET/PUT /api/v1/security-headers
* - GET /api/v1/audit-logs
*/
import { test, expect, loginUser, TEST_PASSWORD } from '../fixtures/auth-fixtures';
import { generateProxyHost } from '../fixtures/proxy-hosts';
import { generateAccessList, generateAllowListForIPs } from '../fixtures/access-lists';
import {
waitForToast,
waitForLoadingComplete,
waitForAPIResponse,
waitForModal,
clickAndWaitForResponse,
} from '../utils/wait-helpers';
/**
* Selectors for Security pages
*/
const SELECTORS = {
// Cerberus Dashboard
cerberusTitle: 'h1, h2',
securityStatusCard: '[data-testid="security-status"], .security-status',
wafStatusIndicator: '[data-testid="waf-status"], .waf-status',
crowdsecStatusIndicator: '[data-testid="crowdsec-status"], .crowdsec-status',
aclStatusIndicator: '[data-testid="acl-status"], .acl-status',
// WAF Configuration
wafEnableToggle: 'input[name="waf_enabled"], [data-testid="waf-toggle"]',
wafModeSelect: 'select[name="waf_mode"], [data-testid="waf-mode"]',
wafRulesTable: '[data-testid="waf-rules-table"], table',
// CrowdSec Configuration
crowdsecEnableToggle: 'input[name="crowdsec_enabled"], [data-testid="crowdsec-toggle"]',
crowdsecApiKey: 'input[name="crowdsec_api_key"], #crowdsec-api-key',
crowdsecDecisionsList: '[data-testid="crowdsec-decisions"], .decisions-list',
crowdsecImportBtn: 'button:has-text("Import CrowdSec")',
// Security Headers
hstsToggle: 'input[name="hsts_enabled"], [data-testid="hsts-toggle"]',
cspInput: 'textarea[name="csp"], #csp-policy',
xfoSelect: 'select[name="x_frame_options"], #x-frame-options',
// Audit Logs
auditLogTable: '[data-testid="audit-log-table"], table',
auditLogRow: '[data-testid="audit-log-row"], tbody tr',
auditLogFilter: '[data-testid="audit-filter"], .filter',
// Common
saveButton: 'button:has-text("Save"), button[type="submit"]',
loadingSkeleton: '[data-testid="loading-skeleton"], .loading',
statusBadge: '.badge, [data-testid="status-badge"]',
};
test.describe('Security Suite Integration', () => {
// ===========================================================================
// Group A: Cerberus Dashboard (4 tests)
// ===========================================================================
test.describe('Group A: Cerberus Dashboard', () => {
test('should display Cerberus security dashboard', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to security dashboard', async () => {
await page.goto('/security');
await waitForLoadingComplete(page);
});
await test.step('Verify dashboard heading', async () => {
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
await test.step('Verify main content loads', async () => {
const content = page.locator('main, .content, [role="main"]').first();
await expect(content).toBeVisible();
});
});
test('should show WAF status indicator', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to WAF configuration', async () => {
await page.goto('/security/waf');
await waitForLoadingComplete(page);
});
await test.step('Verify WAF page loads', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should show CrowdSec connection status', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to CrowdSec configuration', async () => {
await page.goto('/security/crowdsec');
await waitForLoadingComplete(page);
});
await test.step('Verify CrowdSec page loads', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should display overall security score', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to security dashboard', async () => {
await page.goto('/security');
await waitForLoadingComplete(page);
});
await test.step('Verify security content', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
});
// ===========================================================================
// Group B: WAF + Proxy Integration (5 tests)
// ===========================================================================
test.describe('Group B: WAF + Proxy Integration', () => {
test('should enable WAF for proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost();
const createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await test.step('Navigate to proxy hosts', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
});
await test.step('Verify proxy host exists', async () => {
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
});
test('should configure WAF paranoia level', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to WAF config', async () => {
await page.goto('/security/waf');
await waitForLoadingComplete(page);
});
await test.step('Verify WAF configuration page', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should display WAF rule violations in logs', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to security dashboard', async () => {
await page.goto('/security');
await waitForLoadingComplete(page);
});
await test.step('Verify security page', async () => {
const content = page.locator('main, table, .content').first();
await expect(content).toBeVisible();
});
});
test('should block SQL injection attempts', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to WAF page', async () => {
await page.goto('/security/waf');
await waitForLoadingComplete(page);
});
await test.step('Verify page loads', async () => {
const heading = page.locator('h1, h2').first();
await expect(heading).toBeVisible();
});
});
test('should block XSS attempts', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to WAF configuration', async () => {
await page.goto('/security/waf');
await waitForLoadingComplete(page);
});
await test.step('Verify WAF page content', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
});
// ===========================================================================
// Group C: CrowdSec + Proxy Integration (6 tests)
// ===========================================================================
test.describe('Group C: CrowdSec + Proxy Integration', () => {
test('should display CrowdSec decisions', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to CrowdSec decisions', async () => {
await page.goto('/security/crowdsec');
await waitForLoadingComplete(page);
});
await test.step('Verify CrowdSec page', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should show CrowdSec configuration options', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to CrowdSec config', async () => {
await page.goto('/security/crowdsec');
await waitForLoadingComplete(page);
});
await test.step('Verify configuration section', async () => {
const content = page.locator('main, .content, form').first();
await expect(content).toBeVisible();
});
});
test('should display banned IPs from CrowdSec', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to CrowdSec page', async () => {
await page.goto('/security/crowdsec');
await waitForLoadingComplete(page);
});
await test.step('Verify CrowdSec page', async () => {
const content = page.locator('main, table, .content').first();
await expect(content).toBeVisible();
});
});
test('should import CrowdSec configuration', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to CrowdSec page', async () => {
await page.goto('/security/crowdsec');
await waitForLoadingComplete(page);
});
await test.step('Verify import option exists', async () => {
// Import functionality should be available on the page
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should show CrowdSec alerts timeline', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to CrowdSec', async () => {
await page.goto('/security/crowdsec');
await waitForLoadingComplete(page);
});
await test.step('Verify page content', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should integrate CrowdSec with proxy host blocking', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost();
const createdProxy = await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await test.step('Navigate to proxy hosts', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
});
await test.step('Verify proxy host exists', async () => {
await expect(page.getByText(createdProxy.domain)).toBeVisible();
});
});
});
// ===========================================================================
// Group D: Security Headers Integration (4 tests)
// ===========================================================================
test.describe('Group D: Security Headers Integration', () => {
test('should configure HSTS header for proxy host', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to security headers', async () => {
await page.goto('/security/headers');
await waitForLoadingComplete(page);
});
await test.step('Verify security headers page', async () => {
const content = page.locator('main, .content, form').first();
await expect(content).toBeVisible();
});
});
test('should configure Content-Security-Policy', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to security headers', async () => {
await page.goto('/security/headers');
await waitForLoadingComplete(page);
});
await test.step('Verify page content', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should configure X-Frame-Options header', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to security headers', async () => {
await page.goto('/security/headers');
await waitForLoadingComplete(page);
});
await test.step('Verify headers configuration', async () => {
const content = page.locator('main, form, .content').first();
await expect(content).toBeVisible();
});
});
test('should apply security headers to proxy host', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
const proxyInput = generateProxyHost();
await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await test.step('Navigate to security headers', async () => {
await page.goto('/security/headers');
await waitForLoadingComplete(page);
});
await test.step('Verify configuration page', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
});
// ===========================================================================
// Group E: Combined Security Features (4 tests)
// ===========================================================================
test.describe('Group E: Combined Security Features', () => {
test('should enable all security features simultaneously', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create proxy host with ACL
const aclConfig = generateAllowListForIPs(['192.168.1.0/24']);
await testData.createAccessList(aclConfig);
const proxyInput = generateProxyHost();
await testData.createProxyHost({
domain: proxyInput.domain,
forwardHost: proxyInput.forwardHost,
forwardPort: proxyInput.forwardPort,
});
await test.step('Navigate to security dashboard', async () => {
await page.goto('/security');
await waitForLoadingComplete(page);
});
await test.step('Verify security dashboard', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should log all security events in audit log', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to security dashboard', async () => {
await page.goto('/security');
await waitForLoadingComplete(page);
});
await test.step('Verify security page loads', async () => {
const content = page.locator('main, table, .content').first();
await expect(content).toBeVisible();
});
});
test('should display security notifications', async ({
page,
adminUser,
}) => {
await loginUser(page, adminUser);
await test.step('Navigate to notifications', async () => {
await page.goto('/settings/notifications');
await waitForLoadingComplete(page);
});
await test.step('Verify notifications page', async () => {
const content = page.locator('main, .content').first();
await expect(content).toBeVisible();
});
});
test('should enforce security policy across all proxy hosts', async ({
page,
adminUser,
testData,
}) => {
await loginUser(page, adminUser);
// Create multiple proxy hosts
const proxy1Input = generateProxyHost();
const proxy2Input = generateProxyHost();
const createdProxy1 = await testData.createProxyHost({
domain: proxy1Input.domain,
forwardHost: proxy1Input.forwardHost,
forwardPort: proxy1Input.forwardPort,
});
const createdProxy2 = await testData.createProxyHost({
domain: proxy2Input.domain,
forwardHost: proxy2Input.forwardHost,
forwardPort: proxy2Input.forwardPort,
});
await test.step('Navigate to proxy hosts', async () => {
await page.goto('/proxy-hosts');
await waitForLoadingComplete(page);
});
await test.step('Verify both proxy hosts exist', async () => {
await expect(page.getByText(createdProxy1.domain)).toBeVisible();
await expect(page.getByText(createdProxy2.domain)).toBeVisible();
});
});
});
});