Ctrl K
ComunidadGuías…

__tests__/profile-role.test.js

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

Archivo original ↗Preguntar a KiBot
/*
 * WHICH ROLE A PROFILE USES — and why that answer has to carry its source.
 *
 * The pairing used to be worked out by comparing NAMES. It held until someone renamed either side,
 * or named a profile the way a person actually would, and then it was gone with no error: the agent
 * became a generalist with no playbook. It happened twice to the same CapCut walk, which then spent
 * seventy steps working out a video editor from scratch, and "why did it run as general?" was
 * unanswerable both times.
 *
 * So each case below is a state that produced, or would produce, a silent generalist.
 */
import { describe, it, expect } from 'vitest';
import { siteKey, roleChoice } from '../src/profileRole.js';

/* Stand-ins for the two stores, so this needs no volume and no browser. */
const store = (byName) => ({
  read: (n) => byName[n] || {},
});
const roleStore = (rows) => ({
  list: () => rows,
  get: (id) => rows.find((r) => r.name === id || r.id === id) || null,
  canonical: (id) => String(id || '').toLowerCase(),
});

const CAPCUT = { name: 'capcut-video-editor', site: 'capcut.com' };
const LINKEDIN = { name: 'linkedin-reply-desk', site: 'linkedin.com' };
const ROLES = roleStore([CAPCUT, LINKEDIN]);

describe('siteKey folds the names a profile and a role are spelled with', () => {
  it.each([
    ['p_capcut', 'capcut'],
    ['capcut.com', 'capcut'],
    ['www.capcut.com', 'capcut'],
    ['CapCut.COM', 'capcut'],
    ['p_linked-in', 'linkedin'],
  ])('%s → %s', (input, want) => expect(siteKey(input)).toBe(want));

  it('strips www BEFORE splitting — otherwise every www site keys as "www"', () => {
    expect(siteKey('www.capcut.com')).not.toBe('www');
    expect(siteKey('www.linkedin.com')).not.toBe(siteKey('www.capcut.com'));
  });

  it('is empty for nothing, so an unnamed profile matches nothing rather than everything', () => {
    for (const v of ['', null, undefined, '...', '///']) expect(siteKey(v)).toBe('');
  });
});

describe('the profile chooses its role', () => {
  const deps = (byName) => ({ profiles: store(byName), roles: ROLES });

  it('a stored choice wins, even when the names match nothing', () => {
    const c = roleChoice('work-video', deps({ 'work-video': { defaultRole: 'capcut-video-editor' } }));
    expect(c).toMatchObject({ role: 'capcut-video-editor', source: 'chosen' });
  });

  it('a stored choice wins over a site that points somewhere else', () => {
    const c = roleChoice('p_linkedin', deps({ p_linkedin: { site: 'linkedin.com', defaultRole: 'capcut-video-editor' } }));
    expect(c.role).toBe('capcut-video-editor');
    expect(c.source).toBe('chosen');
  });

  it('with no choice stored, the site match still applies — an unconfigured profile is not a generalist', () => {
    const c = roleChoice('p_capcut', deps({}));
    expect(c).toMatchObject({ role: 'capcut-video-editor', source: 'site', suggested: 'capcut-video-editor' });
  });

  it('matches on the declared site before the name, so "work-video" with site capcut.com works', () => {
    const c = roleChoice('work-video', deps({ 'work-video': { site: 'capcut.com' } }));
    expect(c).toMatchObject({ role: 'capcut-video-editor', source: 'site' });
  });

  it('falls back to general and SAYS so, rather than defaulting in silence', () => {
    const c = roleChoice('scratchpad', deps({}));
    expect(c).toEqual({ role: 'general', source: 'none' });
  });

  it('a stored role that no longer exists is reported, not silently ignored', () => {
    /* The configuration rotted: the profile looks configured, reports a role, and behaves as a
       generalist. That is the one state worth shouting about. */
    const c = roleChoice('scratchpad', deps({ scratchpad: { defaultRole: 'capcut-editor-v2' } }));
    expect(c).toMatchObject({ role: 'general', source: 'none', missing: 'capcut-editor-v2' });
  });

  it('a rotted choice still names what was meant when the site can cover for it', () => {
    const c = roleChoice('p_capcut', deps({ p_capcut: { defaultRole: 'capcut-editor-v2' } }));
    expect(c).toMatchObject({ role: 'capcut-video-editor', source: 'site', missing: 'capcut-editor-v2' });
  });

  it('a profile that cannot be read at all answers general instead of throwing', () => {
    const c = roleChoice('p', { profiles: { read: () => { throw new Error('volume gone'); } }, roles: ROLES });
    expect(c.role).toBe('general');
  });

  it('refuses to guess without its stores, rather than quietly answering general', () => {
    expect(() => roleChoice('p_capcut')).toThrow(/needs/);
  });
});