scheduler.test.js 5.94 KB
import { describe, it, expect } from 'vitest';
const { Scheduler } = require('../../core/scheduler');

describe('Scheduler', () => {
  describe('getExecutionGroups', () => {
    it('should return single group when no dependencies', () => {
      const agents = {
        coder: { type: 'executor' },
        reviewer: { type: 'executor' }
      };
      const calls = [
        { agent: 'coder', task: 'task1' },
        { agent: 'reviewer', task: 'task2' }
      ];

      const groups = Scheduler.getExecutionGroups(agents, calls);

      expect(groups).toHaveLength(1);
      expect(groups[0]).toContain('coder');
      expect(groups[0]).toContain('reviewer');
    });

    it('should separate dependent agents into different groups', () => {
      const agents = {
        coder: { type: 'executor' },
        verifier: { type: 'validator', dependsOn: ['coder'] }
      };
      const calls = [
        { agent: 'coder', task: 'task1' },
        { agent: 'verifier', task: 'task2' }
      ];

      const groups = Scheduler.getExecutionGroups(agents, calls);

      expect(groups).toHaveLength(2);
      expect(groups[0]).toEqual(['coder']);
      expect(groups[1]).toEqual(['verifier']);
    });

    it('should group agents with same dependencies together', () => {
      const agents = {
        coder: { type: 'executor' },
        verifier: { type: 'validator', dependsOn: ['coder'] },
        tester: { type: 'validator', dependsOn: ['coder'] }
      };
      const calls = [
        { agent: 'coder', task: 'task1' },
        { agent: 'verifier', task: 'task2' },
        { agent: 'tester', task: 'task3' }
      ];

      const groups = Scheduler.getExecutionGroups(agents, calls);

      expect(groups).toHaveLength(2);
      expect(groups[0]).toEqual(['coder']);
      expect(groups[1]).toHaveLength(2);
      expect(groups[1]).toContain('verifier');
      expect(groups[1]).toContain('tester');
    });

    it('should handle chain dependencies', () => {
      const agents = {
        a: { type: 'executor' },
        b: { type: 'executor', dependsOn: ['a'] },
        c: { type: 'executor', dependsOn: ['b'] }
      };
      const calls = [
        { agent: 'a', task: 't1' },
        { agent: 'b', task: 't2' },
        { agent: 'c', task: 't3' }
      ];

      const groups = Scheduler.getExecutionGroups(agents, calls);

      expect(groups).toHaveLength(3);
      expect(groups[0]).toEqual(['a']);
      expect(groups[1]).toEqual(['b']);
      expect(groups[2]).toEqual(['c']);
    });

    it('should throw on circular dependency', () => {
      const agents = {
        a: { type: 'executor', dependsOn: ['b'] },
        b: { type: 'executor', dependsOn: ['a'] }
      };
      const calls = [
        { agent: 'a', task: 't1' },
        { agent: 'b', task: 't2' }
      ];

      expect(() => Scheduler.getExecutionGroups(agents, calls))
        .toThrow('Circular dependency');
    });

    it('should return empty array when no calls', () => {
      const agents = { coder: { type: 'executor' } };
      const calls = [];

      const groups = Scheduler.getExecutionGroups(agents, calls);

      expect(groups).toEqual([]);
    });

    it('should only consider called agents dependencies', () => {
      const agents = {
        coder: { type: 'executor' },
        verifier: { type: 'validator', dependsOn: ['coder'] },
        tester: { type: 'validator', dependsOn: ['coder'] }
      };
      // 只调用 verifier,不调用 coder
      const calls = [
        { agent: 'verifier', task: 'task' }
      ];

      const groups = Scheduler.getExecutionGroups(agents, calls);

      // verifier 依赖 coder,但 coder 不在调用列表中,应该直接执行
      expect(groups).toHaveLength(1);
      expect(groups[0]).toEqual(['verifier']);
    });

    it('should handle multiple dependencies', () => {
      const agents = {
        a: { type: 'executor' },
        b: { type: 'executor' },
        c: { type: 'executor', dependsOn: ['a', 'b'] }
      };
      const calls = [
        { agent: 'a', task: 't1' },
        { agent: 'b', task: 't2' },
        { agent: 'c', task: 't3' }
      ];

      const groups = Scheduler.getExecutionGroups(agents, calls);

      expect(groups).toHaveLength(2);
      expect(groups[0]).toContain('a');
      expect(groups[0]).toContain('b');
      expect(groups[1]).toEqual(['c']);
    });
  });

  describe('isComplete', () => {
    it('should return true when all validators pass and executors done', () => {
      const agents = {
        coder: { type: 'executor' },
        verifier: { type: 'validator' }
      };
      const results = [
        { agent: 'coder', status: 'DONE' },
        { agent: 'verifier', status: 'PASS' }
      ];

      expect(Scheduler.isComplete(agents, results)).toBe(true);
    });

    it('should return false when any validator fails', () => {
      const agents = {
        coder: { type: 'executor' },
        verifier: { type: 'validator' }
      };
      const results = [
        { agent: 'coder', status: 'DONE' },
        { agent: 'verifier', status: 'FAIL' }
      ];

      expect(Scheduler.isComplete(agents, results)).toBe(false);
    });

    it('should return false when any executor errors', () => {
      const agents = {
        coder: { type: 'executor' }
      };
      const results = [
        { agent: 'coder', status: 'ERROR' }
      ];

      expect(Scheduler.isComplete(agents, results)).toBe(false);
    });

    it('should return true with empty results', () => {
      const agents = {};
      const results = [];

      expect(Scheduler.isComplete(agents, results)).toBe(true);
    });

    it('should handle mixed results correctly', () => {
      const agents = {
        coder: { type: 'executor' },
        verifier: { type: 'validator' },
        tester: { type: 'validator' }
      };
      const results = [
        { agent: 'coder', status: 'DONE' },
        { agent: 'verifier', status: 'PASS' },
        { agent: 'tester', status: 'FAIL' }
      ];

      expect(Scheduler.isComplete(agents, results)).toBe(false);
    });
  });
});