feat: add safe Gitea sync and signed updates
This commit is contained in:
@@ -320,6 +320,86 @@ test("low-level inventory scan is read-only and user discovery auto-links exact
|
||||
assert.deepEqual(discovery.refreshedProfileIds, [profiles[0].id]);
|
||||
});
|
||||
|
||||
test("a stale deployment link cannot block adoption of its running replacement", async () => {
|
||||
const repository = {
|
||||
fullName: "Jens/DevRunBook",
|
||||
name: "DevRunBook",
|
||||
defaultBranch: "main",
|
||||
sshUrl: "git@gitea.test:Jens/DevRunBook.git",
|
||||
};
|
||||
const stale = {
|
||||
workloadId: "old-devrunbook",
|
||||
status: "stale",
|
||||
classification: { type: "stale-link" },
|
||||
runtime: { running: false, health: "missing" },
|
||||
link: { profileId: "old-profile", repositoryFullName: repository.fullName },
|
||||
candidates: [{ repositoryFullName: repository.fullName, score: 100, exact: true }],
|
||||
};
|
||||
const replacement = {
|
||||
workloadId: "devrunbook-runtime",
|
||||
serverId: "unraid",
|
||||
displayName: "DevRunBook",
|
||||
status: "suggested",
|
||||
classification: { type: "active-application" },
|
||||
runtime: { running: true, health: "healthy" },
|
||||
link: null,
|
||||
candidates: [{
|
||||
repositoryFullName: repository.fullName,
|
||||
score: 85,
|
||||
exact: false,
|
||||
identityExact: true,
|
||||
}],
|
||||
compose: {
|
||||
project: "devrunbook",
|
||||
workingDir: "/mnt/user/appdata/DevRunBook",
|
||||
configFiles: ["/mnt/user/appdata/DevRunBook/compose.yml"],
|
||||
services: ["app"],
|
||||
},
|
||||
containers: [{ name: "DevRunBook", running: true, mounts: [], ports: [] }],
|
||||
metadata: { branch: "main" },
|
||||
remoteFolderCandidate: "DevRunBook",
|
||||
};
|
||||
const saved = [];
|
||||
const service = new UnraidDeploymentService({
|
||||
store: {
|
||||
data: {
|
||||
deploymentProfiles: {
|
||||
[repository.fullName]: [{
|
||||
id: "old-profile",
|
||||
provider: "ssh-unraid",
|
||||
serverId: "unraid",
|
||||
workloadIdentity: { workloadId: stale.workloadId, linkSource: "automatic" },
|
||||
}],
|
||||
},
|
||||
},
|
||||
createRecoverySnapshot: async () => ({}),
|
||||
saveDeploymentProfile: async (_fullName, profile) => {
|
||||
saved.push(profile);
|
||||
return profile;
|
||||
},
|
||||
saveDeploymentState: async () => ({}),
|
||||
},
|
||||
});
|
||||
service.collectServerInventory = async () => ({
|
||||
server: { id: "unraid", name: "Unraid", basePath: "/mnt/user/appdata" },
|
||||
inventory: { capabilities: {}, warnings: [] },
|
||||
workloads: [stale, replacement],
|
||||
});
|
||||
|
||||
const plan = service.reconciliationPlan(
|
||||
{ id: "unraid" },
|
||||
[stale, replacement],
|
||||
[repository],
|
||||
{ autoLink: true },
|
||||
);
|
||||
assert.deepEqual(plan.additions.map((item) => item.workloadId), [replacement.workloadId]);
|
||||
|
||||
const result = await service.scanServerInventory("unraid", [repository], { autoLink: true });
|
||||
assert.equal(result.adopted, 1);
|
||||
assert.equal(replacement.link.repositoryFullName, repository.fullName);
|
||||
assert.equal(saved.length, 1);
|
||||
});
|
||||
|
||||
|
||||
|
||||
test("server inventory includes stopped DockerMan containers without Git and keeps name matches manual", () => {
|
||||
|
||||
Reference in New Issue
Block a user