This commit is contained in:
2026-06-25 12:05:53 -05:00
commit b58e50e423
141 changed files with 28190 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
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));
});