/** * tester-tools 单测 */ const fs = require('fs'); const path = require('path'); const os = require('os'); const TEST_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'devloop-tt-')); process.env.HOME = TEST_HOME; const pathResolver = require('../../../services/devloop/path-resolver'); const tools = require('../../../src/tools/devloop/tester-tools'); function setupProject() { pathResolver._resetForTesting(); const root = fs.mkdtempSync(path.join(os.tmpdir(), 'tt-proj-')); const pid = 'p-testertest'; const reg = { [pid]: { root_path: root, mode: 'linked-new', company_id: 'c-test' }, }; fs.mkdirSync(path.join(TEST_HOME, '.devloop'), { recursive: true }); fs.writeFileSync( path.join(TEST_HOME, '.devloop', 'linked-projects.json'), JSON.stringify(reg) ); pathResolver._resetForTesting(); return { pid, root }; } describe('tester-tools', () => { let env, ctx; beforeEach(() => { env = setupProject(); ctx = { pid: env.pid, agent: 'tester' }; }); afterEach(() => { fs.rmSync(env.root, { recursive: true, force: true }); }); describe('hint_builder', () => { it('hints on TypeError compare (bcrypt-like)', async () => { const r = await tools.hint_builder.execute({ stack: 'TypeError: Illegal arguments: undefined,string compare', }, ctx); expect(r.hint).toMatch(/bcrypt\.compare/); }); it('hints on 401 expected vs 500 got', async () => { const r = await tools.hint_builder.execute({ expected: 401, got: 500, stack: '', }, ctx); expect(r.hint).toMatch(/401/); }); it('parses status code from stack', async () => { const r = await tools.hint_builder.execute({ stack: 'Expected: 200\nReceived: 404', }, ctx); expect(r.hint).toMatch(/200/); }); }); describe('parse_vitest', () => { it('returns crashed when raw json missing', async () => { const r = await tools.parse_vitest.execute({ raw_json_path: path.join(env.root, 'nope.json'), }, ctx); expect(r.ok).toBe(false); expect(r.crashed).toBe(true); }); it('parses passing vitest output', async () => { const rawPath = path.join(env.root, 'raw.json'); fs.writeFileSync(rawPath, JSON.stringify({ testResults: [{ assertionResults: [ { status: 'passed', fullName: 'a' }, { status: 'passed', fullName: 'b' }, ], }], })); const r = await tools.parse_vitest.execute({ raw_json_path: rawPath }, ctx); expect(r.ok).toBe(true); expect(r.summary.passed).toBe(2); expect(r.failures).toHaveLength(0); }); it('extracts failure with hint', async () => { const rawPath = path.join(env.root, 'raw2.json'); fs.writeFileSync(rawPath, JSON.stringify({ testResults: [{ assertionResults: [{ status: 'failed', fullName: 'login returns 401', failureMessages: ['Expected: 401\nReceived: 500\nat handler'], }], }], })); const r = await tools.parse_vitest.execute({ raw_json_path: rawPath }, ctx); expect(r.ok).toBe(false); expect(r.summary.failed).toBe(1); expect(r.failures[0].hint).toMatch(/401/); }); it('writes result.json when result_out_path given', async () => { const rawPath = path.join(env.root, 'raw3.json'); fs.writeFileSync(rawPath, JSON.stringify({ testResults: [{ assertionResults: [{ status: 'passed', fullName: 't' }] }], })); const out = path.join(env.root, 'result.json'); await tools.parse_vitest.execute({ raw_json_path: rawPath, result_out_path: out, }, ctx); expect(fs.existsSync(out)).toBe(true); }); }); describe('bug_open / bug_list / bug_transition', () => { it('opens a bug then lists it', async () => { const r = await tools.bug_open.execute({ title: 'login 500 instead of 401', unit_id: 'u-be-auth', test_name: 'login wrong password', severity: 'high', }, ctx); expect(r.ok).toBe(true); expect(r.bug_id).toBeTruthy(); const ls = await tools.bug_list.execute({ unit_id: 'u-be-auth' }, ctx); expect(ls.total).toBeGreaterThanOrEqual(1); }); it('transitions bug open → fixing', async () => { const opened = await tools.bug_open.execute({ title: 'x', unit_id: 'u-a', test_name: 't', }, ctx); const r = await tools.bug_transition.execute({ bug_id: opened.bug_id, to: 'fixing', note: 'coder picked up', }, ctx); expect(r.ok).toBe(true); }); it('rejects invalid bug transition', async () => { const opened = await tools.bug_open.execute({ title: 'y', unit_id: 'u-b', test_name: 't', }, ctx); await expect(tools.bug_transition.execute({ bug_id: opened.bug_id, to: 'verified', }, ctx)).rejects.toThrow(); }); }); });