Files
ForgeFlow/tests/clone-target.test.mjs
T
2026-07-24 20:29:23 +02:00

92 lines
4.2 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import os from 'node:os';
import path from 'node:path';
import fs from 'node:fs/promises';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import cloneTargetModule from '../src/shared/clone-target.cjs';
import gitModule from '../src/main/git-service.cjs';
const exec = promisify(execFile);
const { cloneDirectoryName, resolveCloneTarget } = cloneTargetModule;
const { GitService } = gitModule;
test('derives a safe repository folder name from HTTPS and SSH clone URLs', () => {
assert.equal(cloneDirectoryName('https://gitea.example.test/jens/ForgeFlow.git'), 'ForgeFlow');
assert.equal(cloneDirectoryName('git@gitea.example.test:jens/my-app.git'), 'my-app');
assert.equal(cloneDirectoryName('ssh://git@gitea.example.test/jens/app.git?ref=main'), 'app');
});
test('resolves the automatic clone target inside the configured project root', () => {
const root = path.join(os.tmpdir(), 'forgeflow-projects');
const plan = resolveCloneTarget(root, 'https://gitea.example.test/jens/portfolio.git');
assert.equal(plan.root, path.resolve(root));
assert.equal(plan.target, path.join(path.resolve(root), 'portfolio'));
assert.equal(plan.directoryName, 'portfolio');
});
test('clone target inspection accepts missing and empty destinations', async (t) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'forgeflow-clone-target-'));
t.after(() => fs.rm(root, { recursive: true, force: true }));
const service = new GitService();
const remote = 'https://gitea.example.test/jens/app.git';
const missing = await service.inspectCloneTarget(remote, path.join(root, 'missing-app'));
assert.equal(missing.state, 'missing');
const emptyPath = path.join(root, 'empty-app');
await fs.mkdir(emptyPath);
const empty = await service.inspectCloneTarget(remote, emptyPath);
assert.equal(empty.state, 'empty');
});
test('clone target inspection reuses an existing checkout with the same origin', async (t) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'forgeflow-clone-reuse-'));
t.after(() => fs.rm(root, { recursive: true, force: true }));
const target = path.join(root, 'app');
await fs.mkdir(target);
await exec('git', ['init'], { cwd: target, encoding: 'utf8' });
await exec('git', ['remote', 'add', 'origin', 'git@gitea.example.test:jens/app.git'], { cwd: target, encoding: 'utf8' });
const service = new GitService();
const assessment = await service.inspectCloneTarget('https://gitea.example.test/jens/app.git', target);
assert.equal(assessment.state, 'matching-repository');
});
test('clone target inspection blocks a different repository and ordinary files', async (t) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'forgeflow-clone-conflict-'));
t.after(() => fs.rm(root, { recursive: true, force: true }));
const service = new GitService();
const otherRepository = path.join(root, 'repository');
await fs.mkdir(otherRepository);
await exec('git', ['init'], { cwd: otherRepository, encoding: 'utf8' });
await exec('git', ['remote', 'add', 'origin', 'https://gitea.example.test/jens/other.git'], { cwd: otherRepository, encoding: 'utf8' });
await assert.rejects(
service.inspectCloneTarget('https://gitea.example.test/jens/app.git', otherRepository),
(error) => error.code === 'CLONE_TARGET_DIFFERENT_REPOSITORY'
);
const ordinaryFolder = path.join(root, 'ordinary');
await fs.mkdir(ordinaryFolder);
await fs.writeFile(path.join(ordinaryFolder, 'notes.txt'), 'do not overwrite\n');
await assert.rejects(
service.inspectCloneTarget('https://gitea.example.test/jens/app.git', ordinaryFolder),
(error) => error.code === 'CLONE_TARGET_NOT_EMPTY'
);
});
test('clone target inspection blocks a file at the automatic destination', async (t) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'forgeflow-clone-file-'));
t.after(() => fs.rm(root, { recursive: true, force: true }));
const target = path.join(root, 'app');
await fs.writeFile(target, 'not a directory');
const service = new GitService();
await assert.rejects(
service.inspectCloneTarget('https://gitea.example.test/jens/app.git', target),
(error) => error.code === 'CLONE_TARGET_NOT_DIRECTORY'
);
});