ihm_client/tests/unit/channel-messages.test.ts

156 lines
6.6 KiB
TypeScript

import { registerAllListeners } from '../../src/router';
import { MessageType } from '../../src/models/process.model';
// Mocks TokenService
jest.mock('../../src/services/token', () => {
class TokenServiceMock {
static async getInstance() { return new TokenServiceMock(); }
async generateSessionToken(origin: string) {
return { accessToken: `access-${origin}`, refreshToken: `refresh-${origin}` };
}
async validateToken(token: string, origin: string) { return true; }
async refreshAccessToken(refreshToken: string, origin: string) { return `new-access-${origin}`; }
}
return { __esModule: true, default: TokenServiceMock };
});
// Mocks Services
jest.mock('../../src/services/service', () => {
class ServicesMock {
static instance: any;
static async getInstance() {
if (!ServicesMock.instance) ServicesMock.instance = new ServicesMock();
return ServicesMock.instance;
}
isPaired() { return true; }
async confirmPairing() { /* no-op */ }
getPairingProcessId() { return 'pair-123'; }
async getProcesses() { return { 'p1': { id: 'p1' } as any }; }
async getMyProcesses() { return ['p1', 'p2']; }
async getProcess(id: string) { return { states: [{ state_id: 'a'.repeat(64), public_data: {}, pcd_commitment: {}, validation_tokens: [] }] } as any; }
async decryptAttribute() { return null; }
decodeValue(v: any) { return v; }
getHashForFile() { return 'hash-abc'; }
getMerkleProofForFile() { return { proof: true } as any; }
validateMerkleProof() { return true; }
async createPrdUpdate() { return {}; }
async approveChange() { return { updated_process: { id: 'p1' } }; }
async updateProcess() { return { updated_process: { id: 'p1' } }; }
async createProcess() { return { updated_process: { process_id: 'pid', current_process: { states: [{ state_id: 'a'.repeat(64) }] } } } as any; }
async handleApiReturn() { /* no-op */ }
}
return { __esModule: true, default: ServicesMock };
});
// Mocks ModalService (used by REQUEST_LINK)
jest.mock('../../src/services/modal.service', () => {
class ModalServiceMock {
static async getInstance() { return new ModalServiceMock(); }
async showConfirmationModal() { return true; }
}
return { __esModule: true, default: ModalServiceMock };
});
describe('postMessage interfaces (registerAllListeners)', () => {
const ORIGIN = 'https://host.test';
let postSpy: jest.SpyInstance;
beforeAll(async () => {
postSpy = jest.spyOn(window.parent, 'postMessage');
await registerAllListeners();
});
afterEach(() => {
postSpy.mockClear();
});
afterAll(() => {
postSpy.mockRestore();
});
function dispatch(data: any) {
window.dispatchEvent(new MessageEvent('message', { data, origin: ORIGIN }));
}
async function waitForLastCall(expectedType?: string, prevCount?: number) {
const start = Date.now();
const timeoutMs = 1000;
// eslint-disable-next-line no-constant-condition
while (true) {
const calls = postSpy.mock.calls;
const countOk = prevCount === undefined ? calls.length > 0 : calls.length > (prevCount || 0);
if (countOk) {
const last = calls[calls.length - 1] as any;
const payload = last && last[0];
if (!expectedType || (payload && payload.type === expectedType)) {
return last;
}
}
if (Date.now() - start > timeoutMs) throw new Error('Timeout waiting for postMessage');
await new Promise(r => setTimeout(r, 10));
}
}
it('REQUEST_LINK -> LINK_ACCEPTED avec tokens', async () => {
const prev = postSpy.mock.calls.length;
dispatch({ type: MessageType.REQUEST_LINK, messageId: 'm1' });
const [payload, targetOrigin] = (await waitForLastCall(MessageType.LINK_ACCEPTED, prev)) as any;
expect(targetOrigin).toBe(ORIGIN);
expect(payload.type).toBe(MessageType.LINK_ACCEPTED);
expect(payload.accessToken).toContain('access-');
expect(payload.refreshToken).toContain('refresh-');
expect(payload.messageId).toBe('m1');
});
it('VALIDATE_TOKEN -> VALIDATE_TOKEN avec isValid=true', async () => {
const prev = postSpy.mock.calls.length;
dispatch({ type: MessageType.VALIDATE_TOKEN, accessToken: 'a', refreshToken: 'r', messageId: 'm2' });
const [payload, targetOrigin] = (await waitForLastCall(MessageType.VALIDATE_TOKEN, prev)) as any;
expect(targetOrigin).toBe(ORIGIN);
expect(payload.type).toBe(MessageType.VALIDATE_TOKEN);
expect(payload.isValid).toBe(true);
expect(payload.messageId).toBe('m2');
});
it('RENEW_TOKEN -> RENEW_TOKEN avec nouveau accessToken', async () => {
const prev = postSpy.mock.calls.length;
dispatch({ type: MessageType.RENEW_TOKEN, refreshToken: 'r', messageId: 'm3' });
const [payload, targetOrigin] = (await waitForLastCall(MessageType.RENEW_TOKEN, prev)) as any;
expect(targetOrigin).toBe(ORIGIN);
expect(payload.type).toBe(MessageType.RENEW_TOKEN);
expect(typeof payload.accessToken).toBe('string');
expect(payload.refreshToken).toBe('r');
expect(payload.messageId).toBe('m3');
});
it('GET_PAIRING_ID -> GET_PAIRING_ID avec userPairingId', async () => {
const prev = postSpy.mock.calls.length;
dispatch({ type: MessageType.GET_PAIRING_ID, accessToken: 'a', messageId: 'm4' });
const [payload, targetOrigin] = (await waitForLastCall(MessageType.GET_PAIRING_ID, prev)) as any;
expect(targetOrigin).toBe(ORIGIN);
expect(payload.type).toBe(MessageType.GET_PAIRING_ID);
expect(payload.userPairingId).toBe('pair-123');
expect(payload.messageId).toBe('m4');
});
it('GET_MY_PROCESSES -> GET_MY_PROCESSES avec liste', async () => {
const prev = postSpy.mock.calls.length;
dispatch({ type: MessageType.GET_MY_PROCESSES, accessToken: 'a', messageId: 'm5' });
const [payload, targetOrigin] = (await waitForLastCall(MessageType.GET_MY_PROCESSES, prev)) as any;
expect(targetOrigin).toBe(ORIGIN);
expect(payload.type).toBe(MessageType.GET_MY_PROCESSES);
expect(Array.isArray(payload.myProcesses)).toBe(true);
expect(payload.messageId).toBe('m5');
});
it('HASH_VALUE -> VALUE_HASHED avec hash', async () => {
const prev = postSpy.mock.calls.length;
dispatch({ type: MessageType.HASH_VALUE, accessToken: 'a', commitedIn: 'c', label: 'L', fileBlob: { type: 'application/octet-stream', data: new Uint8Array([1]) }, messageId: 'm6' });
const [payload, targetOrigin] = (await waitForLastCall(MessageType.VALUE_HASHED, prev)) as any;
expect(targetOrigin).toBe(ORIGIN);
expect(payload.type).toBe(MessageType.VALUE_HASHED);
expect(payload.hash).toBe('hash-abc');
expect(payload.messageId).toBe('m6');
});
});