__tests__/practice.test.js
Rama main · réplica de lectura del repositorio original.
/*
* THE PLATFORM AND ROLE COLLECTOR - and the ways it could quietly become dangerous or useless:
* - it runs while automatic acting is on, and a "reply" task actually replies;
* - it accepts an account task because the role's own prompt is about accounts;
* - it practises a role whose platform has no login, and every run is a login wall;
* - one role takes the whole hour while the other thin roles wait for ever;
* - two walks share a signed-in profile.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import fs from 'fs';
import os from 'os';
import path from 'path';
import practice from '../src/practice.js';
import { normalizeSite } from '../src/trainScopes.js';
describe('which role to practise', () => {
const gaps = [
{ role: 'facebook-thread-reply', platform: 'facebook', examples: 14, needsRealUse: true },
{ role: 'useme.proposal', platform: 'useme', examples: 27, needsRealUse: true },
{ role: 'hn-scout', platform: 'hackernews', examples: 2, needsRealUse: false },
{ role: 'plain-scout', platform: 'web', examples: 0, needsRealUse: false },
];
it('takes the thinnest role whose platform holds a login, never the open web', () => {
const p = practice.planFor({ gaps, logins: [{ profile: 'facebook', platform: 'facebook' }, { profile: 'useme', platform: 'useme' }] });
expect(p.role).toBe('facebook-thread-reply');
expect(p.profile).toBe('facebook');
});
it('skips a platform with no signed-in profile, and rests a role for an hour after a run', () => {
const p = practice.planFor({ gaps, logins: [{ profile: 'useme', platform: 'useme' }] });
expect(p.role).toBe('useme.proposal');
const rested = practice.planFor({ gaps, logins: [{ profile: 'useme', platform: 'useme' }], perRole: { 'useme.proposal': { lastAt: new Date().toISOString() } } });
expect(rested.role).toBe(null);
expect(rested.why).toContain('within the hour');
});
it('reads the platform off a profile name, aliases included', () => {
expect(practice.platformOfProfile('facebook', normalizeSite)).toBe('facebook');
expect(practice.platformOfProfile('hn', normalizeSite)).toBe('hackernews');
expect(practice.platformOfProfile('linkdin', normalizeSite)).toBe('linkedin');
expect(practice.platformOfProfile('google-watch', normalizeSite)).toBe('google');
});
});
describe('what it refuses, in code', () => {
it('NEVER runs while automatic acting is on', () => {
expect(practice.decide({ on: true, autoAct: true, queued: 3 }).run).toBe(false);
expect(practice.decide({ on: true, autoAct: true, queued: 3 }).why).toContain('automatic acting');
});
it('one walk at a time, a cap an hour, and nothing without a task', () => {
expect(practice.decide({ on: true, busy: true, queued: 3 }).run).toBe(false);
expect(practice.decide({ on: true, live: 1, queued: 3 }).run).toBe(false);
expect(practice.decide({ on: true, queued: 3, recent: Array(6).fill(Date.now()) }).run).toBe(false);
expect(practice.decide({ on: true, queued: 0 }).run).toBe(false);
expect(practice.decide({ on: true, queued: 2 }).run).toBe(true);
expect(practice.decide({ on: false, queued: 2 }).why).toContain('off');
});
it('throws away account tasks whatever the role, and keeps outward ones for the gate', () => {
const { kept, rejected } = practice.vet([
'Open the notification about Piet\'s comment on the group post and draft one reply to him.',
'Log in to the account and change the password.',
'Use run_script to read the page.',
'Read the three newest posts in the Ecom Nederland group and record who asked for a webshop.',
]);
expect(kept.length).toBe(2);
expect(rejected.map((r) => r.why)).toEqual(['an account task — never', 'names a tool, which no person would type']);
});
it('puts the specialist and the gate rule into the brief', () => {
const m = practice.askFor({ role: { name: 'facebook-thread-reply', description: 'Continues one thread.', prompt: 'You continue ONE thread.' }, platform: 'facebook', want: 4 });
expect(m[0].content).toContain('a person approves it');
expect(m[0].content).toContain('NEVER an account task');
expect(m[1].content).toContain('facebook-thread-reply on facebook');
expect(m[1].content).toContain('Write 4 tasks');
});
});
describe('the queue takes turns', () => {
let dir;
beforeEach(() => { dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gb-practice-')); process.env.PROFILE_DIR = dir; });
afterEach(() => { delete process.env.PROFILE_DIR; fs.rmSync(dir, { recursive: true, force: true }); });
it('queues per role, hands out per role, and records the run against the role', () => {
expect(practice.push('fb-reply', 'facebook', 'facebook', ['a task one two three four', 'b task one two three four'])).toBe(2);
expect(practice.push('fb-reply', 'facebook', 'facebook', Array(10).fill('x task one two three four'))).toBe(4);
expect(practice.take('useme.proposal')).toBe(null);
const t = practice.take('fb-reply');
expect(t.prompt).toBe('a task one two three four');
practice.attachJob('j-1');
const s = practice.state();
expect(s.queued).toBe(5);
expect(s.current).toMatchObject({ role: 'fb-reply', jobId: 'j-1' });
expect(s.perRole['fb-reply'].runs).toBe(1);
expect(s.history[0].jobId).toBe('j-1');
});
it('is off until switched on, and remembers why it stopped', () => {
expect(practice.on()).toBe(false);
practice.setOn(true);
expect(practice.on()).toBe(true);
practice.stop('every key is out of allowance');
expect(practice.state()).toMatchObject({ on: false, stoppedBecause: 'every key is out of allowance' });
});
});