fix: harden repository refresh and server pull
ForgeFlow quality gate / quality (push) Canceled after 0s
ForgeFlow quality gate / quality (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import policyModule from '../src/main/process-error-policy.cjs';
|
||||
|
||||
const { installOutputPipeGuards, isBrokenPipeError } = policyModule;
|
||||
|
||||
test('broken output pipes are recognized without treating unrelated failures as EPIPE', () => {
|
||||
assert.equal(isBrokenPipeError(Object.assign(new Error('closed'), { code: 'EPIPE' })), true);
|
||||
assert.equal(isBrokenPipeError(Object.assign(new Error('denied'), { code: 'EACCES' })), false);
|
||||
assert.equal(isBrokenPipeError(null), false);
|
||||
});
|
||||
|
||||
test('output pipe guard absorbs EPIPE and can be cleanly removed', () => {
|
||||
const stdout = new EventEmitter();
|
||||
const stderr = new EventEmitter();
|
||||
const observed = [];
|
||||
const remove = installOutputPipeGuards({ stdout, stderr, onBrokenPipe: (error) => observed.push(error.code) });
|
||||
|
||||
stdout.emit('error', Object.assign(new Error('closed'), { code: 'EPIPE' }));
|
||||
stderr.emit('error', Object.assign(new Error('closed'), { code: 'EPIPE' }));
|
||||
assert.deepEqual(observed, ['EPIPE', 'EPIPE']);
|
||||
remove();
|
||||
assert.equal(stdout.listenerCount('error'), 0);
|
||||
assert.equal(stderr.listenerCount('error'), 0);
|
||||
});
|
||||
@@ -143,6 +143,47 @@ test('refresh remains local-only without configured Gitea credentials', async ()
|
||||
assert.deepEqual(await instance.refresh(), []);
|
||||
});
|
||||
|
||||
test('refresh uses last-known Gitea repositories after a transient remote failure', async () => {
|
||||
const warnings = [];
|
||||
let remoteAvailable = true;
|
||||
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 () => {
|
||||
if (!remoteAvailable) throw new Error('Gitea timed out');
|
||||
return [remote];
|
||||
}
|
||||
}, { debug: async () => {}, warning: async (...args) => warnings.push(args) });
|
||||
instance.discoverAll = async () => [];
|
||||
|
||||
const fresh = await instance.refresh();
|
||||
remoteAvailable = false;
|
||||
const degraded = await instance.refresh();
|
||||
|
||||
assert.equal(fresh[0].remoteStale, false);
|
||||
assert.equal(degraded[0].fullName, remote.full_name);
|
||||
assert.equal(degraded[0].remoteStale, true);
|
||||
assert.equal(degraded[0].remoteRefreshError, 'Gitea timed out');
|
||||
assert.ok(degraded[0].remoteLastRefreshedAt);
|
||||
assert.equal(warnings[0][0], 'repositories.remote-refresh.degraded');
|
||||
});
|
||||
|
||||
test('initial Gitea failure remains visible when no safe cache exists', async () => {
|
||||
const store = {
|
||||
data: { gitea: { baseUrl: 'https://gitea.example' } },
|
||||
getToken: () => 'token'
|
||||
};
|
||||
const instance = new RepositoryService(store, {}, {
|
||||
listRepositories: async () => { throw new Error('Gitea unavailable'); }
|
||||
});
|
||||
await assert.rejects(() => instance.refresh(), /Gitea unavailable/);
|
||||
});
|
||||
|
||||
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 } } }, []);
|
||||
|
||||
@@ -116,6 +116,20 @@ test("server pull provisions a pinned repository-scoped key and records access m
|
||||
assert.equal(result.remoteSha, "a".repeat(40));
|
||||
});
|
||||
|
||||
test("server pull prefers the linked checkout origin over stale detected SSH endpoints", () => {
|
||||
const service = new UnraidDeploymentService({});
|
||||
const repository = {
|
||||
fullName: "Jens/Portfolio",
|
||||
localStatus: { remoteUrl: "git@gitea.itworx.tech:Jens/Portfolio.git" },
|
||||
sshUrl: "ssh://git@192.168.10.150:222/Jens/Portfolio.git",
|
||||
preferredCloneUrl: "ssh://git@192.168.10.150:222/Jens/Portfolio.git",
|
||||
};
|
||||
const profile = { cloneUrl: "ssh://git@192.168.10.150:222/Jens/Portfolio.git" };
|
||||
|
||||
assert.equal(service.serverGitRemote(repository, profile), "git@gitea.itworx.tech:Jens/Portfolio.git");
|
||||
assert.deepEqual(service.serverGitHost(repository, profile), { host: "gitea.itworx.tech", port: 22 });
|
||||
});
|
||||
|
||||
test("write-access inspection parses the remote permission report through the access module", async () => {
|
||||
const encoded = (value) => Buffer.from(value).toString("base64");
|
||||
const profile = { id: "profile", provider: "ssh-unraid", serverId: "server", remoteFolder: "App", composeFiles: ["compose.yml"] };
|
||||
|
||||
Reference in New Issue
Block a user