Add Prettier and ESLint tooling (Refs #77)
This commit is contained in:
@@ -1,22 +1,65 @@
|
||||
import { expect, it } from 'vitest';
|
||||
import { render } from 'svelte/server';
|
||||
import type { RebalanceCandidate, RebalancePreview, RebalanceRun } from '../../src/lib/rebalancing.js';
|
||||
import type {
|
||||
RebalanceCandidate,
|
||||
RebalancePreview,
|
||||
RebalanceRun
|
||||
} from '../../src/lib/rebalancing.js';
|
||||
import RebalancePreviewView from '../../src/lib/ui/components/capacity/RebalancePreviewView.svelte';
|
||||
import RebalanceRunView from '../../src/lib/ui/components/capacity/RebalanceRunView.svelte';
|
||||
|
||||
const at = '2026-10-08T12:00:00Z';
|
||||
const move: RebalanceCandidate = { server: 't5_zombie_theater_1', instanceId: 'instance-a', generation: 1,
|
||||
sourceHostId: 'source-a', sourceHostName: 'Source A', targetHostId: 'target-b', targetHostName: 'Target B', improvement: 0.1 };
|
||||
const preview: RebalancePreview = { id: 'preview-a', createdAt: at, expiresAt: '2026-10-08T12:05:00Z',
|
||||
settings: { hostIds: ['source-a', 'target-b'], game: 'all', maxMoves: 5 }, moves: [move],
|
||||
hosts: [{ id: 'source-a', name: 'Source A', before: { cpuPercent: 75, memoryPercent: 50, instances: 6 }, after: { cpuPercent: 62.5, memoryPercent: 42, instances: 5 } }],
|
||||
exclusions: [{ server: 't6_zm_transit_1', reason: 'Lobby has players.' }], warnings: ['Occupied lobbies stay in place.'] };
|
||||
const run: RebalanceRun = { id: 'run-a', createdAt: at, updatedAt: at, settings: preview.settings, state: 'running', message: 'Moving one lobby.',
|
||||
moves: [{ ...move, id: 'move-a', state: 'moving', operationId: 'operation-a', message: 'Waiting for destination health.', updatedAt: at }] };
|
||||
const move: RebalanceCandidate = {
|
||||
server: 't5_zombie_theater_1',
|
||||
instanceId: 'instance-a',
|
||||
generation: 1,
|
||||
sourceHostId: 'source-a',
|
||||
sourceHostName: 'Source A',
|
||||
targetHostId: 'target-b',
|
||||
targetHostName: 'Target B',
|
||||
improvement: 0.1
|
||||
};
|
||||
const preview: RebalancePreview = {
|
||||
id: 'preview-a',
|
||||
createdAt: at,
|
||||
expiresAt: '2026-10-08T12:05:00Z',
|
||||
settings: { hostIds: ['source-a', 'target-b'], game: 'all', maxMoves: 5 },
|
||||
moves: [move],
|
||||
hosts: [
|
||||
{
|
||||
id: 'source-a',
|
||||
name: 'Source A',
|
||||
before: { cpuPercent: 75, memoryPercent: 50, instances: 6 },
|
||||
after: { cpuPercent: 62.5, memoryPercent: 42, instances: 5 }
|
||||
}
|
||||
],
|
||||
exclusions: [{ server: 't6_zm_transit_1', reason: 'Lobby has players.' }],
|
||||
warnings: ['Occupied lobbies stay in place.']
|
||||
};
|
||||
const run: RebalanceRun = {
|
||||
id: 'run-a',
|
||||
createdAt: at,
|
||||
updatedAt: at,
|
||||
settings: preview.settings,
|
||||
state: 'running',
|
||||
message: 'Moving one lobby.',
|
||||
moves: [
|
||||
{
|
||||
...move,
|
||||
id: 'move-a',
|
||||
state: 'moving',
|
||||
operationId: 'operation-a',
|
||||
message: 'Waiting for destination health.',
|
||||
updatedAt: at
|
||||
}
|
||||
]
|
||||
};
|
||||
const noop = () => {};
|
||||
|
||||
it('shows current and projected resources, the move destination, and exclusion reasons before starting', () => {
|
||||
const body = render(RebalancePreviewView, { props: { preview, expired: false, onstart: noop } }).body;
|
||||
const body = render(RebalancePreviewView, {
|
||||
props: { preview, expired: false, onstart: noop }
|
||||
}).body;
|
||||
expect(body).toContain('6 → 5');
|
||||
expect(body).toContain('75% → 62.5%');
|
||||
expect(body).toContain('50% → 42%');
|
||||
@@ -27,11 +70,15 @@ it('shows current and projected resources, the move destination, and exclusion r
|
||||
expect(body).not.toMatch(/<button[^>]*disabled/);
|
||||
});
|
||||
|
||||
it.each(['expired', 'empty', 'blocked'] as const)('prevents starting a %s preview', condition => {
|
||||
const body = render(RebalancePreviewView, { props: {
|
||||
preview: condition === 'empty' ? { ...preview, moves: [] } : preview,
|
||||
expired: condition === 'expired', disabled: condition === 'blocked', onstart: noop
|
||||
} }).body;
|
||||
it.each(['expired', 'empty', 'blocked'] as const)('prevents starting a %s preview', (condition) => {
|
||||
const body = render(RebalancePreviewView, {
|
||||
props: {
|
||||
preview: condition === 'empty' ? { ...preview, moves: [] } : preview,
|
||||
expired: condition === 'expired',
|
||||
disabled: condition === 'blocked',
|
||||
onstart: noop
|
||||
}
|
||||
}).body;
|
||||
expect(body).toMatch(/<button[^>]*disabled/);
|
||||
if (condition === 'expired') expect(body).toContain('This preview has expired.');
|
||||
if (condition === 'empty') expect(body).toContain('No eligible moves would improve');
|
||||
@@ -41,15 +88,23 @@ it('keeps run progress tied to its provisioning record and explains stopping sem
|
||||
const body = render(RebalanceRunView, { props: { run, canManage: true, onstop: noop } }).body;
|
||||
expect(body).toContain('0 of 1 moves completed');
|
||||
expect(body).toContain('Waiting for destination health.');
|
||||
expect(body).toContain('href="/provisioning/operation-a?returnTo=%2Fcapacity%3Frebalance%3D1%26rebalanceRun%3Drun-a"');
|
||||
expect(body).toContain(
|
||||
'href="/provisioning/operation-a?returnTo=%2Fcapacity%3Frebalance%3D1%26rebalanceRun%3Drun-a"'
|
||||
);
|
||||
expect(body).toContain('Stop remaining moves');
|
||||
expect(body).toContain('A move already in progress continues through health verification.');
|
||||
expect(body.replace(/\s+/g, ' ')).toContain(
|
||||
'A move already in progress continues through health verification.'
|
||||
);
|
||||
});
|
||||
|
||||
it('hides stop from viewers and terminal or already-stopping runs', () => {
|
||||
expect(render(RebalanceRunView, { props: { run, canManage: false, onstop: noop } }).body).not.toContain('Stop remaining moves');
|
||||
expect(
|
||||
render(RebalanceRunView, { props: { run, canManage: false, onstop: noop } }).body
|
||||
).not.toContain('Stop remaining moves');
|
||||
for (const state of ['stopping', 'done', 'stopped', 'failed'] as const) {
|
||||
const body = render(RebalanceRunView, { props: { run: { ...run, state }, canManage: true, onstop: noop } }).body;
|
||||
const body = render(RebalanceRunView, {
|
||||
props: { run: { ...run, state }, canManage: true, onstop: noop }
|
||||
}).body;
|
||||
expect(body).not.toContain('Stop remaining moves');
|
||||
expect(body).toContain('View provisioning');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user