67 lines
3.4 KiB
TypeScript
67 lines
3.4 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { BadRequestException, ForbiddenException } from '@nestjs/common';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { resolve } from 'node:path';
|
|
import { GoalsService } from '../src/goals/goals.service.js';
|
|
|
|
const admin = { actor: 'goal-admin', role: 'org-admin', project: 'default', tenant: 'goal-test' };
|
|
const root = resolve(import.meta.dirname, '../../../..');
|
|
const registry = resolve(root, 'packages/casan-harness/config/project-registry.json');
|
|
|
|
test('goal project selector exposes only active allowlisted registry entries', () => {
|
|
const result = new GoalsService().projects(admin);
|
|
assert.ok(result.projects.some((project) => project.project_id === 'AINative_OKR_CASAN4'));
|
|
assert.ok(result.projects.every((project) => project.context_roots.every((contextRoot) => !contextRoot.startsWith('/'))));
|
|
assert.ok(result.projects.every((project) => project.project_id !== 'CASAN_DEMO_PROJECT_A'));
|
|
});
|
|
|
|
test('goal start rejects project ids outside the server registry before model routing', async () => {
|
|
await assert.rejects(
|
|
new GoalsService().start({ goal: 'Review the current application architecture safely', projectId: '../../tmp' }, admin),
|
|
BadRequestException,
|
|
);
|
|
});
|
|
|
|
test('goal project creation is restricted to organization administrators', () => {
|
|
assert.throws(
|
|
() => new GoalsService().createProject({ projectId: 'denied-project', domain: 'Denied Project' }, { ...admin, role: 'viewer' }),
|
|
ForbiddenException,
|
|
);
|
|
});
|
|
|
|
test('goal project creation rejects unsafe template values before touching the workspace', () => {
|
|
assert.throws(
|
|
() => new GoalsService().createProject({ projectId: 'unsafe-shell', domain: 'Broken "Template"' }, admin),
|
|
BadRequestException,
|
|
);
|
|
});
|
|
|
|
test('goal project creation builds a complete isolated shell and central adapter manifest', () => {
|
|
const projectId = `goal-shell-${process.pid}`;
|
|
const projectRoot = resolve(root, 'apps/projects', projectId);
|
|
const registryBefore = readFileSync(registry, 'utf8');
|
|
try {
|
|
const created = new GoalsService().createProject({ projectId, domain: 'Goal Shell Verification' }, admin);
|
|
assert.equal(created.project_id, projectId);
|
|
assert.equal(created.shell_root, `apps/projects/${projectId}`);
|
|
assert.equal(created.manifest, `apps/projects/${projectId}/casan.workspace.manifest.json`);
|
|
assert.ok(existsSync(resolve(projectRoot, '.github/workflows/ci.yml')));
|
|
assert.ok(existsSync(resolve(projectRoot, `apps/${projectId}/domain/project.manifest.json`)));
|
|
assert.ok(existsSync(resolve(projectRoot, 'packages/casan-harness/scripts/bash/project-gate.sh')));
|
|
assert.ok(existsSync(resolve(projectRoot, 'casan.workspace.manifest.json')));
|
|
|
|
const validation = execFileSync('python3', [
|
|
resolve(root, 'packages/casan-harness/scripts/bash/project_manifest.py'),
|
|
'validate', '--root', root, '--manifest', created.manifest!,
|
|
], { encoding: 'utf8' });
|
|
assert.match(validation, /"status": "valid"/);
|
|
assert.ok(new GoalsService().projects(admin).projects.some((project) => project.project_id === projectId && project.shell_root === created.shell_root));
|
|
} finally {
|
|
writeFileSync(registry, registryBefore, 'utf8');
|
|
rmSync(projectRoot, { recursive: true, force: true });
|
|
rmSync(`${registry}.lock`, { force: true });
|
|
}
|
|
});
|