/** * bugTracker 单测 */ const fs = require('fs'); const pathResolver = require('../../services/devloop/path-resolver'); const bugTracker = require('../../services/devloop/bug-tracker'); describe('bugTracker', () => { let pid, root; beforeEach(() => { pathResolver._resetForTesting(); const r = pathResolver.allocateProjectRoot({ company_id: 'c-test', mode: 'sandbox' }); pid = r.pid; root = r.root_path; }); afterEach(() => { fs.rmSync(root, { recursive: true, force: true }); }); describe('open + dedup', () => { it('open new bug returns id', () => { const b = bugTracker.open(pid, { source: 'unit_test', unit_id: 'auth-be', test_name: 'login.invalid-pwd', severity: 'high', title: 'expected 401 got 500', }); expect(b.id).toMatch(/^bug-/); expect(b.status).toBe('open'); }); it('same unit + test → dedup returns existing id', () => { const b1 = bugTracker.open(pid, { unit_id: 'auth-be', test_name: 'login.invalid-pwd', title: 'T1', }); const b2 = bugTracker.open(pid, { unit_id: 'auth-be', test_name: 'login.invalid-pwd', title: 'T2', }); expect(b2.id).toBe(b1.id); }); }); describe('transition', () => { it('open → fixing → fixed → verified', () => { const b = bugTracker.open(pid, { unit_id: 'auth-be', test_name: 'x', title: 'T', }); bugTracker.transition(pid, b.id, { to: 'fixing', by: 'coder' }); expect(bugTracker.get(pid, b.id).status).toBe('fixing'); bugTracker.transition(pid, b.id, { to: 'fixed', by: 'tester' }); expect(bugTracker.get(pid, b.id).status).toBe('fixed'); bugTracker.transition(pid, b.id, { to: 'verified', by: 'integrator' }); expect(bugTracker.get(pid, b.id).status).toBe('verified'); }); it('open → verified (skip) throws', () => { const b = bugTracker.open(pid, { unit_id: 'x-be', test_name: 'y', title: 'T' }); expect(() => bugTracker.transition(pid, b.id, { to: 'verified', by: 'system' })) .toThrow(bugTracker.InvalidBugTransitionError); }); it('verified → open (reopen) ok', () => { const b = bugTracker.open(pid, { unit_id: 'x-be', test_name: 'y', title: 'T' }); bugTracker.transition(pid, b.id, { to: 'fixing', by: 'coder' }); bugTracker.transition(pid, b.id, { to: 'fixed', by: 'tester' }); bugTracker.transition(pid, b.id, { to: 'verified', by: 'integrator' }); bugTracker.transition(pid, b.id, { to: 'open', by: 'system' }); expect(bugTracker.get(pid, b.id).status).toBe('open'); }); }); describe('list + stats', () => { it('filter by status', () => { const b1 = bugTracker.open(pid, { unit_id: 'a-be', test_name: 't1', title: 'T1' }); const b2 = bugTracker.open(pid, { unit_id: 'b-be', test_name: 't2', title: 'T2' }); bugTracker.transition(pid, b2.id, { to: 'fixing', by: 'coder' }); const open = bugTracker.list(pid, { status: ['open'] }); const fixing = bugTracker.list(pid, { status: ['fixing'] }); expect(open.map(b => b.id)).toContain(b1.id); expect(fixing.map(b => b.id)).toContain(b2.id); }); it('stats counts by status', () => { bugTracker.open(pid, { unit_id: 'a-be', test_name: 't1', title: 'T', severity: 'high' }); bugTracker.open(pid, { unit_id: 'b-be', test_name: 't2', title: 'T', severity: 'low' }); const s = bugTracker.stats(pid); expect(s.open).toBe(2); expect(s.by_severity.high).toBe(1); expect(s.by_severity.low).toBe(1); }); }); describe('history', () => { it('tracks all transitions', () => { const b = bugTracker.open(pid, { unit_id: 'a-be', test_name: 't', title: 'T' }); bugTracker.transition(pid, b.id, { to: 'fixing', by: 'coder' }); bugTracker.transition(pid, b.id, { to: 'fixed', by: 'tester' }); const h = bugTracker.history(pid, b.id); // 含 open 时的 history (1) + 2 次 transition = 3 条 expect(h.length).toBeGreaterThanOrEqual(3); }); }); });