forked from boos/BoosAPI
[codex] tighten update center and proxy debug UX (#325)
* tighten update center and proxy debug UX * Address update center review feedback
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const {
|
||||
getUpdateCenterHelperStatusMock,
|
||||
getCurrentRuntimeVersionMock,
|
||||
parseStableSemVerMock,
|
||||
} = vi.hoisted(() => ({
|
||||
getUpdateCenterHelperStatusMock: vi.fn(),
|
||||
getCurrentRuntimeVersionMock: vi.fn(),
|
||||
parseStableSemVerMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('./updateCenterHelperClient.js', () => ({
|
||||
getUpdateCenterHelperStatus: (...args: unknown[]) => getUpdateCenterHelperStatusMock(...args),
|
||||
}));
|
||||
|
||||
vi.mock('./updateCenterVersionService.js', () => ({
|
||||
getCurrentRuntimeVersion: () => getCurrentRuntimeVersionMock(),
|
||||
parseStableSemVer: (...args: unknown[]) => parseStableSemVerMock(...args),
|
||||
}));
|
||||
|
||||
import {
|
||||
getUpdateCenterDeployBlockMessage,
|
||||
normalizeUpdateCenterTargetDigest,
|
||||
} from './updateCenterDeployGuardService.js';
|
||||
|
||||
describe('updateCenterDeployGuardService', () => {
|
||||
beforeEach(() => {
|
||||
getUpdateCenterHelperStatusMock.mockReset();
|
||||
getCurrentRuntimeVersionMock.mockReset();
|
||||
parseStableSemVerMock.mockReset();
|
||||
getCurrentRuntimeVersionMock.mockReturnValue('1.2.3');
|
||||
parseStableSemVerMock.mockImplementation((value: string | null | undefined) => {
|
||||
const normalized = String(value || '').trim().replace(/^v/i, '');
|
||||
const match = normalized.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
||||
if (!match) return null;
|
||||
return {
|
||||
raw: String(value || ''),
|
||||
normalized,
|
||||
major: Number.parseInt(match[1], 10),
|
||||
minor: Number.parseInt(match[2], 10),
|
||||
patch: Number.parseInt(match[3], 10),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
it('normalizes valid digests and drops invalid ones', () => {
|
||||
expect(normalizeUpdateCenterTargetDigest(' sha256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA '))
|
||||
.toBe('sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
|
||||
expect(normalizeUpdateCenterTargetDigest('not-a-real-digest')).toBeNull();
|
||||
});
|
||||
|
||||
it('treats digest equality as the source of truth when a digest is supplied', async () => {
|
||||
getUpdateCenterHelperStatusMock.mockResolvedValue({
|
||||
imageDigest: 'sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
||||
});
|
||||
|
||||
await expect(getUpdateCenterDeployBlockMessage({
|
||||
config: {
|
||||
enabled: true,
|
||||
helperBaseUrl: 'http://helper.local',
|
||||
namespace: 'ai',
|
||||
releaseName: 'metapi',
|
||||
chartRef: 'oci://ghcr.io/cita-777/charts/metapi',
|
||||
imageRepository: '1467078763/metapi',
|
||||
githubReleasesEnabled: true,
|
||||
dockerHubTagsEnabled: true,
|
||||
defaultDeploySource: 'github-release',
|
||||
},
|
||||
helperToken: 'helper-token',
|
||||
targetTag: '1.2.3',
|
||||
targetDigest: 'sha256:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB',
|
||||
})).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it('falls back to semver equality only when no digest is provided', async () => {
|
||||
await expect(getUpdateCenterDeployBlockMessage({
|
||||
config: {
|
||||
enabled: true,
|
||||
helperBaseUrl: 'http://helper.local',
|
||||
namespace: 'ai',
|
||||
releaseName: 'metapi',
|
||||
chartRef: 'oci://ghcr.io/cita-777/charts/metapi',
|
||||
imageRepository: '1467078763/metapi',
|
||||
githubReleasesEnabled: true,
|
||||
dockerHubTagsEnabled: true,
|
||||
defaultDeploySource: 'github-release',
|
||||
},
|
||||
helperToken: 'helper-token',
|
||||
targetTag: '1.2.3',
|
||||
targetDigest: null,
|
||||
})).resolves.toBe('target version is already running');
|
||||
expect(getUpdateCenterHelperStatusMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { UpdateCenterConfig } from './updateCenterConfigService.js';
|
||||
import { getUpdateCenterHelperStatus } from './updateCenterHelperClient.js';
|
||||
import { getCurrentRuntimeVersion, parseStableSemVer } from './updateCenterVersionService.js';
|
||||
|
||||
function normalizeDigest(value: string | null | undefined) {
|
||||
const digest = String(value || '').trim();
|
||||
return /^sha256:[a-f0-9]{64}$/i.test(digest) ? digest.toLowerCase() : '';
|
||||
}
|
||||
|
||||
function isSameTargetVersion(currentVersion: string, targetTag: string) {
|
||||
const current = parseStableSemVer(currentVersion);
|
||||
const target = parseStableSemVer(targetTag);
|
||||
return !!current && !!target && current.normalized === target.normalized;
|
||||
}
|
||||
|
||||
export function normalizeUpdateCenterTargetDigest(value: string | null | undefined): string | null {
|
||||
return normalizeDigest(value) || null;
|
||||
}
|
||||
|
||||
export async function getUpdateCenterDeployBlockMessage(input: {
|
||||
config: UpdateCenterConfig;
|
||||
helperToken: string;
|
||||
targetTag: string;
|
||||
targetDigest: string | null;
|
||||
}): Promise<string | null> {
|
||||
const normalizedTargetDigest = normalizeDigest(input.targetDigest);
|
||||
if (normalizedTargetDigest) {
|
||||
try {
|
||||
const helperStatus = await getUpdateCenterHelperStatus(input.config, input.helperToken);
|
||||
const currentDigest = normalizeDigest(helperStatus?.imageDigest);
|
||||
if (currentDigest && currentDigest === normalizedTargetDigest) {
|
||||
return 'target image is already running';
|
||||
}
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isSameTargetVersion(getCurrentRuntimeVersion(), input.targetTag)) {
|
||||
return 'target version is already running';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user