Add Vault, Consul, eBPF connector plugins and thorough integration e2e tests
Backend: - Add SecretsManager=9 type, Vault=550 and Consul=551 providers to IntegrationEnums - Create VaultConnectorPlugin (GET /v1/sys/health), ConsulConnectorPlugin (GET /v1/status/leader), EbpfAgentConnectorPlugin (GET /api/v1/health) - Register all 3 plugins in Program.cs and WebService.csproj - Extend Concelier JobRegistrationExtensions with 20 additional advisory source connectors (ghsa, kev, epss, debian, ubuntu, alpine, suse, etc.) - Add connector project references to Concelier WebService.csproj so Type.GetType() can resolve job classes at runtime - Fix job kind names to match SourceDefinitions IDs (jpcert not jvn, oracle not vndr-oracle, etc.) Infrastructure: - Add Consul service to docker-compose.integrations.yml (127.1.2.8:8500) - Add runtime-host nginx fixture to docker-compose.integration-fixtures.yml (127.1.1.9:80) Frontend: - Mirror SecretsManager/Vault/Consul enum additions in integration.models.ts - Fix Secrets tab route type from RepoSource to SecretsManager - Add SecretsManager to parseType() and TYPE_DISPLAY_NAMES E2E tests (117/117 passing): - vault-consul-secrets.e2e.spec.ts: compose health, probes, CRUD, UI - runtime-hosts.e2e.spec.ts: fixture probe, CRUD, hosts tab - advisory-sync.e2e.spec.ts: 21 sources sync accepted, catalog, management - ui-onboarding-wizard.e2e.spec.ts: wizard steps for registry/scm/ci - ui-integration-detail.e2e.spec.ts: detail tabs, health data - ui-crud-operations.e2e.spec.ts: search, sort, delete - helpers.ts: shared configs, API helpers, screenshot util - Updated playwright.integrations.config.ts with reporter and CI retries Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Runtime Host Integration — End-to-End Tests
|
||||
*
|
||||
* Validates the full lifecycle for runtime-host integrations (eBPF Agent):
|
||||
* 1. Fixture compose health
|
||||
* 2. Direct endpoint probe
|
||||
* 3. Connector plugin API (create, test-connection, health, delete)
|
||||
* 4. UI: Runtimes / Hosts tab shows created integration
|
||||
*
|
||||
* Prerequisites:
|
||||
* - Main Stella Ops stack running
|
||||
* - docker-compose.integration-fixtures.yml (includes runtime-host-fixture)
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { test, expect } from './live-auth.fixture';
|
||||
import {
|
||||
INTEGRATION_CONFIGS,
|
||||
createIntegrationViaApi,
|
||||
cleanupIntegrations,
|
||||
snap,
|
||||
} from './helpers';
|
||||
|
||||
const BASE = process.env['PLAYWRIGHT_BASE_URL'] || 'https://stella-ops.local';
|
||||
const runId = process.env['E2E_RUN_ID'] || 'run1';
|
||||
|
||||
function dockerHealthy(containerName: string): boolean {
|
||||
try {
|
||||
const out = execSync(
|
||||
`docker ps --filter "name=${containerName}" --format "{{.Status}}"`,
|
||||
{ encoding: 'utf-8', timeout: 5_000 },
|
||||
).trim();
|
||||
return out.includes('(healthy)') || (out.startsWith('Up') && !out.includes('health: starting'));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 1. Compose Health
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test.describe('Runtime Host — Compose Health', () => {
|
||||
test('runtime-host-fixture container is healthy', () => {
|
||||
expect(
|
||||
dockerHealthy('stellaops-runtime-host-fixture'),
|
||||
'runtime-host-fixture should be healthy',
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 2. Direct Endpoint Probe
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test.describe('Runtime Host — Direct Probe', () => {
|
||||
test('eBPF agent /api/v1/health returns 200 with healthy status', async ({ playwright }) => {
|
||||
const ctx = await playwright.request.newContext({ ignoreHTTPSErrors: true });
|
||||
try {
|
||||
const resp = await ctx.get('http://127.1.1.9/api/v1/health', { timeout: 10_000 });
|
||||
expect(resp.status()).toBeLessThan(300);
|
||||
const body = await resp.json();
|
||||
expect(body.status).toBe('healthy');
|
||||
expect(body.agent).toBe('ebpf');
|
||||
expect(body.probes_loaded).toBeGreaterThan(0);
|
||||
} finally {
|
||||
await ctx.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
test('eBPF agent /api/v1/info returns agent details', async ({ playwright }) => {
|
||||
const ctx = await playwright.request.newContext({ ignoreHTTPSErrors: true });
|
||||
try {
|
||||
const resp = await ctx.get('http://127.1.1.9/api/v1/info', { timeout: 10_000 });
|
||||
expect(resp.status()).toBeLessThan(300);
|
||||
const body = await resp.json();
|
||||
expect(body.agent_type).toBe('ebpf');
|
||||
expect(body.probes).toBeDefined();
|
||||
expect(body.probes.length).toBeGreaterThan(0);
|
||||
} finally {
|
||||
await ctx.dispose();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 3. Connector Lifecycle (API)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test.describe('Runtime Host — Connector Lifecycle', () => {
|
||||
const createdIds: string[] = [];
|
||||
|
||||
test('create eBPF Agent integration returns 201', async ({ apiRequest }) => {
|
||||
const id = await createIntegrationViaApi(apiRequest, INTEGRATION_CONFIGS.ebpfAgent, runId);
|
||||
createdIds.push(id);
|
||||
expect(id).toBeTruthy();
|
||||
|
||||
const getResp = await apiRequest.get(`/api/v1/integrations/${id}`);
|
||||
expect(getResp.status()).toBe(200);
|
||||
const body = await getResp.json();
|
||||
expect(body.type).toBe(5); // RuntimeHost
|
||||
expect(body.provider).toBe(500); // EbpfAgent
|
||||
});
|
||||
|
||||
test('test-connection on eBPF Agent returns success', async ({ apiRequest }) => {
|
||||
expect(createdIds.length).toBeGreaterThan(0);
|
||||
const resp = await apiRequest.post(`/api/v1/integrations/${createdIds[0]}/test`);
|
||||
expect(resp.status()).toBe(200);
|
||||
const body = await resp.json();
|
||||
expect(body.success).toBe(true);
|
||||
});
|
||||
|
||||
test('health-check on eBPF Agent returns Healthy', async ({ apiRequest }) => {
|
||||
expect(createdIds.length).toBeGreaterThan(0);
|
||||
const resp = await apiRequest.get(`/api/v1/integrations/${createdIds[0]}/health`);
|
||||
expect(resp.status()).toBe(200);
|
||||
const body = await resp.json();
|
||||
expect(body.status).toBe(1); // Healthy
|
||||
});
|
||||
|
||||
test('list RuntimeHost integrations returns at least 1', async ({ apiRequest }) => {
|
||||
const resp = await apiRequest.get('/api/v1/integrations?type=5&pageSize=100');
|
||||
expect(resp.status()).toBe(200);
|
||||
const body = await resp.json();
|
||||
expect(body.totalCount).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
test.afterAll(async ({ apiRequest }) => {
|
||||
await cleanupIntegrations(apiRequest, createdIds);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 4. UI: Runtimes / Hosts Tab
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test.describe('Runtime Host — UI Verification', () => {
|
||||
const createdIds: string[] = [];
|
||||
|
||||
test.beforeAll(async ({ apiRequest }) => {
|
||||
const id = await createIntegrationViaApi(apiRequest, INTEGRATION_CONFIGS.ebpfAgent, `ui-${runId}`);
|
||||
createdIds.push(id);
|
||||
});
|
||||
|
||||
test('Runtimes / Hosts tab loads and shows integration', async ({ liveAuthPage: page }) => {
|
||||
await page.goto(`${BASE}/setup/integrations/runtime-hosts`, {
|
||||
waitUntil: 'networkidle',
|
||||
timeout: 30_000,
|
||||
});
|
||||
await page.waitForTimeout(2_000);
|
||||
|
||||
const heading = page.getByRole('heading', { name: /runtime host/i });
|
||||
await expect(heading).toBeVisible({ timeout: 5_000 });
|
||||
|
||||
const rows = page.locator('table tbody tr');
|
||||
const count = await rows.count();
|
||||
expect(count).toBeGreaterThanOrEqual(1);
|
||||
|
||||
await snap(page, 'runtime-hosts-tab');
|
||||
});
|
||||
|
||||
test.afterAll(async ({ apiRequest }) => {
|
||||
await cleanupIntegrations(apiRequest, createdIds);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user