test(fleet): stabilize reconciler CI fixtures
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
This commit is contained in:
@@ -493,7 +493,13 @@ describe('fleet command construction', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('runs fleet status through injected runner without touching tmux in tests', async () => {
|
it('runs legacy fleet status through injected runner without touching tmux in tests', async () => {
|
||||||
|
const home = await tempDir();
|
||||||
|
await mkdir(join(home, 'fleet'), { recursive: true });
|
||||||
|
await writeFile(
|
||||||
|
join(home, 'fleet', 'roster.yaml'),
|
||||||
|
'version: 1\ntransport: tmux\nagents: []\n',
|
||||||
|
);
|
||||||
const calls: string[][] = [];
|
const calls: string[][] = [];
|
||||||
const runner: CommandRunner = async (command, args) => {
|
const runner: CommandRunner = async (command, args) => {
|
||||||
calls.push([command, ...args]);
|
calls.push([command, ...args]);
|
||||||
@@ -501,11 +507,14 @@ describe('fleet command construction', () => {
|
|||||||
};
|
};
|
||||||
const program = new Command();
|
const program = new Command();
|
||||||
program.exitOverride();
|
program.exitOverride();
|
||||||
registerFleetCommand(program, { runner });
|
registerFleetCommand(program, { runner, mosaicHome: home });
|
||||||
|
|
||||||
|
try {
|
||||||
await program.parseAsync(['node', 'mosaic', 'fleet', 'status']);
|
await program.parseAsync(['node', 'mosaic', 'fleet', 'status']);
|
||||||
|
|
||||||
expect(calls).toEqual([['systemctl', '--user', 'status', 'mosaic-tmux-holder.service']]);
|
expect(calls).toEqual([['systemctl', '--user', 'status', 'mosaic-tmux-holder.service']]);
|
||||||
|
} finally {
|
||||||
|
await rm(home, { recursive: true, force: true });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('verifies liveness with tmux has-session and does not trust systemd active exited', async () => {
|
it('verifies liveness with tmux has-session and does not trust systemd active exited', async () => {
|
||||||
@@ -621,13 +630,19 @@ describe('fleet command construction', () => {
|
|||||||
).rejects.toThrow('Unsupported fleet profile');
|
).rejects.toThrow('Unsupported fleet profile');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets process exitCode when status runner fails', async () => {
|
it('sets process exitCode when legacy status runner fails', async () => {
|
||||||
|
const home = await tempDir();
|
||||||
|
await mkdir(join(home, 'fleet'), { recursive: true });
|
||||||
|
await writeFile(
|
||||||
|
join(home, 'fleet', 'roster.yaml'),
|
||||||
|
'version: 1\ntransport: tmux\nagents: []\n',
|
||||||
|
);
|
||||||
const originalExitCode = process.exitCode;
|
const originalExitCode = process.exitCode;
|
||||||
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
||||||
const runner: CommandRunner = async () => ({ stdout: '', stderr: 'missing\n', exitCode: 3 });
|
const runner: CommandRunner = async () => ({ stdout: '', stderr: 'missing\n', exitCode: 3 });
|
||||||
const program = new Command();
|
const program = new Command();
|
||||||
program.exitOverride();
|
program.exitOverride();
|
||||||
registerFleetCommand(program, { runner });
|
registerFleetCommand(program, { runner, mosaicHome: home });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await program.parseAsync(['node', 'mosaic', 'fleet', 'status']);
|
await program.parseAsync(['node', 'mosaic', 'fleet', 'status']);
|
||||||
@@ -635,6 +650,7 @@ describe('fleet command construction', () => {
|
|||||||
} finally {
|
} finally {
|
||||||
process.exitCode = originalExitCode;
|
process.exitCode = originalExitCode;
|
||||||
stderrSpy.mockRestore();
|
stderrSpy.mockRestore();
|
||||||
|
await rm(home, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -134,11 +134,10 @@ describe('fleet roster-owned reconciler', (): void => {
|
|||||||
});
|
});
|
||||||
await rm(lockPath);
|
await rm(lockPath);
|
||||||
|
|
||||||
await chmod(fleet, 0o500);
|
const ioFailure = Object.assign(new Error('injected I/O failure'), { code: 'EIO' });
|
||||||
await expect(acquirePrivateReconcileLock(home)()).rejects.toMatchObject({
|
await expect(
|
||||||
code: 'lock-io-failed',
|
acquirePrivateReconcileLock(home, async () => Promise.reject(ioFailure))(),
|
||||||
});
|
).rejects.toMatchObject({ code: 'lock-io-failed' });
|
||||||
await chmod(fleet, 0o700);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not unlink a replacement lock and normally releases its own lock', async (): Promise<void> => {
|
it('does not unlink a replacement lock and normally releases its own lock', async (): Promise<void> => {
|
||||||
|
|||||||
@@ -609,6 +609,7 @@ function mosaicHomeFor(deps: FleetReconcileDeps): string {
|
|||||||
/** Acquires a private lock only after proving the canonical managed path. */
|
/** Acquires a private lock only after proving the canonical managed path. */
|
||||||
export function acquirePrivateReconcileLock(
|
export function acquirePrivateReconcileLock(
|
||||||
mosaicHome: string,
|
mosaicHome: string,
|
||||||
|
openLock: typeof open = open,
|
||||||
): () => Promise<() => Promise<void>> {
|
): () => Promise<() => Promise<void>> {
|
||||||
const fleetDir = join(mosaicHome, 'fleet');
|
const fleetDir = join(mosaicHome, 'fleet');
|
||||||
const lockPath = join(fleetDir, 'roster.yaml.reconcile.lock');
|
const lockPath = join(fleetDir, 'roster.yaml.reconcile.lock');
|
||||||
@@ -619,7 +620,7 @@ export function acquirePrivateReconcileLock(
|
|||||||
|
|
||||||
let handle: FileHandle;
|
let handle: FileHandle;
|
||||||
try {
|
try {
|
||||||
handle = await open(lockPath, 'wx', 0o600);
|
handle = await openLock(lockPath, 'wx', 0o600);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (isCode(error, 'EEXIST')) {
|
if (isCode(error, 'EEXIST')) {
|
||||||
await assertSafeLockLeafIfPresent(lockPath);
|
await assertSafeLockLeafIfPresent(lockPath);
|
||||||
|
|||||||
Reference in New Issue
Block a user