import { describe, expect, it, vi } from 'vitest';
import { readFileSync } from 'node:fs';
import { JSDOM } from 'jsdom';
import type { ImportedDataset } from '../../src/domain/dataset';
import { makeSheet } from '../../src/importers/sheet-factory';
import { buildReportProjection, type ReportProjection, type ReportWorkRecord } from '../../src/reporting/report-projection';
import { buildReportWorkspaceView, resultStatement } from '../../src/reporting/report-workspace-view';
import { buildTechnicalReportHtml } from '../../src/reporting/technical-report';
import { reportRichText } from '../../src/reporting/report-rich-text';
import { buildTechnicalReportModel } from '../../src/reporting/technical-report-model';
import { reportContext } from './context-report-fixture';
// Vitest stubs CSS imports; the production build embeds the token files verbatim (`?raw`).
vi.mock('../../src/styles/ui-tokens.css?raw', () => ({ default: readFileSync('src/styles/ui-tokens.css', 'utf8') }));
vi.mock('../../src/styles/tokens.css?raw', () => ({ default: readFileSync('src/styles/tokens.css', 'utf8') }));
// Synthetic data only.
const NO_SCRIPT = /');
const document = new JSDOM(`
${html}
`).window.document;
expect([...document.querySelectorAll('h5')].map(item => item.textContent)).toEqual(['Remediation action', 'Related links']);
expect([...document.querySelectorAll('ol li')].map(item => item.textContent)).toEqual(['Open the Synthetic portal.', 'Click Next.', 'Finish.']);
expect(document.querySelector('ol b')?.textContent).toBe('Synthetic');
expect([...document.querySelectorAll('ul li')].map(item => item.textContent)).toEqual(['Synthetic Guide - Page 2', 'Unsafe) ']);
expect([...document.querySelectorAll('a')].map(item => item.getAttribute('href'))).toEqual(['https://example.test/next', 'https://example.test/guide']);
expect(document.querySelector('script')).toBeNull();
expect(document.body.textContent).not.toMatch(/\*\*|####|\]\(/);
// A CIS rule number and a single `#` inside a URL stay text.
const plain = new JSDOM(`${reportRichText('1.1.1 (L1) Ensure synthetic setting [Link](https://example.test/a?b=" # "/c)')}
`).window.document;
expect(plain.querySelectorAll('h5, ol')).toHaveLength(0);
expect(plain.body.textContent).toBe('1.1.1 (L1) Ensure synthetic setting Link');
});
it('keeps only the statement of a result text in the card, without appended evidence or URLs', () => {
expect(resultStatement('Your tenant has 3 resource(s) with access to all subscriptions in this tenant. - [Synthetic A](https://example.test/a) - [Synthetic B](https://example.test/b)'))
.toBe('Your tenant has 3 resource(s) with access to all subscriptions in this tenant.');
expect(resultStatement('You have disabled a synthetic setting. ➡️ Open [Recommendation](https://example.test/r) in the portal.'))
.toBe('You have disabled a synthetic setting.');
expect(resultStatement('Your tenant does not have [diagnostic settings](https://example.test/d) configured for all logs:\n| Log | State |'))
.toBe('Your tenant does not have diagnostic settings configured for all logs');
expect(resultStatement('8 user(s) are inactive - Synthetic One with Role A - Synthetic Two with Role B')).toBe('8 user(s) are inactive');
expect(resultStatement('Found 1 synthetic endpoint that may support NTLM authentication.')).toBe('Found 1 synthetic endpoint that may support NTLM authentication.');
});
it('builds the lean Work cut with native area, counting key and the frozen Pathfinder references', () => {
const sheet = makeSheet('Project', [['Title', 'Category', 'Status', 'Benchmark section', 'Source Rule ID'],
['Synthetic rule', 'CIS', 'Fail', 'Local Policies > User Rights Assignment', '2.2.2']], '', 'synthetic.csv', { isProjectSheet: true });
const item = sheet.analysis.records[0];
item.findingOccurrenceId = 'synthetic-occurrence';
item.referenceRecognition = { references: [
{ framework: 'mitre-attack', id: 'tactic:Credential Access', title: 'Credential Access', kind: 'tactic', tactics: ['Credential Access'],
status: 'active', evidence: 'declared', matchedBy: 'exact-title', catalogVersion: '19.2', catalogSha256: 'x',
source: { fieldKey: 'k', fieldLabel: 'l', valuePath: '', representation: 'value', start: 0, end: 1, offsetUnit: 'utf16', text: 'C' } },
{ framework: 'mitre-attack', id: 'T1003', title: 'Retired', kind: 'technique', tactics: [], status: 'revoked', evidence: 'declared',
matchedBy: 'id', catalogVersion: '19.2', catalogSha256: 'x',
source: { fieldKey: 'k', fieldLabel: 'l', valuePath: '', representation: 'value', start: 0, end: 1, offsetUnit: 'utf16', text: 'T' } }
] } as unknown as NonNullable;
const view = buildReportWorkspaceView({ sheets: [sheet] } as unknown as ImportedDataset)!;
expect(view.records[0]).toMatchObject({
area: 'Local Policies > User Rights Assignment', ruleId: '2.2.2', countingKey: 'scanner:synthetic-occurrence',
attack: [{ kind: 'tactic', title: 'Credential Access', evidence: 'declared' }], defenses: []
});
expect(view.records[0]).not.toHaveProperty('fields');
});
});