perf: streamline repository and deployment awareness
ForgeFlow quality gate / quality (push) Canceled after 0s

This commit is contained in:
NuklearRabbit
2026-08-12 15:26:53 +02:00
parent 38e221cbd1
commit 32ed4fcb5e
19 changed files with 351 additions and 73 deletions
+19
View File
@@ -172,3 +172,22 @@ test('rollback refuses a stale target that is no longer the server-reported prev
assert.equal(dispatched, false);
});
test('active deployment refreshes run concurrently with a bounded worker pool', async () => {
const operations = Array.from({ length: 9 }, (_, index) => ({ id: `operation-${index}`, type: 'deployment', status: 'running' }));
const service = new DeploymentService({ data: { operations } }, {}, {});
let running = 0;
let peak = 0;
service.refreshOperation = async (id) => {
running += 1;
peak = Math.max(peak, running);
await new Promise((resolve) => setTimeout(resolve, 10));
running -= 1;
return { id };
};
const refreshed = await service.refreshActiveOperations();
assert.equal(refreshed.length, operations.length);
assert.ok(peak > 1);
assert.ok(peak <= 4);
});
+21 -1
View File
@@ -163,7 +163,7 @@ test('refresh uses last-known Gitea repositories after a transient remote failur
const fresh = await instance.refresh();
remoteAvailable = false;
const degraded = await instance.refresh();
const degraded = await instance.refresh({ force: true });
assert.equal(fresh[0].remoteStale, false);
assert.equal(degraded[0].fullName, remote.full_name);
@@ -184,6 +184,26 @@ test('initial Gitea failure remains visible when no safe cache exists', async ()
await assert.rejects(() => instance.refresh(), /Gitea unavailable/);
});
test('refresh coalesces concurrent work and briefly reuses remote and discovery results', async () => {
let remoteCalls = 0;
let discoveryCalls = 0;
const store = {
data: { gitea: { baseUrl: 'https://gitea.example' }, workspaceRoots: [], repositoryMappings: {}, preferences: { preferredCloneProtocol: 'ssh' }, favorites: [] },
getToken: () => 'token', getDeploymentProfiles: () => [], getDeploymentState: () => null
};
const instance = new RepositoryService(store, {}, { listRepositories: async () => { remoteCalls += 1; await new Promise((resolve) => setTimeout(resolve, 10)); return [remote]; } });
instance.discoverAll = async () => { discoveryCalls += 1; return []; };
const [first, second] = await Promise.all([instance.refresh(), instance.refresh()]);
assert.deepEqual(first, second);
await instance.refresh();
assert.equal(remoteCalls, 1);
assert.equal(discoveryCalls, 1);
await instance.refresh({ force: true });
assert.equal(remoteCalls, 2);
assert.equal(discoveryCalls, 2);
});
test('decoration reports conflicts, behind branches, errors and remote-only repositories', () => {
const instance = service();
const conflicted = instance.decorate(remote, { localPath: 'repo', status: { ...status(), counts: { changed: 1, conflicts: 2 }, branch: { ...status().branch, behind: 3 } } }, []);
+47
View File
@@ -317,6 +317,7 @@ test("low-level inventory scan is read-only and user discovery auto-links exact
assert.equal(profiles[0].containerName, "Portfolio");
assert.equal(profiles[0].adoptedFromServer, true);
assert.equal(states.get(profiles[0].id).matchesGitea, true);
assert.deepEqual(discovery.refreshedProfileIds, [profiles[0].id]);
});
@@ -385,6 +386,18 @@ test("server inventory includes stopped DockerMan containers without Git and kee
assert.match(workloads[0].candidates[0].reasons.join(" "), /manual confirmation/i);
});
test("server inventory batches Docker inspect and retains a disappearing-container fallback", async () => {
const source = await unraidSource();
assert.match(source, /docker inspect "\\\$\{container_ids\[@\]\}"/);
assert.match(source, /for container_id in "\\\$\{container_ids\[@\]\}"/);
});
test("server inventory avoids a second Compose process for static image definitions", async () => {
const source = await unraidSource();
assert.match(source, /has_override=false/);
assert.match(source, /\[ -z "\$images" \].*config --images/);
});
test("server inventory groups multi-service Compose projects and preserves their identity", () => {
const baseContainer = {
image: "example/app:latest",
@@ -477,6 +490,40 @@ test("manual workload linking does not claim Gitea parity for unrelated provenan
assert.equal(savedState.giteaSha, null);
});
test("inventory provenance alone never claims Gitea commit parity", async () => {
let savedState = null;
const service = new UnraidDeploymentService({
store: { getDeploymentState: () => ({}), saveDeploymentState: async (_id, state) => { savedState = state; return state; } },
});
await service.saveWorkloadState(
{ id: "profile", remoteFolder: "app", cloneUrl: "git@gitea.test:Owner/App.git" },
{
workloadId: "workload", observedAt: new Date().toISOString(),
metadata: { sourceRepository: "git@gitea.test:Owner/App.git", liveRevision: "a".repeat(40) },
runtime: { running: true, health: "healthy" }, containers: [{ name: "app", running: true, health: "healthy" }], compose: {},
},
{ basePath: "/mnt/user/appdata" },
);
assert.equal(savedState.matchesGitea, false);
assert.equal(savedState.giteaSha, null);
});
test("a stopped workload cannot become healthy through a reused healthcheck port", async () => {
let savedState = null;
const service = new UnraidDeploymentService({
store: { getDeploymentState: () => ({}), saveDeploymentState: async (_id, state) => { savedState = state; return state; } },
});
await service.saveWorkloadState(
{ id: "profile", remoteFolder: "app", healthcheckUrl: "http://server.test/health" },
{ workloadId: "workload", metadata: {}, runtime: { running: false, health: "unverified" }, containers: [{ name: "app", running: false }], compose: {} },
{ basePath: "/mnt/user/appdata" },
{ health: { configured: true, healthy: true, status: 200 } },
);
assert.equal(savedState.containerRunning, false);
assert.equal(savedState.healthy, false);
assert.equal(savedState.runtimeVerification, "stopped");
});
test("Docker ignore checks identify exact runtime and Git context exclusions", () => {
const rules = "# build context\n.git\ndata/\nlogs/**\n!logs/keep.txt\n";
assert.equal(dockerIgnoreHasPath(rules, ".git"), true);