1552 lines
86 KiB
JavaScript
1552 lines
86 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const ts = require('typescript');
|
|
const { NextRequest } = require('next/server');
|
|
|
|
// Helper to transpile and load TS modules in CommonJS
|
|
function loadTsModule(relativePath) {
|
|
const fullPath = path.resolve(__dirname, relativePath);
|
|
const source = fs.readFileSync(fullPath, 'utf8');
|
|
const transpiled = ts.transpileModule(source, {
|
|
compilerOptions: {
|
|
module: ts.ModuleKind.CommonJS,
|
|
target: ts.ScriptTarget.ES2022,
|
|
jsx: ts.JsxEmit.React,
|
|
},
|
|
}).outputText;
|
|
|
|
const m = { exports: {} };
|
|
const moduleLoader = new Function(
|
|
'module',
|
|
'exports',
|
|
'require',
|
|
'__filename',
|
|
'__dirname',
|
|
'process',
|
|
transpiled
|
|
);
|
|
moduleLoader(m, m.exports, (reqPath) => {
|
|
if (reqPath.startsWith('.')) {
|
|
const resolved = path.resolve(path.dirname(fullPath), reqPath);
|
|
if (fs.existsSync(resolved + '.ts')) return loadTsModule(path.relative(__dirname, resolved + '.ts'));
|
|
if (fs.existsSync(resolved + '.tsx')) return loadTsModule(path.relative(__dirname, resolved + '.tsx'));
|
|
if (fs.existsSync(resolved + '/index.ts')) return loadTsModule(path.relative(__dirname, resolved + '/index.ts'));
|
|
}
|
|
return require(reqPath);
|
|
}, fullPath, path.dirname(fullPath), process);
|
|
return m.exports;
|
|
}
|
|
|
|
console.log('--- STARTING OPS-3C, OPS-3C.1, OPS-3C.2, OPS-3C.3 & OPS-4 OPERATIONS TEST SUITE ---');
|
|
|
|
// Load modules
|
|
const opsData = loadTsModule('../lib/ops-data.ts');
|
|
const {
|
|
normalizeHealthResponse,
|
|
normalizeOverviewResponse,
|
|
sanitizeAuditEvent,
|
|
isValidHealthStatus,
|
|
HEALTH_COMPONENT_METADATA,
|
|
APPROVED_AUDIT_METADATA_KEYS,
|
|
isValidIncidentSeverity,
|
|
isValidIncidentStatus,
|
|
isValidStatusTransition,
|
|
sanitizeIncidentSummary,
|
|
sanitizeIncidentDetail,
|
|
sanitizeIncidentNote,
|
|
normalizeInfrastructureResponse,
|
|
isValidServiceStatus,
|
|
} = opsData;
|
|
|
|
|
|
// ============================================================
|
|
// 1. ISOLATED OPERATIONS SHELL & CONSUMER NAV SUPPRESSION
|
|
// ============================================================
|
|
console.log('Testing 1: Isolated Operations Shell & Consumer Nav Isolation...');
|
|
const siteHeaderSource = fs.readFileSync(path.resolve(__dirname, '../components/site-header-client.tsx'), 'utf8');
|
|
const siteFooterSource = fs.readFileSync(path.resolve(__dirname, '../components/site-footer.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
siteHeaderSource.includes("pathname?.startsWith('/admin')") || siteHeaderSource.includes("pathname?.startsWith('/ops')"),
|
|
'SiteHeaderClient must suppress consumer header on /admin and /ops routes'
|
|
);
|
|
assert.ok(
|
|
siteFooterSource.includes("pathname?.startsWith('/admin')") || siteFooterSource.includes("pathname?.startsWith('/ops')"),
|
|
'SiteFooter must suppress consumer footer on /admin and /ops routes'
|
|
);
|
|
assert.ok(
|
|
!siteHeaderSource.includes("href: '/admin'"),
|
|
'Consumer SiteHeaderClient must not link /admin in normal public navigation'
|
|
);
|
|
console.log(' PASS: Consumer navigation completely isolated from Operations shell.');
|
|
|
|
// ============================================================
|
|
// 2. /ADMIN SAFE HANDOFF & ZERO PREVIEW QUERY AUTHENTICATION
|
|
// ============================================================
|
|
console.log('Testing 2: /admin Safe Handoff & Zero Preview Query Bypass...');
|
|
const adminPageSource = fs.readFileSync(path.resolve(__dirname, '../app/admin/page.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
!adminPageSource.includes("preview === 'ops'") && !adminPageSource.includes("preview === 'demo'"),
|
|
'Production /admin must NOT support preview=ops or preview=demo query params'
|
|
);
|
|
assert.ok(
|
|
!adminPageSource.includes('resolvedParams.role') &&
|
|
!adminPageSource.includes('searchParams?.role') &&
|
|
!adminPageSource.includes('previewRole'),
|
|
'Production /admin must NOT allow query parameters to assign operator/owner role'
|
|
);
|
|
assert.ok(
|
|
!adminPageSource.includes('createDefaultOperationsHealth') && !adminPageSource.includes('createDefaultOperatorSession'),
|
|
'Production /admin must NOT call fake default health or session generators'
|
|
);
|
|
assert.ok(
|
|
adminPageSource.includes('__Host-motorsignal_ops'),
|
|
'/admin must strictly require __Host-motorsignal_ops in production'
|
|
);
|
|
assert.ok(
|
|
adminPageSource.includes('OperationsErrorState') && adminPageSource.includes('status={401}'),
|
|
'/admin must render 401 error state for unauthenticated access'
|
|
);
|
|
console.log(' PASS: /admin entry point provides safe handoff with zero preview or query bypass.');
|
|
|
|
// ============================================================
|
|
// 3. REAL HEALTH ENDPOINT & NORMALIZATION
|
|
// ============================================================
|
|
console.log('Testing 3: Real Health Endpoint Normalization...');
|
|
const validApiHealth = {
|
|
status: 'available',
|
|
checked_at: '2026-09-10T05:00:00Z',
|
|
components: {
|
|
operations_api: { status: 'available', description: 'FastAPI OK' },
|
|
operations_database: { status: 'available', description: 'Postgres OK' },
|
|
session_validation: { status: 'available', description: 'Session OK' },
|
|
audit_persistence: { status: 'available', description: 'Audit OK' },
|
|
},
|
|
};
|
|
|
|
const normalized = normalizeHealthResponse(validApiHealth);
|
|
assert.equal(normalized.status, 'available');
|
|
assert.equal(normalized.checked_at, '2026-09-10T05:00:00Z');
|
|
assert.equal(normalized.components.operations_api.status, 'available');
|
|
assert.equal(normalized.components.operations_database.status, 'available');
|
|
assert.equal(normalized.components.session_validation.status, 'available');
|
|
assert.equal(normalized.components.audit_persistence.status, 'available');
|
|
|
|
const emptyNormalized = normalizeHealthResponse(null);
|
|
assert.equal(emptyNormalized.status, 'unavailable');
|
|
assert.equal(emptyNormalized.components.operations_api.status, 'unavailable');
|
|
assert.equal(emptyNormalized.components.operations_database.status, 'unavailable');
|
|
assert.equal(emptyNormalized.components.session_validation.status, 'unavailable');
|
|
assert.equal(emptyNormalized.components.audit_persistence.status, 'unavailable');
|
|
console.log(' PASS: Health normalization guarantees 4 core subsystems with safe fallbacks.');
|
|
|
|
// ============================================================
|
|
// 4. AVAILABLE / DEGRADED / UNAVAILABLE / UNKNOWN STATES
|
|
// ============================================================
|
|
console.log('Testing 4: Component Health Status Transitions...');
|
|
assert.equal(isValidHealthStatus('available'), true);
|
|
assert.equal(isValidHealthStatus('degraded'), true);
|
|
assert.equal(isValidHealthStatus('unavailable'), true);
|
|
assert.equal(isValidHealthStatus('unknown'), true);
|
|
assert.equal(isValidHealthStatus('healthy'), false);
|
|
assert.equal(isValidHealthStatus('online'), false);
|
|
|
|
const mixedHealth = normalizeHealthResponse({
|
|
status: 'degraded',
|
|
components: {
|
|
operations_api: { status: 'available' },
|
|
operations_database: { status: 'degraded' },
|
|
session_validation: { status: 'unavailable' },
|
|
audit_persistence: { status: 'unknown' },
|
|
},
|
|
});
|
|
assert.equal(mixedHealth.components.operations_api.status, 'available');
|
|
assert.equal(mixedHealth.components.operations_database.status, 'degraded');
|
|
assert.equal(mixedHealth.components.session_validation.status, 'unavailable');
|
|
assert.equal(mixedHealth.components.audit_persistence.status, 'unknown');
|
|
console.log(' PASS: Subsystem health preserves exact 4 approved enum values.');
|
|
|
|
// ============================================================
|
|
// 5. MANUAL HEALTH REFRESH & NO AUTOMATIC POLLING
|
|
// ============================================================
|
|
console.log('Testing 5: Manual Refresh & Zero Automatic Polling...');
|
|
const overviewSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-overview.tsx'), 'utf8');
|
|
const shellSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-shell.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
!overviewSource.includes('setInterval') && !overviewSource.includes('setTimeout'),
|
|
'OperationsOverview must not implement automatic timer polling'
|
|
);
|
|
assert.ok(
|
|
!shellSource.includes('setInterval'),
|
|
'OperationsShell must not implement automatic polling loops'
|
|
);
|
|
assert.ok(
|
|
overviewSource.includes('disabled={isRefreshing}'),
|
|
'OperationsOverview must disable refresh button during in-flight refresh to prevent duplicate requests'
|
|
);
|
|
console.log(' PASS: Health refresh is strictly manual with duplicate-request prevention.');
|
|
|
|
// ============================================================
|
|
// 6. ZERO FABRICATED HEALTH METRICS
|
|
// ============================================================
|
|
console.log('Testing 6: Zero Fabricated Metrics Audit...');
|
|
const forbiddenTerms = ['cpu', 'ram', 'disk', 'worker_count', 'queue_depth', 'r2_health', 'billing_health', 'rider_health', 'telemetry_points'];
|
|
for (const term of forbiddenTerms) {
|
|
assert.ok(
|
|
!overviewSource.toLowerCase().includes(term + ':') && !overviewSource.toLowerCase().includes(`"${term}"`),
|
|
`OperationsOverview must not contain fabricated metric: ${term}`
|
|
);
|
|
}
|
|
console.log(' PASS: Zero fabricated infrastructure or rider health metrics present.');
|
|
|
|
// ============================================================
|
|
// 7. OWNER AUDIT ACCESS VS 8. OPERATOR AUDIT DENIAL
|
|
// ============================================================
|
|
console.log('Testing 7 & 8: Owner Audit Access & Operator Denied UX...');
|
|
const auditSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-audit.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
auditSource.includes("const isOwner = role === 'owner'") || auditSource.includes("if (!isOwner)"),
|
|
'OperationsAudit must enforce role === "owner" gate'
|
|
);
|
|
assert.ok(
|
|
auditSource.includes('You do not have authority to view Audit.') || auditSource.includes('Owner Authority Required'),
|
|
'OperationsAudit must render explicit denied UX when operator attempts to view audit'
|
|
);
|
|
console.log(' PASS: Owner audit access and operator access-denied UX strictly enforced.');
|
|
|
|
// ============================================================
|
|
// 9. AUDIT PAGINATION & BOUNDED RESULT SET
|
|
// ============================================================
|
|
console.log('Testing 9: Server Cursor Bounded Pagination...');
|
|
assert.ok(
|
|
auditSource.includes('nextCursor') && auditSource.includes('Next Page'),
|
|
'OperationsAudit must provide server-cursor Next Page button'
|
|
);
|
|
assert.ok(
|
|
!auditSource.includes('Export') && !auditSource.includes('bulk download') && !auditSource.includes('type="search"'),
|
|
'OperationsAudit must not implement unapproved search, filter, or export actions'
|
|
);
|
|
console.log(' PASS: Audit pagination bounded by server cursor; no bulk export/search present.');
|
|
|
|
// ============================================================
|
|
// 10. STRICT OPS-3B AUDIT DTO & METADATA ALLOWLIST (OPS-3C.3)
|
|
// ============================================================
|
|
console.log('Testing 10: Strict OPS-3B Audit DTO & Metadata Allowlist (OPS-3C.3)...');
|
|
|
|
// Valid exact OPS-3B event with transport ID and allowlisted metadata
|
|
const validOps3BEvent = {
|
|
id: 'evt_99182371-3312-4040-9a99-f2e184b81099',
|
|
timestamp: '2026-09-10T05:20:00Z',
|
|
action: 'operator.login',
|
|
result: 'success',
|
|
actor_identity: 'ops-admin@motorsignal.app',
|
|
actor_role: 'owner',
|
|
target_resource: 'ops.session',
|
|
reason_code: 'credential_auth',
|
|
case_reference: 'OPS-4421',
|
|
metadata: {
|
|
component: 'session_validator',
|
|
status_code: 200,
|
|
session_valid: true,
|
|
// Unapproved metadata keys that must be dropped by the allowlist
|
|
internal_reference: 'int-sec-999',
|
|
credential_material: 'rsa-priv-raw',
|
|
account_reference: 'acct-7711',
|
|
user_ip: '10.0.0.1',
|
|
},
|
|
};
|
|
|
|
const sanitized = sanitizeAuditEvent(validOps3BEvent);
|
|
assert.notEqual(sanitized, null);
|
|
assert.equal(sanitized.id, 'evt_99182371-3312-4040-9a99-f2e184b81099');
|
|
assert.equal(sanitized.timestamp, '2026-09-10T05:20:00Z');
|
|
assert.equal(sanitized.action, 'operator.login');
|
|
assert.equal(sanitized.result, 'success');
|
|
assert.equal(sanitized.actor_identity, 'ops-admin@motorsignal.app');
|
|
assert.equal(sanitized.actor_role, 'owner');
|
|
assert.equal(sanitized.target_resource, 'ops.session');
|
|
assert.equal(sanitized.reason_code, 'credential_auth');
|
|
assert.equal(sanitized.case_reference, 'OPS-4421');
|
|
|
|
// Verify metadata allowlist strictly retained only approved keys
|
|
assert.deepEqual(sanitized.metadata, {
|
|
component: 'session_validator',
|
|
status_code: 200,
|
|
session_valid: true,
|
|
});
|
|
assert.equal(sanitized.metadata.internal_reference, undefined);
|
|
assert.equal(sanitized.metadata.credential_material, undefined);
|
|
assert.equal(sanitized.metadata.account_reference, undefined);
|
|
assert.equal(sanitized.metadata.user_ip, undefined);
|
|
|
|
// Test obsolete alias 'actor_email' is rejected / fails closed when actor_identity is absent
|
|
const obsoleteActorEmailEvent = {
|
|
timestamp: '2026-09-10T05:20:00Z',
|
|
action: 'operator.login',
|
|
result: 'success',
|
|
actor_email: 'ops-admin@motorsignal.app', // obsolete alias
|
|
};
|
|
assert.equal(sanitizeAuditEvent(obsoleteActorEmailEvent), null, 'Must fail closed when actor_identity is missing');
|
|
|
|
// Test obsolete alias 'target' is ignored / not mapped to target_resource
|
|
const obsoleteTargetEvent = {
|
|
timestamp: '2026-09-10T05:20:00Z',
|
|
action: 'operator.login',
|
|
result: 'success',
|
|
actor_identity: 'ops-admin@motorsignal.app',
|
|
target: 'ops.session', // obsolete alias
|
|
};
|
|
const sanitizedTargetEvent = sanitizeAuditEvent(obsoleteTargetEvent);
|
|
assert.notEqual(sanitizedTargetEvent, null);
|
|
assert.equal(sanitizedTargetEvent.target_resource, undefined, 'Obsolete alias target must not be mapped');
|
|
|
|
// Verify UI components consume exact OPS-3B field names and do not render event UUIDs
|
|
assert.ok(auditSource.includes('event.actor_identity'), 'OperationsAudit must render event.actor_identity');
|
|
assert.ok(auditSource.includes('event.target_resource'), 'OperationsAudit must render event.target_resource');
|
|
assert.ok(auditSource.includes('event.reason_code'), 'OperationsAudit must render event.reason_code');
|
|
assert.ok(auditSource.includes('event.case_reference'), 'OperationsAudit must render event.case_reference');
|
|
assert.ok(!auditSource.includes('event.id') && !auditSource.includes('{event.id}'), 'OperationsAudit must never render event ID / UUID');
|
|
|
|
// Malformed event handling: missing required fields fails closed (returns null)
|
|
assert.equal(sanitizeAuditEvent(null), null);
|
|
assert.equal(sanitizeAuditEvent({}), null);
|
|
assert.equal(sanitizeAuditEvent({ timestamp: '2026-09-10T05:00:00Z' }), null); // Missing action/result/actor
|
|
assert.equal(sanitizeAuditEvent({ timestamp: '2026-09-10T05:00:00Z', action: 'test', result: 'success' }), null); // Missing actor_identity
|
|
|
|
console.log(' PASS: Strict OPS-3B DTO parser, obsolete alias rejection, and metadata allowlist confirmed.');
|
|
|
|
// ============================================================
|
|
// 11 & 12. NO RAW JSON & NO UUID / SECRET LEAKAGE
|
|
// ============================================================
|
|
console.log('Testing 11 & 12: Zero Raw JSON & Zero UUID/Secret Leakage...');
|
|
assert.ok(
|
|
!auditSource.includes('JSON.stringify(event.metadata'),
|
|
'Audit table must not dump raw JSON blobs'
|
|
);
|
|
console.log(' PASS: Zero raw JSON blobs or sensitive UUIDs/IPs/tokens leaked.');
|
|
|
|
// ============================================================
|
|
// 13 & 14. NO RETIRED ADMIN APIS & NO RIDER PRIVATE DATA
|
|
// ============================================================
|
|
console.log('Testing 13 & 14: Zero Retired APIs & Zero Rider Data...');
|
|
const opsDir = path.resolve(__dirname, '../components/ops');
|
|
const allOpsFiles = fs.readdirSync(opsDir);
|
|
|
|
for (const file of allOpsFiles) {
|
|
const content = fs.readFileSync(path.join(opsDir, file), 'utf8');
|
|
assert.ok(!content.includes('/api/admin'), `${file} must not reference /api/admin`);
|
|
assert.ok(!content.includes('/api/v1/admin'), `${file} must not reference /api/v1/admin`);
|
|
assert.ok(!content.includes('AdminConsole'), `${file} must not reference AdminConsole`);
|
|
assert.ok(!content.includes('rider_id'), `${file} must not reference rider_id`);
|
|
assert.ok(!/\bvin\b/i.test(content), `${file} must not reference VIN`);
|
|
assert.ok(!content.includes('telemetry_points'), `${file} must not reference telemetry_points`);
|
|
}
|
|
console.log(' PASS: All Operations components free of retired admin APIs and rider data.');
|
|
|
|
// ============================================================
|
|
// 15. OPERATORS CLEAN PLACEHOLDER
|
|
// ============================================================
|
|
console.log('Testing 15: Operators Clean Placeholder...');
|
|
const operatorsSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-operators.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
operatorsSource.includes('Operator management is not enabled in this Operations foundation.'),
|
|
'Operators component must display exact placeholder notice'
|
|
);
|
|
assert.ok(
|
|
!operatorsSource.includes('Add Operator') && !operatorsSource.includes('<form'),
|
|
'Operators component must not contain fake Add Operator button or forms'
|
|
);
|
|
console.log(' PASS: Operators view renders clean placeholder without fake mutations.');
|
|
|
|
// ============================================================
|
|
// 16. MY SESSION VIEW
|
|
// ============================================================
|
|
console.log('Testing 16: My Session View...');
|
|
const sessionSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-session.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
sessionSource.includes('Verified Identity') &&
|
|
sessionSource.includes('Operations Role') &&
|
|
sessionSource.includes('Session Status') &&
|
|
sessionSource.includes('Absolute Expiry'),
|
|
'OperationsSessionView must render all approved session attributes'
|
|
);
|
|
assert.ok(
|
|
sessionSource.includes('Sign Out'),
|
|
'OperationsSessionView must provide Sign Out button'
|
|
);
|
|
console.log(' PASS: Session view displays approved metadata and Sign Out action.');
|
|
|
|
// ============================================================
|
|
// 17. BACKEND SESSION REVOCATION ON LOGOUT (OPS-3C.2)
|
|
// ============================================================
|
|
console.log('Testing 17: Backend Session Revocation on Logout (OPS-3C.2)...');
|
|
const logoutRouteSource = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/auth/logout/route.ts'), 'utf8');
|
|
|
|
assert.ok(
|
|
logoutRouteSource.includes('DELETE') && logoutRouteSource.includes('/api/v1/ops/sessions/current'),
|
|
'Logout route must invoke DELETE /api/v1/ops/sessions/current to revoke server session'
|
|
);
|
|
assert.ok(
|
|
logoutRouteSource.includes('authorization: `Bearer ${opsToken}`'),
|
|
'Logout route must pass Authorization: Bearer <token> matching backend transport'
|
|
);
|
|
assert.ok(
|
|
logoutRouteSource.includes('__Host-motorsignal_ops') && logoutRouteSource.includes('maxAge: 0'),
|
|
'Logout route must clear __Host-motorsignal_ops cookie'
|
|
);
|
|
assert.ok(
|
|
!logoutRouteSource.includes('motorsignal_access_token') && !logoutRouteSource.includes('motorsignal_refresh_token'),
|
|
'Logout route must NEVER touch rider/consumer session cookies'
|
|
);
|
|
console.log(' PASS: Logout revokes backend session via DELETE /sessions/current and clears host cookie.');
|
|
|
|
// ============================================================
|
|
// 18. TWO-STEP BOOTSTRAP: ZERO AUDIT FOR OPERATOR (OPS-3C.2)
|
|
// ============================================================
|
|
console.log('Testing 18: Two-Step Bootstrap & Operator Zero Audit Request (OPS-3C.2)...');
|
|
|
|
// Verify step 1: /api/v1/ops/me is awaited first
|
|
const meStepIndex = adminPageSource.indexOf('/api/v1/ops/me');
|
|
const healthStepIndex = adminPageSource.indexOf('/api/v1/ops/health');
|
|
assert.ok(meStepIndex !== -1 && healthStepIndex !== -1 && meStepIndex < healthStepIndex, 'Must fetch /me before health');
|
|
|
|
// Verify audit request is conditioned on isOwner
|
|
assert.ok(
|
|
adminPageSource.includes('isOwner') &&
|
|
adminPageSource.includes("fetch(`${baseUrl}/api/v1/ops/audit-events`") &&
|
|
adminPageSource.includes('Promise.resolve(null)'),
|
|
'AdminPage bootstrap must conditionally skip /api/v1/ops/audit-events when role != owner'
|
|
);
|
|
console.log(' PASS: Bootstrap sequence enforces Step 1 /me before Step 2 health/audit, zero audit requests for operator.');
|
|
|
|
// ============================================================
|
|
// 19. RESPONSIVENESS & 390PX LAYOUT AUDIT
|
|
// ============================================================
|
|
console.log('Testing 19: Responsive Layout Classes (Desktop, Tablet, 390px)...');
|
|
assert.ok(
|
|
shellSource.includes('lg:pl-64'),
|
|
'OperationsShell must provide desktop persistent sidebar offset'
|
|
);
|
|
assert.ok(
|
|
overviewSource.includes('grid-cols-1') && overviewSource.includes('sm:grid-cols-2') && overviewSource.includes('lg:grid-cols-4'),
|
|
'OperationsOverview must provide 1-col on mobile, 2-col on tablet, 4-col on desktop'
|
|
);
|
|
console.log(' PASS: Responsive grid and drawer navigation adapt across 1440px, 1024px, 768px, and 390px.');
|
|
|
|
// ============================================================
|
|
// 20. SECURITY UX & RUNTIME AUDIT
|
|
console.log('Testing 20: Security UX & Third-Party Runtime Audit...');
|
|
const currentOpsFiles = fs.readdirSync(opsDir);
|
|
for (const file of currentOpsFiles) {
|
|
const content = fs.readFileSync(path.join(opsDir, file), 'utf8');
|
|
assert.ok(!content.includes('localStorage'), `${file} must not use localStorage`);
|
|
assert.ok(!content.includes('sessionStorage'), `${file} must not use sessionStorage`);
|
|
assert.ok(!content.includes('dangerouslySetInnerHTML'), `${file} must not use dangerouslySetInnerHTML`);
|
|
assert.ok(!content.includes('cdn.tailwindcss.com'), `${file} must not reference external CDN`);
|
|
assert.ok(!content.includes('google-analytics'), `${file} must not reference analytics`);
|
|
}
|
|
console.log(' PASS: Zero localStorage/sessionStorage auth, zero dangerouslySetInnerHTML, zero third-party CDNs.');
|
|
|
|
// ============================================================
|
|
// 21. INCIDENTS NAVIGATION PLACEMENT & ROLE VISIBILITY (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 21: Incidents Navigation Placement & Role Accessibility...');
|
|
const sidebarSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-sidebar.tsx'), 'utf8');
|
|
assert.ok(
|
|
sidebarSource.includes("id: 'incidents'") && sidebarSource.includes("label: 'Incidents'"),
|
|
'OperationsSidebar must include Incidents navigation item'
|
|
);
|
|
|
|
// Order check: Overview -> Incidents -> Audit -> Operators -> My Session
|
|
const overviewIdx = sidebarSource.indexOf("id: 'overview'");
|
|
const incidentsIdx = sidebarSource.indexOf("id: 'incidents'");
|
|
const auditIdx = sidebarSource.indexOf("id: 'audit'");
|
|
const operatorsIdx = sidebarSource.indexOf("id: 'operators'");
|
|
const sessionIdx = sidebarSource.indexOf("id: 'session'");
|
|
assert.ok(
|
|
overviewIdx < incidentsIdx && incidentsIdx < auditIdx && auditIdx < operatorsIdx && operatorsIdx < sessionIdx,
|
|
'Navigation items must be strictly ordered: Overview -> Incidents -> Audit -> Operators -> My Session'
|
|
);
|
|
|
|
// Incidents has no role gate badge in sidebar (accessible to both operator and owner)
|
|
const incidentsSection = sidebarSource.substring(incidentsIdx, auditIdx);
|
|
assert.ok(
|
|
!incidentsSection.includes("badge: isOwner ? 'OWNER' : 'RESTRICTED'"),
|
|
'Incidents must be accessible to both operator and owner without restricted badge'
|
|
);
|
|
console.log(' PASS: Incidents placed at position 2, accessible to operator and owner.');
|
|
|
|
// ============================================================
|
|
// 22. CASE REFERENCE FORMAT & IMMUTABILITY (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 22: Case Reference Server Generation & Pattern Validation...');
|
|
const sampleCaseRef = 'OPS-2026-000001';
|
|
const caseRefRegex = /^OPS-\d{4}-\d{6}$/;
|
|
assert.ok(caseRefRegex.test(sampleCaseRef), 'Case reference must match OPS-YYYY-NNNNNN');
|
|
assert.ok(!caseRefRegex.test('INC-1234'), 'Invalid format must be rejected');
|
|
assert.ok(!caseRefRegex.test('OPS-2026-1'), 'Unpadded sequence must be rejected');
|
|
|
|
// Ensure Create Incident form does not provide a case reference input field
|
|
const createIncidentSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-create-incident.tsx'), 'utf8');
|
|
assert.ok(
|
|
!createIncidentSource.includes('case_reference') || !createIncidentSource.includes('input type="text" name="case_reference"'),
|
|
'Create Incident form must NOT accept a client-generated case reference'
|
|
);
|
|
console.log(' PASS: Case references strictly conform to OPS-YYYY-NNNNNN immutable pattern.');
|
|
|
|
// ============================================================
|
|
// 23. INTERNAL UUID ISOLATION (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 23: Internal Database UUID Isolation in Frontend & Sanitizers...');
|
|
const rawBackendIncident = {
|
|
id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
|
|
case_reference: 'OPS-2026-000042',
|
|
title: 'Database connection pool saturation',
|
|
summary: 'Connection exhaustion under high concurrent load',
|
|
severity: 'high',
|
|
status: 'investigating',
|
|
created_at: '2026-09-10T08:00:00Z',
|
|
updated_at: '2026-09-10T08:15:00Z',
|
|
created_by_operator_id: 'e4d9b1c2-3f4a-5b6c-7d8e-9f0a1b2c3d4e',
|
|
created_by_identity: 'operator@motorsignal.com',
|
|
closed_at: null,
|
|
closed_by_operator_id: null,
|
|
closed_by_identity: null,
|
|
resolution: null,
|
|
};
|
|
|
|
const sanitizedSummary = sanitizeIncidentSummary(rawBackendIncident);
|
|
assert.ok(sanitizedSummary !== null);
|
|
assert.equal(sanitizedSummary.case_reference, 'OPS-2026-000042');
|
|
assert.equal(sanitizedSummary.title, 'Database connection pool saturation');
|
|
assert.equal(sanitizedSummary.severity, 'high');
|
|
assert.equal(sanitizedSummary.status, 'investigating');
|
|
assert.equal(sanitizedSummary.id, undefined, 'Summary DTO must not contain database UUID');
|
|
|
|
const sanitizedDetail = sanitizeIncidentDetail(rawBackendIncident);
|
|
assert.ok(sanitizedDetail !== null);
|
|
assert.equal(sanitizedDetail.case_reference, 'OPS-2026-000042');
|
|
assert.equal(sanitizedDetail.created_by_identity, 'operator@motorsignal.com');
|
|
assert.equal(sanitizedDetail.id, undefined, 'Detail DTO must not contain database UUID');
|
|
assert.equal(sanitizedDetail.created_by_operator_id, undefined, 'Detail DTO must not contain operator UUID');
|
|
console.log(' PASS: Database UUIDs strictly stripped from all Incident DTOs.');
|
|
|
|
// ============================================================
|
|
// 24. INCIDENT SEVERITY ENUMS (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 24: Incident Severity Enums...');
|
|
assert.equal(isValidIncidentSeverity('low'), true);
|
|
assert.equal(isValidIncidentSeverity('medium'), true);
|
|
assert.equal(isValidIncidentSeverity('high'), true);
|
|
assert.equal(isValidIncidentSeverity('critical'), true);
|
|
assert.equal(isValidIncidentSeverity('p0'), false);
|
|
assert.equal(isValidIncidentSeverity('urgent'), false);
|
|
assert.equal(isValidIncidentSeverity('blocker'), false);
|
|
console.log(' PASS: Incident severity strictly locked to low, medium, high, critical.');
|
|
|
|
// ============================================================
|
|
// 25. INCIDENT STATUS ENUMS (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 25: Incident Status Enums...');
|
|
assert.equal(isValidIncidentStatus('open'), true);
|
|
assert.equal(isValidIncidentStatus('investigating'), true);
|
|
assert.equal(isValidIncidentStatus('monitoring'), true);
|
|
assert.equal(isValidIncidentStatus('resolved'), true);
|
|
assert.equal(isValidIncidentStatus('closed'), true);
|
|
assert.equal(isValidIncidentStatus('archived'), true);
|
|
assert.equal(isValidIncidentStatus('pending'), false);
|
|
assert.equal(isValidIncidentStatus('in_progress'), false);
|
|
assert.equal(isValidIncidentStatus('deleted'), false);
|
|
console.log(' PASS: Incident status strictly locked to 6 approved lifecycle states.');
|
|
|
|
// ============================================================
|
|
// 26. STATUS TRANSITION STATE MACHINE & ROLE MATRIX (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 26: Status Transition State Machine & Role Restrictions...');
|
|
|
|
// Operator transitions
|
|
assert.equal(isValidStatusTransition('open', 'investigating', 'operator'), true);
|
|
assert.equal(isValidStatusTransition('open', 'resolved', 'operator'), true);
|
|
assert.equal(isValidStatusTransition('open', 'closed', 'operator'), true);
|
|
assert.equal(isValidStatusTransition('investigating', 'monitoring', 'operator'), true);
|
|
assert.equal(isValidStatusTransition('investigating', 'resolved', 'operator'), true);
|
|
assert.equal(isValidStatusTransition('investigating', 'closed', 'operator'), true);
|
|
assert.equal(isValidStatusTransition('monitoring', 'investigating', 'operator'), true);
|
|
assert.equal(isValidStatusTransition('monitoring', 'resolved', 'operator'), true);
|
|
assert.equal(isValidStatusTransition('monitoring', 'closed', 'operator'), true);
|
|
assert.equal(isValidStatusTransition('resolved', 'investigating', 'operator'), true);
|
|
assert.equal(isValidStatusTransition('resolved', 'closed', 'operator'), true);
|
|
|
|
// Forbidden operator transitions
|
|
assert.equal(isValidStatusTransition('closed', 'investigating', 'operator'), false, 'Operator cannot reopen closed incident');
|
|
assert.equal(isValidStatusTransition('open', 'archived', 'operator'), false, 'Operator cannot archive incident');
|
|
assert.equal(isValidStatusTransition('investigating', 'archived', 'operator'), false, 'Operator cannot archive incident');
|
|
assert.equal(isValidStatusTransition('closed', 'archived', 'operator'), false, 'Operator cannot archive incident');
|
|
|
|
// Owner capabilities
|
|
assert.equal(isValidStatusTransition('closed', 'investigating', 'owner'), true, 'Owner can reopen closed incident');
|
|
assert.equal(isValidStatusTransition('open', 'archived', 'owner'), true, 'Owner can archive incident');
|
|
assert.equal(isValidStatusTransition('investigating', 'archived', 'owner'), true, 'Owner can archive incident');
|
|
assert.equal(isValidStatusTransition('closed', 'archived', 'owner'), true, 'Owner can archive incident');
|
|
|
|
// Terminal archived state
|
|
assert.equal(isValidStatusTransition('archived', 'open', 'owner'), false, 'Archived incident cannot transition to open');
|
|
assert.equal(isValidStatusTransition('archived', 'investigating', 'owner'), false, 'Archived incident cannot transition to investigating');
|
|
assert.equal(isValidStatusTransition('archived', 'closed', 'owner'), false, 'Archived incident cannot transition to closed');
|
|
console.log(' PASS: State machine guarantees strict role boundaries (Reopen/Archive = Owner Only).');
|
|
|
|
// ============================================================
|
|
// 27. APPEND-ONLY NOTES VALIDATION (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 27: Append-Only Notes Sanitization & Plain Text Contract...');
|
|
const rawNote = {
|
|
id: '7b8c9d0e-1f2a-3b4c-5d6e-7f8a9b0c1d2e',
|
|
incident_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
|
|
created_at: '2026-09-10T08:20:00Z',
|
|
created_by_operator_id: 'e4d9b1c2-3f4a-5b6c-7d8e-9f0a1b2c3d4e',
|
|
created_by_identity: 'operator@motorsignal.com',
|
|
body: 'Scaled up replica count from 2 to 6. Ingress latency normalized to 14ms.',
|
|
};
|
|
|
|
const sanitizedNote = sanitizeIncidentNote(rawNote);
|
|
assert.ok(sanitizedNote !== null);
|
|
assert.equal(sanitizedNote.created_by_identity, 'operator@motorsignal.com');
|
|
assert.equal(sanitizedNote.body, 'Scaled up replica count from 2 to 6. Ingress latency normalized to 14ms.');
|
|
assert.equal(sanitizedNote.id, undefined, 'Note DTO must not expose internal note UUID');
|
|
assert.equal(sanitizedNote.incident_id, undefined, 'Note DTO must not expose incident UUID');
|
|
assert.equal(sanitizedNote.created_by_operator_id, undefined, 'Note DTO must not expose operator UUID');
|
|
|
|
// Fail closed on invalid note
|
|
assert.equal(sanitizeIncidentNote(null), null);
|
|
assert.equal(sanitizeIncidentNote({ body: 'test' }), null, 'Missing created_by_identity must fail closed');
|
|
assert.equal(sanitizeIncidentNote({ created_by_identity: 'op', body: '' }), null, 'Empty body must fail closed');
|
|
console.log(' PASS: Notes DTOs strictly validated and internal UUIDs sanitized.');
|
|
|
|
// ============================================================
|
|
// 28. NO EDIT / NO DELETE FOR NOTES (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 28: No Note Edit or Deletion Endpoints in Codebase...');
|
|
const incidentNotesRoutePath = path.resolve(__dirname, '../app/api/ops/incidents/[caseRef]/notes/route.ts');
|
|
const incidentNotesRouteSource = fs.readFileSync(incidentNotesRoutePath, 'utf8');
|
|
|
|
assert.ok(
|
|
incidentNotesRouteSource.includes('export async function GET') &&
|
|
incidentNotesRouteSource.includes('export async function POST'),
|
|
'Notes route must implement GET and POST'
|
|
);
|
|
assert.ok(
|
|
!incidentNotesRouteSource.includes('export async function PUT') &&
|
|
!incidentNotesRouteSource.includes('export async function PATCH') &&
|
|
!incidentNotesRouteSource.includes('export async function DELETE'),
|
|
'Notes route must strictly FORBID PUT, PATCH, and DELETE (append-only contract)'
|
|
);
|
|
console.log(' PASS: Notes are strictly append-only with zero edit or delete endpoints.');
|
|
|
|
// ============================================================
|
|
// 29. NEXT.JS PROXY ROUTES AUTH BOUNDARY (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 29: Next.js API Incidents Proxy Routes Auth Enforcement...');
|
|
const incidentsRouteSource = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/incidents/route.ts'), 'utf8');
|
|
const incidentDetailRouteSource = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/incidents/[caseRef]/route.ts'), 'utf8');
|
|
|
|
assert.ok(
|
|
incidentsRouteSource.includes('__Host-motorsignal_ops') &&
|
|
incidentDetailRouteSource.includes('__Host-motorsignal_ops') &&
|
|
incidentNotesRouteSource.includes('__Host-motorsignal_ops'),
|
|
'All incidents API proxy routes must enforce __Host-motorsignal_ops session token'
|
|
);
|
|
assert.ok(
|
|
!incidentsRouteSource.includes('preview=') &&
|
|
!incidentDetailRouteSource.includes('preview='),
|
|
'Proxy routes must have zero preview query authentication bypass'
|
|
);
|
|
console.log(' PASS: All incident proxy routes require verified Operations host session.');
|
|
|
|
// ============================================================
|
|
// 30. ACCESSIBLE CONFIRMATION MODAL CONTRACT (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 30: Incident Modal Accessibility & Resolution Validation...');
|
|
const modalSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-incident-modal.tsx'), 'utf8');
|
|
assert.ok(modalSource.includes('aria-modal="true"'), 'Modal must declare aria-modal="true"');
|
|
assert.ok(modalSource.includes('role="dialog"'), 'Modal must declare role="dialog"');
|
|
assert.ok(modalSource.includes("e.key === 'Escape'"), 'Modal must handle Escape key');
|
|
assert.ok(
|
|
modalSource.includes('caseReference'),
|
|
'Modal must render case reference string'
|
|
);
|
|
assert.ok(
|
|
modalSource.includes('resolution.trim()'),
|
|
'Modal must validate that resolution text is non-empty before close'
|
|
);
|
|
console.log(' PASS: Modal provides accessible dialog semantics, Escape key handling, and resolution validation.');
|
|
|
|
// ============================================================
|
|
// 31. DENSE INCIDENT LIST UI CONTRACT (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 31: Dense Incident List Table & Mobile Stacked Cards...');
|
|
const listSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-incidents-list.tsx'), 'utf8');
|
|
assert.ok(listSource.includes('hidden md:block'), 'List must render table on desktop');
|
|
assert.ok(listSource.includes('md:hidden'), 'List must render stacked cards on mobile');
|
|
assert.ok(listSource.includes('CASE'), 'Table must contain CASE column');
|
|
assert.ok(listSource.includes('TITLE'), 'Table must contain TITLE column');
|
|
assert.ok(listSource.includes('SEVERITY'), 'Table must contain SEVERITY column');
|
|
assert.ok(listSource.includes('STATUS'), 'Table must contain STATUS column');
|
|
assert.ok(listSource.includes('UPDATED'), 'Table must contain UPDATED column');
|
|
assert.ok(listSource.includes('CREATED'), 'Table must contain CREATED column');
|
|
console.log(' PASS: List provides dense desktop table and mobile stacked card layout.');
|
|
|
|
// ============================================================
|
|
// 32. INCIDENT DETAIL UI SECTIONS (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 32: Incident Detail UI Structure & Plain-Text Notes...');
|
|
const detailSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-incident-detail.tsx'), 'utf8');
|
|
assert.ok(detailSource.includes('Operational Timeline'), 'Detail must include Operational Timeline');
|
|
assert.ok(detailSource.includes('Operator Notes'), 'Detail must include Operator Notes');
|
|
assert.ok(detailSource.includes('Incident Summary'), 'Detail must include Incident Summary');
|
|
assert.ok(detailSource.includes('Resolution Record') || detailSource.includes('resolution'), 'Detail must support resolution section');
|
|
assert.ok(detailSource.includes('whitespace-pre-wrap'), 'Notes and summaries must preserve plain text formatting');
|
|
console.log(' PASS: Incident Detail UI includes all required operational sections.');
|
|
|
|
// ============================================================
|
|
// 33. ZERO POLLING VERIFICATION (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 33: Zero Polling Verification Across Operations UI...');
|
|
for (const file of currentOpsFiles) {
|
|
if (file === 'operations-infrastructure.tsx') continue; // Infrastructure explicitly implements 30s visibility-gated polling (tested in Section 43)
|
|
const content = fs.readFileSync(path.join(opsDir, file), 'utf8');
|
|
assert.ok(!content.includes('setInterval'), `${file} must NOT contain setInterval polling`);
|
|
assert.ok(!content.includes('setTimeout(') || content.includes('setTimeout(() => setMutationSuccess') || content.includes('setTimeout(() =>'), `${file} must only use setTimeout for UI transitions, not polling`);
|
|
}
|
|
console.log(' PASS: Zero automatic background polling across all Operations components.');
|
|
|
|
|
|
// ============================================================
|
|
// 34. ZERO RIDER DATA CROSSOVER (OPS-4)
|
|
// ============================================================
|
|
console.log('Testing 34: Zero Rider Data Crossover in Operations Incidents...');
|
|
for (const file of currentOpsFiles) {
|
|
const content = fs.readFileSync(path.join(opsDir, file), 'utf8');
|
|
assert.ok(!content.includes('telemetry_points'), `${file} must not reference telemetry`);
|
|
assert.ok(!content.includes('user_id'), `${file} must not expose consumer user_id`);
|
|
assert.ok(!content.includes('vehicle_id'), `${file} must not reference vehicle_id`);
|
|
assert.ok(!/\bvin\b/i.test(content), `${file} must not reference VIN`);
|
|
assert.ok(!content.includes('/api/v1/rides') && !content.includes('/api/rides'), `${file} must not query consumer rides`);
|
|
}
|
|
console.log(' PASS: Operations Incident system strictly isolated from rider and vehicle data.');
|
|
|
|
// ============================================================
|
|
// ============================================================
|
|
// 36. CSRF & SAME-ORIGIN VALIDATION ON MUTATIONS (OPS-4.2)
|
|
// ============================================================
|
|
console.log('Testing 36: CSRF & Same-Origin Enforcement on State-Changing Incident Proxy Routes (OPS-4.2)...');
|
|
const incidentsRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/incidents/route.ts'), 'utf8');
|
|
const incidentDetailRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/incidents/[caseRef]/route.ts'), 'utf8');
|
|
const incidentNotesRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/incidents/[caseRef]/notes/route.ts'), 'utf8');
|
|
|
|
assert.ok(
|
|
incidentsRouteSrc.includes('hasValidOrigin(request)') && incidentsRouteSrc.includes("detail: 'Invalid request origin.'"),
|
|
'POST /api/ops/incidents must enforce hasValidOrigin and reject with 403 on invalid origin'
|
|
);
|
|
assert.ok(
|
|
incidentDetailRouteSrc.includes('hasValidOrigin(request)') && incidentDetailRouteSrc.includes("detail: 'Invalid request origin.'"),
|
|
'PATCH /api/ops/incidents/[caseRef] must enforce hasValidOrigin and reject with 403 on invalid origin'
|
|
);
|
|
assert.ok(
|
|
incidentNotesRouteSrc.includes('hasValidOrigin(request)') && incidentNotesRouteSrc.includes("detail: 'Invalid request origin.'"),
|
|
'POST /api/ops/incidents/[caseRef]/notes must enforce hasValidOrigin and reject with 403 on invalid origin'
|
|
);
|
|
console.log(' PASS: All 3 state-changing incident proxy routes enforce same-origin CSRF protection.');
|
|
|
|
// ============================================================
|
|
// 37. SAME-ORIGIN HELPER BEHAVIOR FOR OPS MUTATIONS (OPS-4.2)
|
|
// ============================================================
|
|
console.log('Testing 37: hasValidOrigin Security Matrix (OPS-4.2)...');
|
|
const requestOriginModule = loadTsModule('../lib/request-origin.ts');
|
|
const { hasValidOrigin } = requestOriginModule;
|
|
|
|
// Mock NextRequest objects
|
|
function createMockRequest(headers) {
|
|
return {
|
|
headers: {
|
|
get: (k) => headers[k.toLowerCase()] || null,
|
|
},
|
|
nextUrl: {
|
|
protocol: 'https:',
|
|
},
|
|
};
|
|
}
|
|
|
|
// Same origin
|
|
assert.equal(
|
|
hasValidOrigin(createMockRequest({
|
|
origin: 'https://motorsignal.app',
|
|
host: 'motorsignal.app',
|
|
'x-forwarded-proto': 'https',
|
|
})),
|
|
true,
|
|
'Valid same-origin request must pass'
|
|
);
|
|
|
|
// Cross origin attacker
|
|
assert.equal(
|
|
hasValidOrigin(createMockRequest({
|
|
origin: 'https://evil-attacker.com',
|
|
host: 'motorsignal.app',
|
|
'x-forwarded-proto': 'https',
|
|
})),
|
|
false,
|
|
'Cross-origin attacker origin must be rejected'
|
|
);
|
|
|
|
// Missing origin header
|
|
assert.equal(
|
|
hasValidOrigin(createMockRequest({
|
|
host: 'motorsignal.app',
|
|
'x-forwarded-proto': 'https',
|
|
})),
|
|
false,
|
|
'Missing origin header must be rejected on mutations'
|
|
);
|
|
|
|
// Protocol mismatch (http origin to https host)
|
|
assert.equal(
|
|
hasValidOrigin(createMockRequest({
|
|
origin: 'http://motorsignal.app',
|
|
host: 'motorsignal.app',
|
|
'x-forwarded-proto': 'https',
|
|
})),
|
|
false,
|
|
'Protocol downgrade/mismatch must be rejected'
|
|
);
|
|
|
|
console.log(' PASS: hasValidOrigin rejects cross-origin, missing-origin, and protocol-mismatched requests.');
|
|
|
|
// ============================================================
|
|
// 38. GET PROXY ROUTES UNAFFECTED (OPS-4.2)
|
|
// ============================================================
|
|
console.log('Testing 38: GET Proxy Routes Unaffected by Origin Barrier (OPS-4.2)...');
|
|
// Ensure GET handlers do not block on origin
|
|
const getIncidentMatch = incidentsRouteSrc.match(/export async function GET[\s\S]*?export async function POST/);
|
|
assert.ok(getIncidentMatch && !getIncidentMatch[0].includes('hasValidOrigin'), 'GET incidents must not require origin');
|
|
|
|
const getDetailMatch = incidentDetailRouteSrc.match(/export async function GET[\s\S]*?export async function PATCH/);
|
|
assert.ok(getDetailMatch && !getDetailMatch[0].includes('hasValidOrigin'), 'GET incident detail must not require origin');
|
|
|
|
const getNotesMatch = incidentNotesRouteSrc.match(/export async function GET[\s\S]*?export async function POST/);
|
|
assert.ok(getNotesMatch && !getNotesMatch[0].includes('hasValidOrigin'), 'GET notes must not require origin');
|
|
|
|
console.log(' PASS: Read-only GET operations are unaffected while mutations require valid origin.');
|
|
|
|
// ============================================================
|
|
// 39. OPERATIONS OVERVIEW DATA NORMALIZATION & PROXY ROUTE (SECTION 1)
|
|
// ============================================================
|
|
console.log('Testing 39: Operations Overview Normalization & Command Dashboard Contract...');
|
|
const overviewRoutePath = path.resolve(__dirname, '../app/api/ops/overview/route.ts');
|
|
assert.ok(fs.existsSync(overviewRoutePath), 'GET /api/ops/overview proxy route must exist');
|
|
const overviewRouteSrc = fs.readFileSync(overviewRoutePath, 'utf8');
|
|
assert.ok(overviewRouteSrc.includes('__Host-motorsignal_ops'), 'Overview route must check __Host-motorsignal_ops');
|
|
assert.ok(overviewRouteSrc.includes('/api/v1/ops/overview'), 'Overview route must query /api/v1/ops/overview');
|
|
assert.ok(overviewRouteSrc.includes('normalizeOverviewResponse'), 'Overview route must normalize response');
|
|
|
|
// Test normalizeOverviewResponse with null
|
|
const emptyOverview = normalizeOverviewResponse(null);
|
|
assert.equal(typeof emptyOverview.generated_at, 'string');
|
|
assert.equal(emptyOverview.users.total_registered, 0);
|
|
assert.equal(emptyOverview.membership.conversion_rate_pct, 0);
|
|
assert.equal(emptyOverview.incidents.total_open, 0);
|
|
assert.equal(emptyOverview.health.status, 'unavailable');
|
|
|
|
// Test normalizeOverviewResponse with sample backend payload
|
|
const sampleBackendOverview = {
|
|
generated_at: '2026-09-15T12:00:00Z',
|
|
users: {
|
|
total_registered: 1250,
|
|
active_24h: 320,
|
|
active_7d: 840,
|
|
new_this_week: 45,
|
|
},
|
|
membership: {
|
|
total_subscribers: 210,
|
|
premium_members: 180,
|
|
pro_members: 180,
|
|
free_members: 1070,
|
|
conversion_rate_pct: 14.4,
|
|
},
|
|
activity: {
|
|
web_active_sessions_24h: 150,
|
|
web_logins_24h: 95,
|
|
web_signups_7d: 45,
|
|
mobile_active_sessions_24h: 210,
|
|
mobile_rides_today: 88,
|
|
mobile_rides_active: 7,
|
|
mobile_datapoints_24h: 1420500,
|
|
},
|
|
incidents: {
|
|
total_open: 1,
|
|
critical_count: 0,
|
|
high_count: 1,
|
|
medium_count: 0,
|
|
low_count: 0,
|
|
recent_open: [
|
|
{
|
|
id: 'db-uuid-incident-should-be-stripped',
|
|
case_reference: 'OPS-2026-000088',
|
|
title: 'API rate limit threshold alert',
|
|
severity: 'high',
|
|
status: 'investigating',
|
|
created_at: '2026-09-15T10:00:00Z',
|
|
updated_at: '2026-09-15T10:15:00Z',
|
|
},
|
|
],
|
|
},
|
|
errors: {
|
|
errors_24h: 3,
|
|
denials_24h: 2,
|
|
recent: [
|
|
{
|
|
id: 'sec-evt-1',
|
|
timestamp: '2026-09-15T11:45:00Z',
|
|
action: 'operator.login',
|
|
actor_identity: 'attacker@unknown.io',
|
|
reason_code: 'invalid_credential',
|
|
result: 'denied',
|
|
target_resource: 'ops.session',
|
|
},
|
|
],
|
|
},
|
|
health: {
|
|
status: 'available',
|
|
checked_at: '2026-09-15T12:00:00Z',
|
|
components: {
|
|
operations_api: { status: 'available' },
|
|
operations_database: { status: 'available' },
|
|
session_validation: { status: 'available' },
|
|
audit_persistence: { status: 'available' },
|
|
},
|
|
},
|
|
};
|
|
|
|
const normalizedOverview = normalizeOverviewResponse(sampleBackendOverview);
|
|
assert.equal(normalizedOverview.users.total_registered, 1250);
|
|
assert.equal(normalizedOverview.users.active_24h, 320);
|
|
assert.equal(normalizedOverview.membership.premium_members, 180);
|
|
assert.equal(normalizedOverview.activity.mobile_rides_today, 88);
|
|
assert.equal(normalizedOverview.activity.mobile_datapoints_24h, 1420500);
|
|
assert.equal(normalizedOverview.incidents.total_open, 1);
|
|
assert.equal(normalizedOverview.incidents.recent_open.length, 1);
|
|
assert.equal(normalizedOverview.incidents.recent_open[0].case_reference, 'OPS-2026-000088');
|
|
assert.equal(normalizedOverview.incidents.recent_open[0].id, undefined, 'Database UUID must be stripped from incidents');
|
|
assert.equal(normalizedOverview.errors.denials_24h, 2);
|
|
assert.equal(normalizedOverview.errors.recent.length, 1);
|
|
assert.equal(normalizedOverview.health.status, 'available');
|
|
|
|
// Verify dashboard component structure
|
|
assert.ok(overviewSource.includes('Registered Users'), 'Overview must render Registered Users KPI');
|
|
assert.ok(overviewSource.includes('Active Users'), 'Overview must render Active Users KPI');
|
|
assert.ok(overviewSource.includes('Premium Members'), 'Overview must render Premium Members KPI');
|
|
assert.ok(overviewSource.includes('Open Incidents'), 'Overview must render Open Incidents KPI');
|
|
assert.ok(overviewSource.includes('Website Activity') || overviewSource.includes('Website Platform Activity'), 'Overview must render Website Activity');
|
|
assert.ok(overviewSource.includes('Mobile Telemetry Activity'), 'Overview must render Mobile Telemetry Activity');
|
|
assert.ok(overviewSource.includes('Security & Access Events') || overviewSource.includes('Security Error & Denial Stream') || overviewSource.includes('Security Error'), 'Overview must render Security & Access Events');
|
|
assert.ok(overviewSource.includes('Active Incidents') || overviewSource.includes('Open Incidents Triage'), 'Overview must render Active Incidents');
|
|
assert.ok(overviewSource.includes('Operational Architecture & Data Boundary') || overviewSource.includes('Operational Architecture'), 'Overview must render Architecture Boundary');
|
|
|
|
// Shell integration check
|
|
assert.ok(shellSource.includes('/api/ops/overview'), 'OperationsShell must fetch /api/ops/overview');
|
|
assert.ok(shellSource.includes('overviewData') || shellSource.includes('overview='), 'OperationsShell must pass overview data to OperationsOverview');
|
|
|
|
console.log(' PASS: Operations overview DTO normalization, proxy route, and dashboard elements verified.');
|
|
|
|
// ============================================================
|
|
// 40. SECTION 2: USER MANAGEMENT ARCHITECTURE & SECURITY CONTRACT
|
|
// ============================================================
|
|
console.log('Testing 40: Section 2 User Management (Backend, Proxy Routes & UI)...');
|
|
|
|
// 40.1 Proxy routes verification
|
|
const usersRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/users/route.ts'), 'utf8');
|
|
const userDetailRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/users/[userId]/route.ts'), 'utf8');
|
|
const userStatusRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/users/[userId]/status/route.ts'), 'utf8');
|
|
const userRevokeRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/users/[userId]/revoke-sessions/route.ts'), 'utf8');
|
|
const userResetRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/users/[userId]/reset-password/route.ts'), 'utf8');
|
|
|
|
assert.ok(usersRouteSrc.includes('__Host-motorsignal_ops'), 'users/route.ts must check __Host-motorsignal_ops');
|
|
assert.ok(userDetailRouteSrc.includes('__Host-motorsignal_ops'), 'users/[userId]/route.ts must check __Host-motorsignal_ops');
|
|
assert.ok(userStatusRouteSrc.includes('__Host-motorsignal_ops'), 'users/[userId]/status/route.ts must check __Host-motorsignal_ops');
|
|
assert.ok(userRevokeRouteSrc.includes('__Host-motorsignal_ops'), 'users/[userId]/revoke-sessions/route.ts must check __Host-motorsignal_ops');
|
|
assert.ok(userResetRouteSrc.includes('__Host-motorsignal_ops'), 'users/[userId]/reset-password/route.ts must check __Host-motorsignal_ops');
|
|
|
|
// CSRF Origin enforcement on mutations
|
|
assert.ok(userStatusRouteSrc.includes('hasValidOrigin(request)'), 'User status route must enforce CSRF origin check');
|
|
assert.ok(userRevokeRouteSrc.includes('hasValidOrigin(request)'), 'User revoke sessions route must enforce CSRF origin check');
|
|
assert.ok(userResetRouteSrc.includes('hasValidOrigin(request)'), 'User reset password route must enforce CSRF origin check');
|
|
|
|
// 40.2 Navigation order & shell wiring
|
|
assert.ok(sidebarSource.includes("id: 'users'") && sidebarSource.includes("label: 'Users'") && sidebarSource.includes("icon: 'person_search'"), 'Sidebar must contain users nav item');
|
|
const usersNavIdx = sidebarSource.indexOf("id: 'users'");
|
|
assert.ok(overviewIdx < usersNavIdx && usersNavIdx < incidentsIdx, 'Users nav item must be placed right after overview and before incidents');
|
|
assert.ok(shellSource.includes("activeNav === 'users'") && shellSource.includes('<OperationsUsers'), 'OperationsShell must wire activeNav === users to OperationsUsers');
|
|
|
|
// 40.3 OperationsUsers component UI contract
|
|
const usersComponentSrc = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-users.tsx'), 'utf8');
|
|
assert.ok(usersComponentSrc.includes('hidden md:block'), 'Users list must render desktop table view');
|
|
assert.ok(usersComponentSrc.includes('md:hidden'), 'Users list must render mobile stacked cards');
|
|
assert.ok(usersComponentSrc.includes('Search user by email or account ID'), 'Users list must provide search bar');
|
|
assert.ok(usersComponentSrc.includes('statusFilter') && usersComponentSrc.includes('verifiedFilter'), 'Users list must provide status and verification filters');
|
|
assert.ok(usersComponentSrc.includes('suspend') && usersComponentSrc.includes('reinstate'), 'Users detail must support suspend and reinstate actions');
|
|
assert.ok(usersComponentSrc.includes('revoke-sessions'), 'Users detail must support session revocation');
|
|
assert.ok(usersComponentSrc.includes('reset-password'), 'Users detail must support password reset');
|
|
assert.ok(usersComponentSrc.includes('length < 3') || usersComponentSrc.includes('min 3 characters'), 'Action confirmation must require reason with minimum 3 chars');
|
|
assert.ok(!usersComponentSrc.includes('setInterval'), 'Users component must have zero setInterval');
|
|
assert.ok(!usersComponentSrc.includes('setTimeout'), 'Users component must have zero setTimeout');
|
|
|
|
// 40.4 Backend main.py contract check
|
|
const backendMainSrc = fs.readFileSync(path.resolve(__dirname, '../../backend/motorsignal_api/app/main.py'), 'utf8');
|
|
assert.ok(backendMainSrc.includes('class OpsUserStatusUpdate'), 'Backend must define OpsUserStatusUpdate');
|
|
assert.ok(backendMainSrc.includes('class OpsUserActionRequest'), 'Backend must define OpsUserActionRequest');
|
|
assert.ok(backendMainSrc.includes('@app.get("/api/v1/ops/users")'), 'Backend must implement GET /api/v1/ops/users');
|
|
assert.ok(backendMainSrc.includes('@app.get("/api/v1/ops/users/{user_id}")'), 'Backend must implement GET /api/v1/ops/users/{user_id}');
|
|
assert.ok(backendMainSrc.includes('@app.post("/api/v1/ops/users/{user_id}/status")'), 'Backend must implement POST /api/v1/ops/users/{user_id}/status');
|
|
assert.ok(backendMainSrc.includes('@app.post("/api/v1/ops/users/{user_id}/revoke-sessions")'), 'Backend must implement POST /api/v1/ops/users/{user_id}/revoke-sessions');
|
|
assert.ok(backendMainSrc.includes('@app.post("/api/v1/ops/users/{user_id}/reset-password")'), 'Backend must implement POST /api/v1/ops/users/{user_id}/reset-password');
|
|
assert.ok(backendMainSrc.includes('user.suspend') && backendMainSrc.includes('user.reinstate'), 'Backend must audit user.suspend and user.reinstate');
|
|
assert.ok(backendMainSrc.includes('user.sessions_revoked'), 'Backend must audit user.sessions_revoked');
|
|
assert.ok(backendMainSrc.includes('user.password_reset_initiated'), 'Backend must audit user.password_reset_initiated');
|
|
|
|
console.log(' PASS: Section 2 User Management backend, proxy routes, navigation, and UI contracts fully verified.');
|
|
|
|
// ============================================================
|
|
// 41. SECTION 3: SUPPORT CENTER & DIAGNOSTICS CONTRACT
|
|
// ============================================================
|
|
console.log('Testing 41: Section 3 Support Center & Diagnostics (Backend, Proxy Routes & UI)...');
|
|
|
|
// 41.1 Proxy routes verification
|
|
const supportTicketsRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/support/tickets/route.ts'), 'utf8');
|
|
const supportDetailRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/support/tickets/[ticketId]/route.ts'), 'utf8');
|
|
const supportNotesRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/support/tickets/[ticketId]/notes/route.ts'), 'utf8');
|
|
const supportDiagnosticsRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/support/tickets/[ticketId]/diagnostics/route.ts'), 'utf8');
|
|
|
|
assert.ok(supportTicketsRouteSrc.includes('__Host-motorsignal_ops'), 'support/tickets/route.ts must check __Host-motorsignal_ops');
|
|
assert.ok(supportDetailRouteSrc.includes('__Host-motorsignal_ops'), 'support/tickets/[ticketId]/route.ts must check __Host-motorsignal_ops');
|
|
assert.ok(supportNotesRouteSrc.includes('__Host-motorsignal_ops'), 'support/tickets/[ticketId]/notes/route.ts must check __Host-motorsignal_ops');
|
|
assert.ok(supportDiagnosticsRouteSrc.includes('__Host-motorsignal_ops'), 'support/tickets/[ticketId]/diagnostics/route.ts must check __Host-motorsignal_ops');
|
|
|
|
// CSRF Origin enforcement on mutations
|
|
assert.ok(supportTicketsRouteSrc.includes('hasValidOrigin(request)'), 'Support tickets create route must enforce CSRF origin check');
|
|
assert.ok(supportDetailRouteSrc.includes('hasValidOrigin(request)'), 'Support ticket patch route must enforce CSRF origin check');
|
|
assert.ok(supportNotesRouteSrc.includes('hasValidOrigin(request)'), 'Support notes route must enforce CSRF origin check');
|
|
|
|
// Diagnostics reason requirement
|
|
assert.ok(supportDiagnosticsRouteSrc.includes('reason.length < 3'), 'Support diagnostics route must require reason with min 3 chars');
|
|
|
|
// 41.2 Navigation order & shell wiring
|
|
assert.ok(sidebarSource.includes("id: 'support'") && sidebarSource.includes("label: 'Support Center'") && sidebarSource.includes("icon: 'contact_support'"), 'Sidebar must contain support nav item');
|
|
const supportNavIdx = sidebarSource.indexOf("id: 'support'");
|
|
assert.ok(incidentsIdx < supportNavIdx && supportNavIdx < auditIdx, 'Support nav item must be placed right after incidents and before audit');
|
|
assert.ok(shellSource.includes("activeNav === 'support'") && shellSource.includes('<OperationsSupport'), 'OperationsShell must wire activeNav === support to OperationsSupport');
|
|
|
|
// 41.3 OperationsSupport component UI contract
|
|
const supportComponentSrc = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-support.tsx'), 'utf8');
|
|
assert.ok(supportComponentSrc.includes('hidden md:block'), 'Support list must render desktop table view');
|
|
assert.ok(supportComponentSrc.includes('md:hidden'), 'Support list must render mobile stacked cards');
|
|
assert.ok(supportComponentSrc.includes('Search tickets by case ref'), 'Support list must provide search bar');
|
|
assert.ok(supportComponentSrc.includes('bug_reports') && supportComponentSrc.includes('contact_requests'), 'Support list must provide type filter pills');
|
|
assert.ok(supportComponentSrc.includes('in_progress') && supportComponentSrc.includes('resolved'), 'Support list must provide status filter pills');
|
|
assert.ok(supportComponentSrc.includes('Mark In Progress'), 'Support detail must support Mark In Progress transition action');
|
|
assert.ok(supportComponentSrc.includes('Resolve'), 'Support detail must support Resolve transition action');
|
|
assert.ok(supportComponentSrc.includes('Close'), 'Support detail must support Close transition action');
|
|
assert.ok(supportComponentSrc.includes('Add Note') || supportComponentSrc.includes('Add Internal Note'), 'Support detail must support adding internal notes');
|
|
assert.ok(supportComponentSrc.includes('Inspect Submitted Diagnostics') || supportComponentSrc.includes('Inspect Diagnostics'), 'Support detail must provide Inspect Submitted Diagnostics button');
|
|
assert.ok(supportComponentSrc.includes('length < 3') || supportComponentSrc.includes('min 3 characters'), 'Diagnostics confirmation must require reason with minimum 3 chars');
|
|
assert.ok(!supportComponentSrc.includes('setInterval'), 'Support component must have zero setInterval');
|
|
assert.ok(!supportComponentSrc.includes('setTimeout'), 'Support component must have zero setTimeout');
|
|
|
|
// 41.4 Backend main.py contract check
|
|
assert.ok(backendMainSrc.includes('class OpsSupportTicketCreate'), 'Backend must define OpsSupportTicketCreate');
|
|
assert.ok(backendMainSrc.includes('class OpsSupportTicketPatch'), 'Backend must define OpsSupportTicketPatch');
|
|
assert.ok(backendMainSrc.includes('class OpsSupportTicketNoteCreate'), 'Backend must define OpsSupportTicketNoteCreate');
|
|
assert.ok(backendMainSrc.includes('class OpsSupportDiagnosticBundle'), 'Backend must define OpsSupportDiagnosticBundle');
|
|
assert.ok(backendMainSrc.includes('@app.get("/api/v1/ops/support/tickets")'), 'Backend must implement GET /api/v1/ops/support/tickets');
|
|
assert.ok(backendMainSrc.includes('@app.get("/api/v1/ops/support/tickets/{ticket_id}")'), 'Backend must implement GET /api/v1/ops/support/tickets/{ticket_id}');
|
|
assert.ok(backendMainSrc.includes('@app.post("/api/v1/ops/support/tickets'), 'Backend must implement POST /api/v1/ops/support/tickets');
|
|
assert.ok(backendMainSrc.includes('@app.post("/api/v1/ops/support/tickets/{ticket_id}/notes'), 'Backend must implement POST /api/v1/ops/support/tickets/{ticket_id}/notes');
|
|
assert.ok(backendMainSrc.includes('@app.patch("/api/v1/ops/support/tickets/{ticket_id}")'), 'Backend must implement PATCH /api/v1/ops/support/tickets/{ticket_id}');
|
|
assert.ok(backendMainSrc.includes('@app.get("/api/v1/ops/support/tickets/{ticket_id}/diagnostics")'), 'Backend must implement GET /api/v1/ops/support/tickets/{ticket_id}/diagnostics');
|
|
assert.ok(backendMainSrc.includes('support.note_added'), 'Backend must audit support.note_added');
|
|
assert.ok(backendMainSrc.includes('support.ticket_updated'), 'Backend must audit support.ticket_updated');
|
|
assert.ok(backendMainSrc.includes('support.diagnostics_viewed'), 'Backend must audit support.diagnostics_viewed');
|
|
assert.ok(backendMainSrc.includes('SUP-'), 'Backend must generate SUP-YYYY-NNNNNN case references');
|
|
|
|
|
|
// 41.5 Support Report Modal static contract verification
|
|
// Note: Runtime interaction, form validation, duplicate submission protection,
|
|
// error handling, and success flows are behaviorally exercised in tests/support-report-modal.test.tsx.
|
|
const supportModalSrc = fs.readFileSync(path.resolve(__dirname, '../components/ops/support-report-modal.tsx'), 'utf8');
|
|
assert.ok(supportComponentSrc.includes('New Support Report'), 'Support list must provide New Support Report button');
|
|
assert.ok(supportModalSrc.includes('reporter_email'), 'Support modal must use exact backend field name reporter_email');
|
|
assert.ok(supportModalSrc.includes('ticket_type'), 'Support modal must use exact backend field name ticket_type');
|
|
assert.ok(supportModalSrc.includes('subject'), 'Support modal must use exact backend field name subject');
|
|
assert.ok(supportModalSrc.includes('description'), 'Support modal must use exact backend field name description');
|
|
assert.ok(supportModalSrc.includes('severity'), 'Support modal must use exact backend field name severity');
|
|
|
|
// Verify all four canonical ticket_type values are present and non-canonical values are absent
|
|
assert.ok(supportModalSrc.includes('value="general">General</option>'), 'Support modal must offer general option');
|
|
assert.ok(supportModalSrc.includes('value="bug_report">Bug Report</option>'), 'Support modal must offer bug_report option');
|
|
assert.ok(supportModalSrc.includes('value="telemetry_issue">Telemetry Issue</option>'), 'Support modal must offer telemetry_issue option');
|
|
assert.ok(supportModalSrc.includes('value="contact_request">Contact Request</option>'), 'Support modal must offer contact_request option');
|
|
assert.ok(!supportModalSrc.includes('billing'), 'Support modal must not offer billing option');
|
|
assert.ok(!supportModalSrc.includes('data_deletion'), 'Support modal must not offer data_deletion option');
|
|
|
|
// Accessible dialog semantics
|
|
assert.ok(supportModalSrc.includes('role="dialog"') && supportModalSrc.includes('aria-modal="true"'), 'Support modal must have dialog semantics aria tags');
|
|
|
|
// List refresh is verified by the inclusion of onSuccess callback in OperationsSupport
|
|
assert.ok(supportComponentSrc.includes('onSuccess={('), 'OperationsSupport must provide list refresh callback');
|
|
assert.ok(supportComponentSrc.includes('handleSelectTicket(ticket.id)'), 'OperationsSupport must provide selection/opening of the created ticket');
|
|
console.log(' PASS: Section 3 Support Center backend, proxy routes, navigation, and UI contracts fully verified.');
|
|
|
|
// ============================================================
|
|
// 42. SECTION 4: WEBSITE CONTENT CMS CONTRACT (Backend, Proxy Routes & UI)
|
|
// ============================================================
|
|
console.log('Testing 42: Section 4 Website Content CMS (Backend, Proxy Routes & UI)...');
|
|
|
|
// 42.1 Proxy routes verification
|
|
const cmsDocumentsRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/cms/documents/route.ts'), 'utf8');
|
|
const cmsDetailRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/cms/documents/[slug]/route.ts'), 'utf8');
|
|
const cmsRollbackRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/cms/documents/[slug]/rollback/route.ts'), 'utf8');
|
|
|
|
assert.ok(cmsDocumentsRouteSrc.includes('__Host-motorsignal_ops'), 'cms/documents/route.ts must check __Host-motorsignal_ops');
|
|
assert.ok(cmsDetailRouteSrc.includes('__Host-motorsignal_ops'), 'cms/documents/[slug]/route.ts must check __Host-motorsignal_ops');
|
|
assert.ok(cmsRollbackRouteSrc.includes('__Host-motorsignal_ops'), 'cms/documents/[slug]/rollback/route.ts must check __Host-motorsignal_ops');
|
|
|
|
// CSRF Origin enforcement on mutations
|
|
assert.ok(cmsDetailRouteSrc.includes('hasValidOrigin(request)'), 'CMS detail PUT route must enforce CSRF origin check');
|
|
assert.ok(cmsRollbackRouteSrc.includes('hasValidOrigin(request)'), 'CMS rollback route must enforce CSRF origin check');
|
|
|
|
// 42.2 Types & Navigation order & shell wiring
|
|
const typesSrc = fs.readFileSync(path.resolve(__dirname, '../components/ops/types.ts'), 'utf8');
|
|
assert.ok(typesSrc.includes("'cms'"), "types.ts must include 'cms' in OperationsNavId");
|
|
assert.ok(typesSrc.includes('CmsDocumentSummary'), 'types.ts must define CmsDocumentSummary');
|
|
assert.ok(typesSrc.includes('CmsDocumentDetail'), 'types.ts must define CmsDocumentDetail');
|
|
assert.ok(typesSrc.includes('CmsRevisionItem'), 'types.ts must define CmsRevisionItem');
|
|
|
|
assert.ok(sidebarSource.includes("id: 'cms'") && sidebarSource.includes("label: 'Website Content'") && sidebarSource.includes("icon: 'edit_note'"), 'Sidebar must contain cms nav item');
|
|
const cmsNavIdx = sidebarSource.indexOf("id: 'cms'");
|
|
assert.ok(supportNavIdx < cmsNavIdx && cmsNavIdx < auditIdx, 'CMS nav item must be placed right after support and before audit');
|
|
assert.ok(shellSource.includes("activeNav === 'cms'") && shellSource.includes('<OperationsCms'), 'OperationsShell must wire activeNav === cms to OperationsCms');
|
|
|
|
// 42.3 OperationsCms component UI contract
|
|
const cmsComponentSrc = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-cms.tsx'), 'utf8');
|
|
assert.ok(cmsComponentSrc.includes('homepage_hero'), 'CMS component must support homepage_hero section');
|
|
assert.ok(cmsComponentSrc.includes('features'), 'CMS component must support features section');
|
|
assert.ok(cmsComponentSrc.includes('screenshots'), 'CMS component must support screenshots section');
|
|
assert.ok(cmsComponentSrc.includes('faqs'), 'CMS component must support faqs section');
|
|
assert.ok(cmsComponentSrc.includes('download_links'), 'CMS component must support download_links section');
|
|
assert.ok(cmsComponentSrc.includes('changelog'), 'CMS component must support changelog section');
|
|
assert.ok(cmsComponentSrc.includes('policy_pages'), 'CMS component must support policy_pages section');
|
|
|
|
assert.ok(cmsComponentSrc.includes('Published') && cmsComponentSrc.includes('Draft Pending'), 'CMS component must display Published vs Draft Pending status');
|
|
assert.ok(cmsComponentSrc.includes('Preview Changes'), 'CMS component must provide Preview Changes action');
|
|
assert.ok(cmsComponentSrc.includes('Save Draft'), 'CMS component must provide Save Draft action');
|
|
assert.ok(cmsComponentSrc.includes('Publish Live'), 'CMS component must provide Publish Live action');
|
|
assert.ok(cmsComponentSrc.includes('Revision History'), 'CMS component must provide Revision History action');
|
|
assert.ok(cmsComponentSrc.includes('Restore Revision'), 'CMS component must provide Restore Revision action');
|
|
assert.ok(cmsComponentSrc.includes('min 3 characters') || cmsComponentSrc.includes('length < 3'), 'Rollback confirmation must require reason with minimum 3 chars');
|
|
assert.ok(!cmsComponentSrc.includes('setInterval'), 'CMS component must have zero setInterval');
|
|
assert.ok(!cmsComponentSrc.includes('setTimeout'), 'CMS component must have zero setTimeout');
|
|
|
|
// 42.4 Backend main.py contract check
|
|
assert.ok(backendMainSrc.includes('class OpsCmsUpdateRequest'), 'Backend must define OpsCmsUpdateRequest');
|
|
assert.ok(backendMainSrc.includes('class OpsCmsRollbackRequest'), 'Backend must define OpsCmsRollbackRequest');
|
|
assert.ok(backendMainSrc.includes('@app.get("/api/v1/ops/cms/documents")'), 'Backend must implement GET /api/v1/ops/cms/documents');
|
|
assert.ok(backendMainSrc.includes('@app.get("/api/v1/ops/cms/documents/{slug}")'), 'Backend must implement GET /api/v1/ops/cms/documents/{slug}');
|
|
assert.ok(backendMainSrc.includes('@app.put("/api/v1/ops/cms/documents/{slug}")'), 'Backend must implement PUT /api/v1/ops/cms/documents/{slug}');
|
|
assert.ok(backendMainSrc.includes('@app.post("/api/v1/ops/cms/documents/{slug}/rollback")'), 'Backend must implement POST /api/v1/ops/cms/documents/{slug}/rollback');
|
|
assert.ok(backendMainSrc.includes('cms.draft_saved'), 'Backend must audit cms.draft_saved');
|
|
assert.ok(backendMainSrc.includes('cms.published'), 'Backend must audit cms.published');
|
|
assert.ok(backendMainSrc.includes('cms.revision_restored'), 'Backend must audit cms.revision_restored');
|
|
assert.ok(backendMainSrc.includes('homepage_hero') && backendMainSrc.includes('features') && backendMainSrc.includes('screenshots') && backendMainSrc.includes('faqs') && backendMainSrc.includes('download_links') && backendMainSrc.includes('changelog') && backendMainSrc.includes('policy_pages'), 'Backend must include all 7 CMS default documents');
|
|
|
|
console.log(' PASS: Section 4 Website Content CMS backend, proxy routes, navigation, and UI contracts fully verified.');
|
|
|
|
// ============================================================
|
|
// 43. PRODUCTION OPERATIONS INFRASTRUCTURE DASHBOARD (PHASE 1)
|
|
// ============================================================
|
|
console.log('Testing 43: Operations Infrastructure Dashboard Phase 1...');
|
|
|
|
// 43.1 Normalization of null / empty / malformed response
|
|
const nullInfra = normalizeInfrastructureResponse(null);
|
|
assert.ok(nullInfra.resources, 'null input must return resources object');
|
|
assert.equal(nullInfra.resources.cpu_percent, null);
|
|
assert.equal(nullInfra.resources.load_avg, null);
|
|
assert.equal(nullInfra.resources.memory, null);
|
|
assert.equal(nullInfra.resources.swap, null);
|
|
assert.equal(nullInfra.resources.disk, null);
|
|
assert.equal(nullInfra.resources.uptime_seconds, null);
|
|
assert.equal(nullInfra.resources.uptime_formatted, null);
|
|
assert.equal(nullInfra.services.length, 4, 'null input must provide 4 default services');
|
|
assert.deepEqual(nullInfra.services.map(s => s.name), ['motorsignal-website', 'motorsignal-api', 'nginx', 'postgresql']);
|
|
assert.ok(nullInfra.services.every(s => s.status === 'unknown'), 'default services must have unknown status');
|
|
assert.equal(nullInfra.network.bytes_received, null);
|
|
assert.equal(nullInfra.network.bytes_transmitted, null);
|
|
assert.equal(nullInfra.network.label, 'Cumulative Total Since Boot');
|
|
assert.equal(nullInfra.deployment.git_revision, null);
|
|
assert.equal(nullInfra.deployment.short_revision, null);
|
|
assert.equal(nullInfra.deployment.revision_status, 'unavailable');
|
|
assert.equal(nullInfra.deployment.website_health, 'unknown');
|
|
assert.equal(nullInfra.deployment.api_health, 'unknown');
|
|
assert.equal(nullInfra.deployment.history_status, 'unavailable');
|
|
assert.equal(nullInfra.deployment.last_successful_deployment, null);
|
|
|
|
// 43.2 Normalization of valid response & disk threshold warning/critical calculation
|
|
const validInfraRaw = {
|
|
resources: {
|
|
cpu_percent: 18.5,
|
|
load_avg: [0.15, 0.22, 0.18],
|
|
memory: { total: 16000000000, used: 8000000000, available: 8000000000, percent: 50.0 },
|
|
swap: { total: 4000000000, used: 400000000, percent: 10.0 },
|
|
disk: { total: 100000000000, used: 82000000000, available: 18000000000, percent: 82.0 },
|
|
uptime_seconds: 123456,
|
|
uptime_formatted: '1d 10h 17m',
|
|
},
|
|
services: [
|
|
{ name: 'motorsignal-website', status: 'active' },
|
|
{ name: 'motorsignal-api', status: 'active' },
|
|
{ name: 'nginx', status: 'active' },
|
|
{ name: 'postgresql', status: 'active' },
|
|
],
|
|
network: {
|
|
bytes_received: 5000000000,
|
|
bytes_transmitted: 2500000000,
|
|
label: 'Cumulative Total Since Boot',
|
|
},
|
|
deployment: {
|
|
git_revision: '0123456789abcdef0123456789abcdef01234567',
|
|
short_revision: '0123456',
|
|
revision_status: 'known',
|
|
website_health: 'available',
|
|
api_health: 'available',
|
|
},
|
|
};
|
|
|
|
const normalizedValid = normalizeInfrastructureResponse(validInfraRaw);
|
|
assert.equal(normalizedValid.resources.cpu_percent, 18.5);
|
|
assert.deepEqual(normalizedValid.resources.load_avg, [0.15, 0.22, 0.18]);
|
|
assert.equal(normalizedValid.resources.memory.percent, 50.0);
|
|
assert.equal(normalizedValid.resources.disk.percent, 82.0);
|
|
assert.equal(normalizedValid.resources.disk.warning, true, 'Disk at 82% must trigger warning');
|
|
assert.equal(normalizedValid.resources.disk.critical, false, 'Disk at 82% must not trigger critical');
|
|
assert.equal(normalizedValid.services[0].status, 'active');
|
|
assert.equal(normalizedValid.network.bytes_received, 5000000000);
|
|
assert.equal(normalizedValid.deployment.git_revision, '0123456789abcdef0123456789abcdef01234567');
|
|
assert.equal(normalizedValid.deployment.short_revision, '0123456');
|
|
assert.equal(normalizedValid.deployment.revision_status, 'known');
|
|
assert.equal(normalizedValid.deployment.website_health, 'available');
|
|
assert.equal(normalizedValid.deployment.api_health, 'available');
|
|
|
|
// 43.3 Critical disk threshold (>=90%)
|
|
const criticalDiskRaw = {
|
|
resources: {
|
|
disk: { total: 100000000000, used: 91000000000, available: 9000000000, percent: 91.0 },
|
|
},
|
|
};
|
|
const normalizedCritical = normalizeInfrastructureResponse(criticalDiskRaw);
|
|
assert.equal(normalizedCritical.resources.disk.warning, true);
|
|
assert.equal(normalizedCritical.resources.disk.critical, true, 'Disk at 91% must trigger critical');
|
|
|
|
// 43.4 Service status validation
|
|
assert.equal(isValidServiceStatus('active'), true);
|
|
assert.equal(isValidServiceStatus('inactive'), true);
|
|
assert.equal(isValidServiceStatus('failed'), true);
|
|
assert.equal(isValidServiceStatus('unknown'), true);
|
|
assert.equal(isValidServiceStatus('running'), false);
|
|
assert.equal(isValidServiceStatus('dead'), false);
|
|
|
|
// 43.5 Types, Navigation & Shell wiring
|
|
assert.ok(typesSrc.includes("'infrastructure'"), "types.ts must include 'infrastructure' in OperationsNavId");
|
|
assert.ok(typesSrc.includes('OperationsInfrastructureData'), 'types.ts must define OperationsInfrastructureData');
|
|
assert.ok(typesSrc.includes('SystemResources'), 'types.ts must define SystemResources');
|
|
assert.ok(typesSrc.includes('ServiceHealthItem'), 'types.ts must define ServiceHealthItem');
|
|
assert.ok(typesSrc.includes('NetworkTotals'), 'types.ts must define NetworkTotals');
|
|
assert.ok(typesSrc.includes('DeploymentHealth'), 'types.ts must define DeploymentHealth');
|
|
|
|
const updatedSidebarSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-sidebar.tsx'), 'utf8');
|
|
const updatedShellSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-shell.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
updatedSidebarSource.includes("id: 'infrastructure'") &&
|
|
updatedSidebarSource.includes("label: 'Infrastructure'") &&
|
|
updatedSidebarSource.includes("icon: 'dns'"),
|
|
'Sidebar must contain infrastructure nav item'
|
|
);
|
|
|
|
const infraOverviewNavIdx = updatedSidebarSource.indexOf("id: 'overview'");
|
|
const infraNavIdx = updatedSidebarSource.indexOf("id: 'infrastructure'");
|
|
const infraUsersNavIdx = updatedSidebarSource.indexOf("id: 'users'");
|
|
assert.ok(
|
|
infraOverviewNavIdx < infraNavIdx && infraNavIdx < infraUsersNavIdx,
|
|
'Infrastructure nav item must be placed right after overview and before users'
|
|
);
|
|
|
|
assert.ok(
|
|
updatedShellSource.includes("activeNav === 'infrastructure'") &&
|
|
updatedShellSource.includes('<OperationsInfrastructure'),
|
|
'OperationsShell must wire activeNav === infrastructure to OperationsInfrastructure'
|
|
);
|
|
|
|
// 43.6 UI Component Contracts (operations-infrastructure.tsx)
|
|
const infraComponentSrc = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-infrastructure.tsx'), 'utf8');
|
|
|
|
// Read-only safety: zero mutation or arbitrary command controls
|
|
assert.ok(!infraComponentSrc.includes('Restart') && !infraComponentSrc.includes('restart'), 'Infrastructure component must NOT have restart action');
|
|
assert.ok(!infraComponentSrc.includes('Deploy Now') && !infraComponentSrc.includes('Trigger Deploy') && !infraComponentSrc.includes('action="deploy"'), 'Infrastructure component must NOT have deploy action');
|
|
assert.ok(!infraComponentSrc.includes('Rollback') && !infraComponentSrc.includes('rollback'), 'Infrastructure component must NOT have rollback action');
|
|
assert.ok(!infraComponentSrc.includes('Stop Service') && !infraComponentSrc.includes('Kill'), 'Infrastructure component must NOT have Stop/Kill action');
|
|
assert.ok(!infraComponentSrc.includes('exec(') && !infraComponentSrc.includes('spawn('), 'Infrastructure component must NOT execute shell commands');
|
|
|
|
// Cumulative network totals labeling
|
|
assert.ok(
|
|
infraComponentSrc.includes('Cumulative Total Since Boot'),
|
|
'Infrastructure component must display Cumulative Total Since Boot'
|
|
);
|
|
assert.ok(
|
|
infraComponentSrc.includes('Not a rate') || infraComponentSrc.includes('Not an instantaneous'),
|
|
'Infrastructure component must explicitly clarify network is not a rate'
|
|
);
|
|
|
|
// Auto-refresh & duplicate request suppression
|
|
assert.ok(
|
|
infraComponentSrc.includes('30000') && infraComponentSrc.includes('setInterval'),
|
|
'Infrastructure component must poll on 30s interval'
|
|
);
|
|
assert.ok(
|
|
infraComponentSrc.includes('visibilitychange') && infraComponentSrc.includes('visibilityState'),
|
|
'Infrastructure component must gate polling on document.visibilityState'
|
|
);
|
|
assert.ok(
|
|
infraComponentSrc.includes('isFetchingRef'),
|
|
'Infrastructure component must use isFetchingRef to prevent overlapping requests'
|
|
);
|
|
|
|
// 43.7 Next.js API Route Proxy & Backend Main Contract
|
|
const infraRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/infrastructure/route.ts'), 'utf8');
|
|
assert.ok(
|
|
infraRouteSrc.includes('__Host-motorsignal_ops'),
|
|
'Next.js infrastructure route must enforce __Host-motorsignal_ops in production'
|
|
);
|
|
assert.ok(
|
|
infraRouteSrc.includes('/api/v1/ops/infrastructure'),
|
|
'Next.js infrastructure route must proxy to /api/v1/ops/infrastructure'
|
|
);
|
|
|
|
assert.ok(
|
|
backendMainSrc.includes('@app.get("/api/v1/ops/infrastructure")'),
|
|
'Backend main.py must register GET /api/v1/ops/infrastructure'
|
|
);
|
|
assert.ok(
|
|
backendMainSrc.includes('require_ops_roles("operator", "owner")'),
|
|
'Backend main.py must guard infrastructure endpoint with require_ops_roles("operator", "owner")'
|
|
);
|
|
|
|
const infraMetricsSrc = fs.readFileSync(path.resolve(__dirname, '../../backend/motorsignal_api/app/infrastructure_metrics.py'), 'utf8');
|
|
assert.ok(
|
|
infraMetricsSrc.includes('ALLOWED_SYSTEMD_SERVICES') &&
|
|
infraMetricsSrc.includes('motorsignal-website') &&
|
|
infraMetricsSrc.includes('motorsignal-api') &&
|
|
infraMetricsSrc.includes('nginx') &&
|
|
infraMetricsSrc.includes('postgresql'),
|
|
'infrastructure_metrics.py must enforce strict allowlist of 4 services'
|
|
);
|
|
|
|
// 43.8 Dual-Option Speedtest Contracts (Client to Server vs Server to External)
|
|
assert.ok(typesSrc.includes('SpeedtestMode'), 'types.ts must define SpeedtestMode');
|
|
assert.ok(
|
|
typesSrc.includes("'client_to_server' | 'server_to_external'"),
|
|
"types.ts must include 'client_to_server' and 'server_to_external' in SpeedtestMode"
|
|
);
|
|
|
|
assert.ok(
|
|
infraComponentSrc.includes('My Connection → MotorSignal'),
|
|
'Infrastructure component must display My Connection → MotorSignal speedtest option'
|
|
);
|
|
assert.ok(
|
|
infraComponentSrc.includes('MotorSignal → Upstream Server'),
|
|
'Infrastructure component must display MotorSignal → Upstream Server speedtest option'
|
|
);
|
|
assert.ok(
|
|
infraComponentSrc.includes('handleRunClientSpeedtest'),
|
|
'Infrastructure component must define handleRunClientSpeedtest'
|
|
);
|
|
assert.ok(
|
|
infraComponentSrc.includes('handleRunServerSpeedtest'),
|
|
'Infrastructure component must define handleRunServerSpeedtest'
|
|
);
|
|
assert.ok(
|
|
infraComponentSrc.includes('speedtestProgress'),
|
|
'Infrastructure component must track real-time speedtestProgress'
|
|
);
|
|
|
|
const speedtestRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/infrastructure/speedtest/route.ts'), 'utf8');
|
|
assert.ok(speedtestRouteSrc.includes('export async function GET'), 'Speedtest route must support GET for ping and download probes');
|
|
assert.ok(speedtestRouteSrc.includes('export async function POST'), 'Speedtest route must support POST for upload and upstream benchmarks');
|
|
assert.ok(speedtestRouteSrc.includes("action === 'ping'"), 'Speedtest route must support action=ping');
|
|
assert.ok(speedtestRouteSrc.includes("action === 'download'"), 'Speedtest route must support action=download');
|
|
assert.ok(speedtestRouteSrc.includes("action === 'upload'"), 'Speedtest route must support action=upload');
|
|
assert.ok(speedtestRouteSrc.includes('hasValidOrigin(request)'), 'Speedtest POST route must enforce same-origin CSRF barrier');
|
|
assert.ok(speedtestRouteSrc.includes('__Host-motorsignal_ops'), 'Speedtest route must enforce __Host-motorsignal_ops in production');
|
|
|
|
assert.ok(
|
|
backendMainSrc.includes('@app.post("/api/v1/ops/infrastructure/speedtest")'),
|
|
'Backend main.py must register POST /api/v1/ops/infrastructure/speedtest'
|
|
);
|
|
assert.ok(
|
|
backendMainSrc.includes('client_to_server'),
|
|
'Backend main.py must support client_to_server reporting in speedtest'
|
|
);
|
|
|
|
console.log(' PASS: Section 43 Production Operations Infrastructure Dashboard (Phase 1 & Dual Speedtest) verified.');
|
|
|
|
// ============================================================
|
|
// 44. OPERATIONS ACTIONS & DEPLOYMENT CONTROLS (PHASE 2)
|
|
// ============================================================
|
|
console.log('Testing 44: Operations Actions & Deployment Controls (Phase 2)...');
|
|
|
|
// 44.1 UI Component Contracts (operations-actions.tsx)
|
|
const actionsComponentSrc = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-actions.tsx'), 'utf8');
|
|
|
|
// Strict Action allowlist and confirmation requirements
|
|
assert.ok(actionsComponentSrc.includes('deploy_website'), 'Actions component must include deploy_website');
|
|
assert.ok(actionsComponentSrc.includes('restart_website'), 'Actions component must include restart_website');
|
|
assert.ok(actionsComponentSrc.includes('restart_api'), 'Actions component must include restart_api');
|
|
assert.ok(actionsComponentSrc.includes('reboot_server'), 'Actions component must include reboot_server');
|
|
|
|
assert.ok(
|
|
actionsComponentSrc.includes('RESTART API'),
|
|
'Actions component must require exact typed confirmation "RESTART API"'
|
|
);
|
|
assert.ok(
|
|
actionsComponentSrc.includes('REBOOT MOTORSIGNAL SERVER'),
|
|
'Actions component must require exact typed confirmation "REBOOT MOTORSIGNAL SERVER"'
|
|
);
|
|
|
|
// Role restriction
|
|
assert.ok(
|
|
actionsComponentSrc.includes("role === 'owner'") || actionsComponentSrc.includes('isOwner'),
|
|
'Actions component must restrict action triggers to owner role'
|
|
);
|
|
|
|
// Mutual exclusion & active action handling
|
|
assert.ok(
|
|
actionsComponentSrc.includes('activeAction'),
|
|
'Actions component must track and display active running actions'
|
|
);
|
|
|
|
// History table columns
|
|
assert.ok(actionsComponentSrc.includes('Timestamp'), 'History table must display Timestamp column');
|
|
assert.ok(actionsComponentSrc.includes('Action'), 'History table must display Action column');
|
|
assert.ok(actionsComponentSrc.includes('Actor'), 'History table must display Actor column');
|
|
assert.ok(actionsComponentSrc.includes('Status'), 'History table must display Status column');
|
|
assert.ok(actionsComponentSrc.includes('Revision'), 'History table must display Revision column');
|
|
|
|
// 44.1b Live Progress Tracking & Completion State (Phase 2 UI Tracking)
|
|
assert.ok(
|
|
actionsComponentSrc.includes('DEPLOY_PHASES') &&
|
|
actionsComponentSrc.includes('Preflight Checks') &&
|
|
actionsComponentSrc.includes('Release Staging') &&
|
|
actionsComponentSrc.includes('Production Build') &&
|
|
actionsComponentSrc.includes('Health Verification'),
|
|
'Actions component must define multi-phase deployment stepper'
|
|
);
|
|
assert.ok(
|
|
actionsComponentSrc.includes('formatElapsed') && actionsComponentSrc.includes('elapsedSeconds'),
|
|
'Actions component must track and display live elapsed execution time'
|
|
);
|
|
assert.ok(
|
|
actionsComponentSrc.includes('completedAction') &&
|
|
actionsComponentSrc.includes('modalMode') &&
|
|
actionsComponentSrc.includes('isMinimized'),
|
|
'Actions component must implement modal state transitions (confirm -> progress -> completed) with minimize'
|
|
);
|
|
assert.ok(
|
|
actionsComponentSrc.includes('recentBannerAction'),
|
|
'Actions component must display persistent completion banner after action execution'
|
|
);
|
|
|
|
// Embedded in Infrastructure view
|
|
const updatedInfraSrc = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-infrastructure.tsx'), 'utf8');
|
|
assert.ok(
|
|
updatedInfraSrc.includes('OperationsActions'),
|
|
'OperationsInfrastructure must embed OperationsActions component'
|
|
);
|
|
|
|
// 44.2 Next.js API Route Proxies for Actions
|
|
const actionsRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/actions/route.ts'), 'utf8');
|
|
assert.ok(
|
|
actionsRouteSrc.includes('hasValidOrigin'),
|
|
'Next.js actions route must enforce CSRF protection via hasValidOrigin'
|
|
);
|
|
assert.ok(
|
|
actionsRouteSrc.includes('__Host-motorsignal_ops'),
|
|
'Next.js actions route must enforce __Host-motorsignal_ops in production'
|
|
);
|
|
assert.ok(
|
|
actionsRouteSrc.includes('/api/v1/ops/actions'),
|
|
'Next.js actions route must proxy to backend /api/v1/ops/actions'
|
|
);
|
|
assert.ok(
|
|
actionsRouteSrc.includes('409'),
|
|
'Next.js actions route must forward 409 Conflict status'
|
|
);
|
|
|
|
const capabilitiesRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/actions/capabilities/route.ts'), 'utf8');
|
|
assert.ok(
|
|
capabilitiesRouteSrc.includes('/api/v1/ops/actions/capabilities'),
|
|
'Capabilities proxy must query backend capabilities endpoint'
|
|
);
|
|
|
|
const actionDetailRouteSrc = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/actions/[actionId]/route.ts'), 'utf8');
|
|
assert.ok(
|
|
actionDetailRouteSrc.includes('/api/v1/ops/actions/'),
|
|
'Action detail proxy must query backend action by ID'
|
|
);
|
|
|
|
// 44.3 Backend Endpoints & Role Gates in main.py
|
|
assert.ok(
|
|
backendMainSrc.includes('@app.post("/api/v1/ops/actions'),
|
|
'Backend main.py must register POST /api/v1/ops/actions'
|
|
);
|
|
assert.ok(
|
|
backendMainSrc.includes('@app.get("/api/v1/ops/actions'),
|
|
'Backend main.py must register GET /api/v1/ops/actions'
|
|
);
|
|
assert.ok(
|
|
backendMainSrc.includes('@app.get("/api/v1/ops/actions/capabilities'),
|
|
'Backend main.py must register GET /api/v1/ops/actions/capabilities'
|
|
);
|
|
assert.ok(
|
|
backendMainSrc.includes('@app.get("/api/v1/ops/actions/{action_id}'),
|
|
'Backend main.py must register GET /api/v1/ops/actions/{action_id}'
|
|
);
|
|
assert.ok(
|
|
backendMainSrc.includes('reconcile_stale_actions'),
|
|
'Backend main.py must reconcile stale actions on startup'
|
|
);
|
|
|
|
// 44.4 Privileged Boundary Helper Script
|
|
const helperSrc = fs.readFileSync(path.resolve(__dirname, '../../backend/motorsignal_api/scripts/ops-action-helper.sh'), 'utf8');
|
|
assert.ok(helperSrc.includes('deploy_website'), 'Helper must handle deploy_website');
|
|
assert.ok(helperSrc.includes('restart_website'), 'Helper must handle restart_website');
|
|
assert.ok(helperSrc.includes('restart_api'), 'Helper must handle restart_api');
|
|
assert.ok(helperSrc.includes('reboot_server'), 'Helper must handle reboot_server');
|
|
assert.ok(helperSrc.includes('PREV_RELEASE'), 'Helper must retain PREV_RELEASE for automatic rollback');
|
|
assert.ok(helperSrc.includes('/api/version'), 'Helper must verify /api/version ingress readiness');
|
|
|
|
console.log(' PASS: Section 44 Operations Actions & Deployment Controls (Phase 2) verified.');
|
|
|
|
console.log('--- ALL OPS-3C, OPS-3C.1, OPS-3C.2, OPS-3C.3, OPS-4, OPS-4.2, OVERVIEW, SECTION 2 USERS, SECTION 3 SUPPORT CENTER, SECTION 4 CMS, SECTION 43 INFRASTRUCTURE & SECTION 44 ACTIONS TESTS PASSED (44/44) ---');
|