import { describe, expect, it, vi } from 'vitest'; import { isStylesheetExecutionTrusted } from '../src/transform/stylesheetTrust'; import { runTransformation } from '../src/transform/transformService'; import { TRANSFORM_LIMITS, assertTransformInputWithinLimits, assertTransformOutputWithinLimits, withTransformationTimeout, } from '../src/transform/transformLimits'; describe('stylesheet execution trust', () => { it('requires both the exact SHA-256 and the current edit revision', () => { const trust = { hash: 'sha256-a', revision: 4 }; expect(isStylesheetExecutionTrusted(trust, 'sha256-a', 4)).toBe(true); expect(isStylesheetExecutionTrusted(trust, 'sha256-b', 4)).toBe(false); expect(isStylesheetExecutionTrusted(trust, 'sha256-a', 5)).toBe(false); expect(isStylesheetExecutionTrusted(null, 'sha256-a', 4)).toBe(false); }); it.each(['saxon-js-dynamic', 'native-xsltprocessor'] as const)( 'refuses the %s service path when authorization does not match', async (engine) => { await expect( runTransformation( { engine, xmlText: '', xsltText: '', }, { trustedStylesheet: { hash: 'not-the-stylesheet-hash', revision: 7 }, currentRevision: 7, } ) ).rejects.toThrow(/review and trust this exact stylesheet/i); } ); }); describe('transformation resource limits', () => { it('rejects oversized XML, XSLT, and result strings', () => { expect(() => assertTransformInputWithinLimits({ engine: 'native-xsltprocessor', xmlText: 'x'.repeat(TRANSFORM_LIMITS.maxXmlBytes + 1), xsltText: '', }) ).toThrow(/XML input exceeds/i); expect(() => assertTransformInputWithinLimits({ engine: 'saxon-js-dynamic', xmlText: '', xsltText: 'x'.repeat(TRANSFORM_LIMITS.maxXsltBytes + 1), }) ).toThrow(/stylesheet exceeds/i); expect(() => assertTransformOutputWithinLimits({ engine: 'saxon-js-dynamic', output: 'x'.repeat(TRANSFORM_LIMITS.maxOutputBytes + 1), diagnostics: [], transformedAt: '2026-01-01T00:00:00.000Z', }) ).toThrow(/output exceeds/i); }); it('rejects an asynchronous execution that exceeds its deadline', async () => { vi.useFakeTimers(); try { const result = withTransformationTimeout( () => new Promise(() => undefined), 25 ); const rejection = expect(result).rejects.toThrow(/25-millisecond/i); await vi.advanceTimersByTimeAsync(25); await rejection; } finally { vi.useRealTimers(); } }); });