sandbox-and-errors.test.js 2.5 KB
/**
 * _sandbox + _errors 单测
 */

const path = require('path');
const {
  ToolValidationError,
  ToolSandboxError,
  ToolMissingDependencyError,
} = require('../../../src/tools/devloop/_errors');
const sandbox = require('../../../src/tools/devloop/_sandbox');

describe('errors', () => {
  it('ToolValidationError carries field + reason', () => {
    const e = new ToolValidationError('html', 'too short');
    expect(e.name).toBe('ToolValidationError');
    expect(e.field).toBe('html');
    expect(e.reason).toBe('too short');
    expect(e.message).toContain('too short');
  });

  it('ToolSandboxError reports agent + path', () => {
    const e = new ToolSandboxError('/etc/passwd', 'coder');
    expect(e.name).toBe('ToolSandboxError');
    expect(e.path).toBe('/etc/passwd');
    expect(e.agent).toBe('coder');
  });

  it('ToolMissingDependencyError names missing dep', () => {
    const e = new ToolMissingDependencyError('ctx.pid');
    expect(e.message).toMatch(/ctx\.pid/);
  });
});

describe('_sandbox', () => {
  const ctx = {
    agent: 'coder',
    projectDir: '/tmp/my-proj',
    unit_id: 'u-be-auth',
  };

  it('matches exact file pattern', () => {
    expect(sandbox.matchesPattern(
      '/tmp/my-proj/timeline.ndjson', 'timeline.ndjson', ctx
    )).toBe(true);
  });

  it('matches /** glob prefix', () => {
    expect(sandbox.matchesPattern(
      '/tmp/my-proj/prd/feature/login.html', 'prd/**', ctx
    )).toBe(true);
  });

  it('matches /** root itself', () => {
    expect(sandbox.matchesPattern(
      '/tmp/my-proj/prd', 'prd/**', ctx
    )).toBe(true);
  });

  it('substitutes {unitId} placeholder', () => {
    expect(sandbox.matchesPattern(
      '/tmp/my-proj/units/u-be-auth/code/server.js',
      'units/{unitId}/code/**',
      ctx
    )).toBe(true);
  });

  it('rejects paths outside projectDir', () => {
    expect(sandbox.matchesPattern(
      '/etc/passwd', 'prd/**', ctx
    )).toBe(false);
  });

  it('isWithinAllowList allows when any pattern matches', () => {
    expect(sandbox.isWithinAllowList(
      '/tmp/my-proj/units/u-be-auth/contract.json',
      ['prd/**', 'units/{unitId}/contract.json'],
      ctx
    )).toBe(true);
  });

  it('assertWritable throws ToolSandboxError on violation', () => {
    expect(() => sandbox.assertWritable(
      '/etc/passwd', ['prd/**'], ctx
    )).toThrow(ToolSandboxError);
  });

  it('assertWritable is silent on allowed path', () => {
    expect(() => sandbox.assertWritable(
      '/tmp/my-proj/prd/index.html', ['prd/**'], ctx
    )).not.toThrow();
  });
});