CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (push) Successful in 7s
masterplan-lock / check (pull_request) Successful in 7s
CI (SIAX Cloud) / security (pull_request) Successful in 20s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 22s
CI (SIAX Cloud) / integration (pull_request) Successful in 10m7s
CI (SIAX Cloud) / quality (pull_request) Successful in 10m17s
Per the reap-use mandate: 'identical file + different instruction' is a DIFFERENT operation. bindOperationToUploadIdentity() folds instruction, field contract, model, data class and export format into the DOC0-compatible upload filename so the durable fingerprint covers every result-affecting input. The bytes' SHA-256 stays stable; DOC0 still receives the caller's x-idempotency-key (its own idempotency, its own contract). Classification: STANDARDIZE/0pt-owned (upstream DOC0 untouched).
47 lines
2.5 KiB
TypeScript
47 lines
2.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
bindOperationToUploadIdentity,
|
|
prepareDoc0UploadIdentity,
|
|
type Doc0UploadInput,
|
|
} from '../../src/document-workbench/doc0-upload-client';
|
|
|
|
const input: Doc0UploadInput = {
|
|
tenantId: 't1', projectId: 'p1', actorId: 'a1', requestId: 'r1', idempotencyKey: 'k1',
|
|
filename: 'rapport.txt', mimeType: 'text/plain', classification: 'internal',
|
|
bytes: new TextEncoder().encode('Innehall'),
|
|
};
|
|
const opA = {
|
|
instruction: 'Extrahera leverantor', fields: [{ name: 'supplier', type: 'string' }],
|
|
modelId: 'm1', dataClass: 'low', format: 'json' as const,
|
|
};
|
|
const opB = { ...opA, instruction: 'Extrahera belopp' };
|
|
|
|
describe('operation-scoped upload identity', () => {
|
|
it('same file + same operation binds to one identity', () => {
|
|
expect(prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, opA)))
|
|
.toEqual(prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, opA)));
|
|
});
|
|
it('same file + DIFFERENT instruction = DIFFERENT identity (not the same operation)', () => {
|
|
const a = prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, opA));
|
|
const b = prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, opB));
|
|
expect(a.requestFingerprint).not.toBe(b.requestFingerprint);
|
|
});
|
|
it('changed model or data class also changes the operation identity', () => {
|
|
const c = prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, { ...opA, modelId: 'm2' }));
|
|
const d = prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, { ...opA, dataClass: 'high' }));
|
|
const e = prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, { ...opA, format: 'csv' }));
|
|
expect(c.requestFingerprint).not.toBe(prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, opA)).requestFingerprint);
|
|
expect(d.requestFingerprint).not.toBe(prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, opA)).requestFingerprint);
|
|
expect(e.requestFingerprint).not.toBe(prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, opA)).requestFingerprint);
|
|
});
|
|
it('raw bytes checksum stays stable across operations', () => {
|
|
const a = prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, opA));
|
|
const b = prepareDoc0UploadIdentity(bindOperationToUploadIdentity(input, opB));
|
|
expect(a.sourceSha256).toBe(b.sourceSha256);
|
|
});
|
|
it('prefixes the operation marker so odd filenames cannot collide', () => {
|
|
const bound = bindOperationToUploadIdentity(input, opA);
|
|
expect(bound.filename.startsWith('rapport.txt#')).toBe(true);
|
|
});
|
|
});
|