- POST /api/v1/projects : crée <root>/<name> (mkdir non récursif, 409 si existant), git init optionnel, puis lance une session dedans. Anti-traversal sur le nom (core/project.ts). - Web : NewProjectModal (racine préremplie scanRoots[0], case git init) + bouton dans SessionsListView. - SessionContextBar : badge du groupe (cliquable) + repos couverts avec leur branche dans l'en-tête de session ; session simple = repo+branche déduits du cwd. - 14 tests (unitaires + route).
46 lines
1.7 KiB
TypeScript
46 lines
1.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { isSafeProjectName, resolveProjectDir } from '../src/core/project.js';
|
|
|
|
describe('isSafeProjectName', () => {
|
|
it('accepte un segment simple', () => {
|
|
expect(isSafeProjectName('mon-projet')).toBe(true);
|
|
expect(isSafeProjectName('Projet_42.v2')).toBe(true);
|
|
expect(isSafeProjectName(' espaces-trim ')).toBe(true); // trim interne
|
|
});
|
|
|
|
it('refuse vide / espaces seuls', () => {
|
|
expect(isSafeProjectName('')).toBe(false);
|
|
expect(isSafeProjectName(' ')).toBe(false);
|
|
});
|
|
|
|
it('refuse `.` et `..`', () => {
|
|
expect(isSafeProjectName('.')).toBe(false);
|
|
expect(isSafeProjectName('..')).toBe(false);
|
|
});
|
|
|
|
it('refuse les séparateurs et le NUL (anti-traversal)', () => {
|
|
expect(isSafeProjectName('a/b')).toBe(false);
|
|
expect(isSafeProjectName('../evil')).toBe(false);
|
|
expect(isSafeProjectName('a\\b')).toBe(false);
|
|
expect(isSafeProjectName('a\0b')).toBe(false);
|
|
});
|
|
|
|
it('refuse un nom trop long (> 255)', () => {
|
|
expect(isSafeProjectName('x'.repeat(256))).toBe(false);
|
|
expect(isSafeProjectName('x'.repeat(255))).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('resolveProjectDir', () => {
|
|
it('joint root + name (trim) en chemin absolu', () => {
|
|
expect(resolveProjectDir('/home/johan/dev', 'mon-projet')).toBe('/home/johan/dev/mon-projet');
|
|
expect(resolveProjectDir('/home/johan/dev', ' mon-projet ')).toBe('/home/johan/dev/mon-projet');
|
|
});
|
|
|
|
it('lève (400) sur un nom invalide', () => {
|
|
expect(() => resolveProjectDir('/home/johan/dev', '..')).toThrow();
|
|
expect(() => resolveProjectDir('/home/johan/dev', 'a/b')).toThrow();
|
|
expect(() => resolveProjectDir('/home/johan/dev', '')).toThrow();
|
|
});
|
|
});
|