Update Harbor client and gateway integration workflows
Build and Deploy Gateway / build-and-push (push) Failing after 14s
Build and Deploy Gateway / deploy (push) Has been skipped

This commit is contained in:
2026-08-19 18:16:10 +03:00
parent 416b2b294a
commit daec12e013
63 changed files with 5016 additions and 108 deletions
+193
View File
@@ -52,6 +52,7 @@ function createHarness(overrides = {}) {
let stateUpdates = 0;
let startFailure = overrides.failStart || null;
const events = [];
const journalEvents = [];
const serialize = (operation) => {
const run = async () => {
@@ -119,13 +120,16 @@ function createHarness(overrides = {}) {
restartCommand: () => captureRuntimeCommand(restart, { preMutationErrorCodes: ['CONFIG_INVALID'] }),
},
route: { isGatewayDirect: () => overrides.gatewayDirect === true },
failover: overrides.failover,
serialize,
now: () => new Date('2026-08-08T12:00:00.000Z'),
onEvent: (event) => journalEvents.push(event),
});
return {
service,
events,
journalEvents,
serialize,
snapshot: () => structuredClone({ state, config, running, peak }),
};
@@ -167,6 +171,8 @@ test('failed candidate config or runtime restores the old profile, config and ru
const before = harness.snapshot();
await assert.rejects(harness.service.apply('work', 'b'));
assert.deepEqual(domain(harness.snapshot()), domain(before));
assert.equal(harness.journalEvents.at(-1).type, 'connection.failed');
assert.match(harness.journalEvents.at(-1).data.errorCode, /^[A-Z0-9_]+$/);
}
});
@@ -256,6 +262,193 @@ test('running restart preserves a pending desired profile while restoring the ap
assert.equal(harness.snapshot().state.appliedProfileId, 'personal');
});
test('running dual restart rebuilds only the applied failover pair', async () => {
const state = initialState();
state.failoverPolicy = { enabled: true };
state.appliedFailoverPolicy = {
primary: { profileId: 'personal', serverId: 'a' },
reserve: { profileId: 'work', serverId: 'b' },
};
const sources = [];
const prepared = [];
const harness = createHarness({
state,
failover: {
build: (_state, source) => {
sources.push(source);
return {
config: { source },
applied: state.appliedFailoverPolicy,
primaryProfile: state.profiles[0],
primaryServer: serverA,
};
},
prepareActivation: async (role) => { prepared.push(role); harness.events.push(`selector.${role}`); },
restoreAppliedActivation: async () => {},
reconcile: async () => {},
},
});
await harness.service.restart();
assert.deepEqual(sources, ['applied']);
assert.deepEqual(prepared, ['primary']);
assert.deepEqual(harness.events.slice(0, 4), ['config.write', 'runtime.restart', 'selector.primary', 'state.update']);
assert.match(harness.snapshot().config, /"source":"applied"/);
});
test('running single-channel keeps server edits pending after failover is enabled', async () => {
const state = initialState();
state.failoverPolicy = { enabled: true };
state.appliedFailoverPolicy = null;
const harness = createHarness({ state });
await harness.service.apply('work', 'b');
assert.equal(harness.snapshot().state.desiredProfileId, 'work');
assert.equal(harness.snapshot().state.appliedProfileId, 'personal');
assert.equal(harness.events.includes('config.write'), false);
assert.equal(harness.events.includes('runtime.start'), false);
});
test('running dual restart preserves reserve and commits only after selector read-back', async () => {
const state = initialState();
state.failoverPolicy = { enabled: true };
state.appliedFailoverPolicy = {
primary: { profileId: 'personal', serverId: 'a' },
reserve: { profileId: 'work', serverId: 'b' },
};
state.appliedProfileId = 'work';
state.appliedServerId = 'b';
state.appliedServerSnapshot = serverB;
const harness = createHarness({
state,
failover: {
build: () => ({
config: { dual: true },
applied: state.appliedFailoverPolicy,
primaryProfile: state.profiles[0],
primaryServer: serverA,
}),
prepareActivation: async (role) => { harness.events.push(`selector.${role}`); },
restoreAppliedActivation: async () => { harness.events.push('selector.primary'); },
reconcile: async () => {},
},
});
await harness.service.restart();
assert.equal(harness.snapshot().state.appliedProfileId, 'work');
assert.equal(harness.snapshot().state.appliedServerId, 'b');
assert.deepEqual(harness.events.slice(0, 4), ['config.write', 'runtime.restart', 'selector.reserve', 'state.update']);
});
test('selector activation failure rolls runtime and config back before applied truth changes', async () => {
const state = initialState();
state.failoverPolicy = { enabled: true };
state.appliedFailoverPolicy = {
primary: { profileId: 'personal', serverId: 'a' },
reserve: { profileId: 'work', serverId: 'b' },
};
const harness = createHarness({
state,
failover: {
build: () => ({
config: { dual: true },
applied: state.appliedFailoverPolicy,
primaryProfile: state.profiles[0],
primaryServer: serverA,
}),
prepareActivation: async (role) => {
harness.events.push(`selector.${role}`);
if (harness.events.filter((event) => event.startsWith('selector.')).length === 1) {
throw new Error('selector failed');
}
},
restoreAppliedActivation: async () => { harness.events.push('selector.primary'); },
reconcile: async () => {},
},
});
const before = harness.snapshot();
await assert.rejects(harness.service.restart(), /selector failed/);
assert.deepEqual(domain(harness.snapshot()), domain(before));
assert.equal(harness.events.includes('state.update'), false);
assert.deepEqual(harness.events.filter((event) => event.startsWith('selector.')), ['selector.primary', 'selector.primary']);
});
test('state commit rollback restores the previously selected reserve role', async () => {
const state = initialState();
state.failoverPolicy = { enabled: true };
state.appliedFailoverPolicy = {
primary: { profileId: 'personal', serverId: 'a' },
reserve: { profileId: 'work', serverId: 'b' },
};
state.appliedProfileId = 'work';
state.appliedServerId = 'b';
state.appliedServerSnapshot = serverB;
const harness = createHarness({
state,
failStateAt: 1,
failover: {
build: () => ({
config: { dual: true },
applied: state.appliedFailoverPolicy,
primaryProfile: state.profiles[0],
primaryServer: serverA,
}),
prepareActivation: async (role) => { harness.events.push(`selector.${role}`); },
restoreAppliedActivation: async () => { harness.events.push('selector.reserve'); },
reconcile: async () => {},
},
});
const before = harness.snapshot();
await assert.rejects(harness.service.restart(), /state failed/);
assert.deepEqual(domain(harness.snapshot()), domain(before));
assert.deepEqual(harness.events.filter((event) => event.startsWith('selector.')), ['selector.reserve', 'selector.reserve']);
});
test('failed stop restores the selected reserve role before restoring state', async () => {
const state = initialState();
state.failoverPolicy = { enabled: true };
state.appliedFailoverPolicy = {
primary: { profileId: 'personal', serverId: 'a' },
reserve: { profileId: 'work', serverId: 'b' },
};
state.appliedProfileId = 'work';
state.appliedServerId = 'b';
state.appliedServerSnapshot = serverB;
const harness = createHarness({
state,
failStateAt: 1,
failover: {
restoreAppliedActivation: async () => { harness.events.push('selector.reserve'); },
reconcile: async () => {},
},
});
const before = harness.snapshot();
await assert.rejects(harness.service.stop(), /state failed/);
assert.deepEqual(domain(harness.snapshot()), domain(before));
assert.deepEqual(harness.events.slice(0, 4), ['runtime.stop', 'state.update', 'runtime.start', 'selector.reserve']);
});
test('disabled passive dual rollback restores the selected reserve role', async () => {
const state = initialState();
state.failoverPolicy = { enabled: false };
state.appliedFailoverPolicy = {
primary: { profileId: 'personal', serverId: 'a' },
reserve: { profileId: 'work', serverId: 'b' },
};
state.appliedProfileId = 'work';
state.appliedServerId = 'b';
state.appliedServerSnapshot = serverB;
const harness = createHarness({
state,
failStateAt: 1,
failover: {
restoreAppliedActivation: async () => { harness.events.push('selector.reserve'); },
reconcile: async () => {},
},
});
const before = harness.snapshot();
await assert.rejects(harness.service.restart(), /state failed/);
assert.deepEqual(domain(harness.snapshot()), domain(before));
assert.equal(harness.events.includes('selector.reserve'), true);
});
test('restart fails closed when runtime status is unknown', async () => {
const state = initialState();
state.desiredProfileId = 'work';