/**
 * PR-38 / EA-7 — HMAC signature contract.
 *
 * The webhook channel computes:
 *   X-Sentinel-Signature: sha256=<hex>
 * where the HMAC input is `${unixTimestamp}.${rawBody}` signed with
 * the channel's per-channel `signingSecret`.
 *
 * Receivers MUST be able to reproduce this exactly to verify. This
 * test locks the canonical input format. If you change either:
 *   - the join separator between timestamp and body
 *   - the algorithm
 *   - the timestamp format (unix seconds, not ms)
 * receivers will silently start rejecting our notifications. The
 * unit test prevents that.
 */
import { createHmac } from 'crypto';

function computeSignature(args: {
    secret: string;
    timestamp: string;
    rawBody: string;
}): string {
    return createHmac('sha256', args.secret)
        .update(`${args.timestamp}.${args.rawBody}`)
        .digest('hex');
}

describe('webhook HMAC signature (EA-7 / PR-38)', () => {
    const secret = 'a'.repeat(64);
    const ts = '1716530400'; // arbitrary stable seconds
    const body = JSON.stringify({ event: 'monitor_down', monitorId: 5 });

    it('signs `${timestamp}.${rawBody}` with HMAC-SHA256', () => {
        const sig = computeSignature({ secret, timestamp: ts, rawBody: body });
        expect(sig).toMatch(/^[a-f0-9]{64}$/);
    });

    it('produces a different signature for a different timestamp (replay defence)', () => {
        const a = computeSignature({ secret, timestamp: '1000', rawBody: body });
        const b = computeSignature({ secret, timestamp: '2000', rawBody: body });
        expect(a).not.toBe(b);
    });

    it('produces a different signature for a different body (tamper defence)', () => {
        const a = computeSignature({ secret, timestamp: ts, rawBody: body });
        const b = computeSignature({
            secret,
            timestamp: ts,
            rawBody: JSON.stringify({ event: 'monitor_down', monitorId: 9999 }),
        });
        expect(a).not.toBe(b);
    });

    it('produces a different signature for a different secret', () => {
        const a = computeSignature({ secret: 'aaa', timestamp: ts, rawBody: body });
        const b = computeSignature({ secret: 'bbb', timestamp: ts, rawBody: body });
        expect(a).not.toBe(b);
    });

    it('matches a receiver implementing the same recipe byte-for-byte', () => {
        const sender = computeSignature({ secret, timestamp: ts, rawBody: body });
        // Receiver-side: same library, same recipe, same inputs.
        const receiver = createHmac('sha256', secret)
            .update(`${ts}.${body}`)
            .digest('hex');
        expect(sender).toBe(receiver);
    });
});
