import { db, adminUserId, makeUser, load } from './setup.mjs'; import test from 'node:test'; import assert from 'node:assert/strict'; const { createChangeRequest, findApprovedRequest, findPendingRequest, decideChangeRequest, markRequestExecuted, listChangeRequests } = await load('../models/changeRequestModel.js'); const { intuneReportingOverview, recordGraphAudit } = await load('../models/graphModel.js'); const { createIntuneDeployment } = await load('../models/psadtModel.js'); const owner = adminUserId(); const other = makeUser({ email: 'gov-other@test.local' }); const deployment = createIntuneDeployment({ name: 'Gov Test App', visibility: 'shared', status: 'ready' }, owner); test('approval lifecycle: pending -> approved -> executed', () => { const request = createChangeRequest({ deploymentId: deployment.id, connectionId: null, action: 'publish', requestedBy: owner, requesterEmail: 'a@b.c' }); assert.equal(request.status, 'pending'); assert.equal(findPendingRequest(deployment.id, 'publish').id, request.id); assert.equal(findApprovedRequest(deployment.id, 'publish'), null); const approved = decideChangeRequest(request.id, { status: 'approved', approverUserId: owner, approverEmail: 'admin@b.c' }); assert.equal(approved.status, 'approved'); assert.equal(findApprovedRequest(deployment.id, 'publish').id, request.id); markRequestExecuted(request.id); assert.equal(findApprovedRequest(deployment.id, 'publish'), null, 'executed request is no longer approved'); }); test('deciding an already-decided request returns null', () => { const request = createChangeRequest({ deploymentId: deployment.id, action: 'assign', requestedBy: owner }); decideChangeRequest(request.id, { status: 'rejected', approverUserId: owner }); assert.equal(decideChangeRequest(request.id, { status: 'approved', approverUserId: owner }), null); }); test('change requests are scoped to visible deployments for non-admins', () => { createChangeRequest({ deploymentId: deployment.id, action: 'relationships', requestedBy: owner }); // deployment is shared, so a non-admin still sees its requests assert.ok(listChangeRequests(other, { isAdmin: false }).some((r) => r.deploymentId === deployment.id)); }); test('reporting overview aggregates deployment status and install states', () => { const ts = new Date().toISOString(); const insert = db.prepare(` INSERT INTO intune_deployment_statuses (id, deployment_id, graph_app_id, scope, install_state, error_code, created_at, updated_at) VALUES (?, ?, 'app-1', 'device', ?, ?, ?, ?) `); insert.run('ids_1', deployment.id, 'failed', '0x87D1041C', ts, ts); insert.run('ids_2', deployment.id, 'installed', '', ts, ts); insert.run('ids_3', deployment.id, 'failed', '0x87D1041C', ts, ts); const overview = intuneReportingOverview(owner, false); const failed = overview.installStateCounts.find((row) => row.state === 'failed'); assert.equal(failed.count, 2); assert.ok(overview.deploymentsByStatus.some((row) => row.status === 'ready')); assert.equal(overview.topErrors[0].code, '0x87D1041C'); assert.equal(overview.topErrors[0].count, 2); }); test('reporting overview includes soak timers and a write-action trend', () => { const soaking = createIntuneDeployment({ name: 'Soak Report', visibility: 'shared', autoPromoteAfterHours: 24, autoPromoteRing: 'Broad' }, owner); db.prepare('UPDATE intune_deployments SET soak_started_at = ? WHERE id = ?') .run(new Date(Date.now() - 3_600_000).toISOString(), soaking.id); recordGraphAudit({ deploymentId: soaking.id, actorUserId: owner, action: 'win32app.publish', status: 'success', message: 'ok' }); const overview = intuneReportingOverview(owner, false); const timer = overview.soakTimers.find((row) => row.id === soaking.id); assert.ok(timer, 'soak timer present'); assert.ok(timer.hoursRemaining > 22 && timer.hoursRemaining <= 24, `~23h remaining, got ${timer.hoursRemaining}`); assert.ok(overview.auditTrend.some((row) => row.status === 'success' && row.count >= 1)); });