__tests__/gb-sync-all.test.js
Rama main · réplica de lectura del repositorio original.
import { describe, it, expect, vi } from 'vitest';
const { syncAll } = require('../scripts/gb-sync-all');
const image = 'wvdstoep/ghost-browser:v674';
const browser = (namespace, current = 'wvdstoep/ghost-browser:v673', replicas = 1) => ({ metadata: { namespace, name: 'ghost-browser' }, spec: { replicas, template: { spec: { containers: [{ name: 'ghost-browser', image: current }] } } } });
const host = { metadata: { namespace: 'byon-cp-ann', name: 'byon-ann' }, spec: { template: { spec: { containers: [{ name: 'k3s', image: 'k3s' }] } } } };
function mock(root, nested = [], busy = 0) {
return vi.fn(args => {
if (args.includes('get')) return JSON.stringify({ items: args.includes('--') ? nested : root });
if (args.includes('node')) return JSON.stringify({ busy });
return '';
});
}
describe('all installed Ghost Browser releases', () => {
it('discovers platform browsers and every hosted cluster without hardcoded tenants', () => {
const run = mock([browser('pod-ann'), host], [browser('ghost-browser')]);
expect(syncAll(image, run, () => {})).toBe(true);
const updates = run.mock.calls.map(([a]) => a).filter(a => a.includes('set'));
expect(updates).toHaveLength(2);
expect(updates[1]).toEqual(expect.arrayContaining(['byon-cp-ann', 'deployment/byon-ann', 'k3s', 'kubectl', 'ghost-browser']));
});
it('leaves current, stopped and busy browsers alone', () => {
const run = mock([browser('current', image), browser('stopped', undefined, 0), browser('busy')], [], 1);
syncAll(image, run, () => {});
expect(run.mock.calls.some(([a]) => a.includes('set'))).toBe(false);
});
it('fails closed on readiness errors and continues other tenants', () => {
const run = mock([browser('broken'), browser('healthy')]);
run.mockImplementationOnce(() => JSON.stringify({ items: [browser('broken'), browser('healthy')] })).mockImplementationOnce(() => { throw Error('unreachable'); });
expect(syncAll(image, run, () => {})).toBe(false);
expect(run.mock.calls.filter(([a]) => a.includes('set'))).toHaveLength(1);
});
it('rejects non-release images before contacting a cluster', () => {
const run = vi.fn();
expect(() => syncAll('untrusted:latest', run)).toThrow('Invalid release image');
expect(run).not.toHaveBeenCalled();
});
it.each(['ImagePullBackOff', 'CrashLoopBackOff', null])('recovers only a browser blocked on image pull: %s', reason => {
const dep = browser('unavailable');
dep.status = { availableReplicas: 0 };
dep.spec.selector = { matchLabels: { app: 'ghost-browser' } };
const run = mock([dep], [], 1);
run.mockImplementationOnce(() => JSON.stringify({ items: [dep] })).mockImplementationOnce(() => JSON.stringify({ items: reason ? [{ status: { containerStatuses: [{ state: { waiting: { reason } } }] } }] : [] }));
syncAll(image, run, () => {});
expect(run.mock.calls.some(([a]) => a.includes('set'))).toBe(reason === 'ImagePullBackOff');
});
it('delivers the signed release to an owned cluster before switching, never a pull that must fail first', () => {
const run = mock([browser('pod-ann'), host], [browser('ghost-browser')]);
const order = [];
run.mockImplementation(args => {
order.push(args.includes('--') ? 'hosted:' + args[args.indexOf('kubectl') + 3] : 'platform:' + args[2]);
if (args.includes('get')) return JSON.stringify({ items: args.includes('--') ? [browser('ghost-browser')] : [browser('pod-ann'), host] });
if (args.includes('node')) return JSON.stringify({ busy: 0 });
return '';
});
const importImage = vi.fn(() => order.push('import'));
expect(syncAll(image, run, () => {}, importImage)).toBe(true);
expect(importImage).toHaveBeenCalledWith(image, expect.arrayContaining(['byon-cp-ann', 'k3s', 'kubectl']));
const hosted = run.mock.calls.map(([a]) => a).filter(a => a.includes('--'));
expect(hosted.some(a => a.includes('set'))).toBe(false);
const patch = hosted.find(a => a.includes('patch'));
expect(JSON.parse(patch.at(-1)).spec.template.spec.containers[0]).toEqual({ name: 'ghost-browser', image, imagePullPolicy: 'IfNotPresent' });
expect(order.indexOf('import')).toBeLessThan(order.indexOf('hosted:patch'));
// the platform's own browser keeps its rolling set image (its image is ctr-imported by the build)
expect(run.mock.calls.some(([a]) => !a.includes('--') && a.includes('set'))).toBe(true);
});
it('reports a dedicated cluster discovery failure', () => {
const run = mock([host]);
run.mockImplementationOnce(() => JSON.stringify({ items: [host] })).mockImplementationOnce(() => { throw Error('offline'); });
expect(syncAll(image, run, () => {})).toBe(false);
});
});