Ctrl K
ComunidadGuías…

__tests__/recorder.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * recorder — the RECORD half, wired to a session's traffic. The properties that matter: it records
 * NOTHING until armed (an ordinary run learns no cards), it keeps only learnable writes on the armed
 * origin, it caps memory keeping the RECENT requests (the act is last), and a discard learns nothing
 * from a broken walk. Pure — the "requests" are plain objects, no browser.
 */
import { describe, it, expect } from 'vitest';
import { makeRecorder } from '../src/recorder.js';

const silent = { info: () => {}, warn: () => {} };
const post = (url, over = {}) => ({ method: 'POST', url, headers: { 'x-fb-lsd': 't' }, postData: '{"a":1}', ...over });

describe('records only while armed', () => {
  it('an unarmed recorder ignores everything', () => {
    const r = makeRecorder({ log: silent });
    r.observe(post('https://www.facebook.com/api/x'));
    expect(r.armed).toBe(false);
    expect(r.finish({ now: 1 })).toBeNull();
  });

  it('armed, it keeps learnable writes on its origin and distils the last as the act', () => {
    const r = makeRecorder({ log: silent });
    r.arm({ intent: 'facebook.page.post', origin: 'https://www.facebook.com' });
    r.observe(post('https://www.facebook.com/ajax/prefetch'));
    r.observe(post('https://www.google-analytics.com/collect', { headers: {} }));   // not learnable
    r.observe(post('https://x.com/other', { }));                                     // wrong origin
    r.observe(post('https://www.facebook.com/api/graphql/', { postData: '{"message":"hi"}' }));
    const out = r.finish({ now: 5 });
    expect(out.ok).toBe(true);
    expect(out.card.url).toMatch(/graphql/);
    expect(out.card.intent).toBe('facebook.page.post');
    expect(r.armed).toBe(false);   // finishing disarms
  });
});

describe('bounded and honest', () => {
  it('caps the buffer keeping the recent requests', () => {
    const r = makeRecorder({ log: silent });
    r.arm({ intent: 'i', origin: 'https://www.facebook.com' });
    for (let n = 0; n < 400; n++) r.observe(post(`https://www.facebook.com/api/${n}`));
    const out = r.finish({ now: 1 });
    // the ACT (last request) survived the cap
    expect(out.card.url).toMatch(/\/api\/399$/);
  });

  it('a walk with no learnable request distils to null, not a broken card', () => {
    const r = makeRecorder({ log: silent });
    r.arm({ intent: 'i', origin: 'https://www.facebook.com' });
    r.observe({ method: 'GET', url: 'https://www.facebook.com/feed' });
    expect(r.finish({ now: 1 })).toBeNull();
  });

  it('discard learns nothing — a failed walk must not teach a card', () => {
    const r = makeRecorder({ log: silent });
    r.arm({ intent: 'i', origin: 'https://www.facebook.com' });
    r.observe(post('https://www.facebook.com/api/graphql/'));
    r.discard();
    expect(r.armed).toBe(false);
    expect(r.finish({ now: 1 })).toBeNull();
  });

  it('a second arm replaces the first — the walk changed', () => {
    const r = makeRecorder({ log: silent });
    r.arm({ intent: 'first', origin: 'https://a.com' });
    r.observe(post('https://a.com/api/x'));
    r.arm({ intent: 'second', origin: 'https://b.com' });
    expect(r.intent).toBe('second');
    r.observe(post('https://b.com/api/y'));
    const out = r.finish({ now: 1 });
    expect(out.card.intent).toBe('second');
    expect(out.card.url).toMatch(/b\.com/);
  });
});

describe('sealing the act — a setup walk creates, THEN navigates', () => {
  it('the card is the write at the seal, not the last navigation write', () => {
    // A Facebook page setup: fill fields (a validation write), CREATE (the act), then the walk
    // navigates on and fires more writes to find the new page's URL. The seal is dropped at the
    // create; the card must be the create, never the trailing navigation.
    const r = makeRecorder({ log: silent });
    r.arm({ intent: 'facebook.page.setup', origin: 'https://www.facebook.com' });
    r.observe(post('https://www.facebook.com/api/graphql/', { postData: '{"validate":"name"}' })); // 0
    r.seal();                                                                                       // act lands next
    r.observe(post('https://www.facebook.com/api/graphql/', { postData: '{"createPage":"PrintOps"}' })); // 1 = the act
    r.observe(post('https://www.facebook.com/api/graphql/', { postData: '{"loadFeed":1}' }));       // 2 navigation
    r.observe(post('https://www.facebook.com/api/graphql/', { postData: '{"search":"PrintOps"}' })); // 3 navigation
    expect(r.sealed).toBe(true);
    const out = r.finish({ now: 1 });
    expect(out.ok).toBe(true);
    expect(out.card.slots).toEqual(['createPage']);        // the CREATE, not loadFeed/search/validate
  });

  it('sealed is false until seal() is called, and reset by a fresh arm', () => {
    const r = makeRecorder({ log: silent });
    r.arm({ intent: 'i', origin: 'https://x.com' });
    expect(r.sealed).toBe(false);
    r.observe(post('https://x.com/api/a'));
    r.seal();
    expect(r.sealed).toBe(true);
    r.arm({ intent: 'i2', origin: 'https://x.com' });      // a new walk starts unsealed
    expect(r.sealed).toBe(false);
  });

  it('a cap-drop shifts the seal so it still points at the act', () => {
    const r = makeRecorder({ log: silent });
    r.arm({ intent: 'i', origin: 'https://x.com' });
    for (let n = 0; n < 300; n++) r.observe(post(`https://x.com/api/pre${n}`)); // fill to the cap
    r.seal();                                                                    // act lands next
    r.observe(post('https://x.com/api/THE-ACT', { postData: '{"act":1}' }));      // pushes one off the front → seal shifts down
    const out = r.finish({ now: 1 });
    expect(out.card.url).toMatch(/THE-ACT$/);
  });
});