Add Prettier and ESLint tooling (Refs #77)
This commit is contained in:
@@ -6,38 +6,69 @@ const now = Date.parse('2026-10-05T12:00:00Z');
|
||||
const iso = (offset = 0) => new Date(now + offset).toISOString();
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
vi.useFakeTimers({ toFake: ['Date'] }); vi.setSystemTime(now);
|
||||
vi.useFakeTimers({ toFake: ['Date'] });
|
||||
vi.setSystemTime(now);
|
||||
fixture = await newFixture([]);
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
db().transaction(() => {
|
||||
const server = db().prepare('INSERT INTO servers(name) VALUES (?)');
|
||||
const operation = db().prepare(`INSERT INTO server_deployments(id,server,action,state,stage,started_at,finished_at,triggered_by)
|
||||
const operation = db()
|
||||
.prepare(`INSERT INTO server_deployments(id,server,action,state,stage,started_at,finished_at,triggered_by)
|
||||
VALUES(?,?,'restart',?,'Complete',?,?,'test')`);
|
||||
for (const name of ['one', 'two']) {
|
||||
server.run(name);
|
||||
for (let i = 0; i < 100; i++) operation.run(`${name}-${i}`, name, 'done', iso(-i), iso(-i));
|
||||
for (let i = 0; i < 100; i++)
|
||||
operation.run(`${name}-${i}`, name, 'done', iso(-i), iso(-i));
|
||||
operation.run(`${name}-old`, name, 'done', iso(-86_400_001), iso(-86_400_001));
|
||||
operation.run(`${name}-failed`, name, 'failed', iso(), iso());
|
||||
operation.run(`${name}-active`, name, 'waiting', iso(), null);
|
||||
}
|
||||
db().prepare("INSERT INTO deployments(id,action,started_at,finished_at) VALUES('campaign','push',?,?)").run(iso(), iso());
|
||||
db()
|
||||
.prepare(
|
||||
"INSERT INTO deployments(id,action,started_at,finished_at) VALUES('campaign','push',?,?)"
|
||||
)
|
||||
.run(iso(), iso());
|
||||
for (const name of ['one', 'two']) {
|
||||
db().prepare(`INSERT INTO deployment_servers(deployment_id,server,files_changed,restart_state,checked_at)
|
||||
VALUES('campaign',?,1,'done',?)`).run(name, iso());
|
||||
db()
|
||||
.prepare(
|
||||
`INSERT INTO deployment_servers(deployment_id,server,files_changed,restart_state,checked_at)
|
||||
VALUES('campaign',?,1,'done',?)`
|
||||
)
|
||||
.run(name, iso());
|
||||
}
|
||||
})();
|
||||
});
|
||||
afterEach(async () => { await fixture.cleanup(); vi.useRealTimers(); vi.restoreAllMocks(); });
|
||||
afterEach(async () => {
|
||||
await fixture.cleanup();
|
||||
vi.useRealTimers();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('fleet and server response scope', () => {
|
||||
it('counts unattached hosts needing attention and rechecks heartbeat age with each snapshot', async () => {
|
||||
const hosts = await import('../../src/lib/server/hosts/index.js');
|
||||
const { fleetSnapshot } = await import('../../src/lib/server/fleet/snapshot.js');
|
||||
const base: import('../../src/lib/hosts.js').Host = { id: 'empty-host', name: 'Empty host', hostname: 'empty-host',
|
||||
portMin: 28960, portMax: 28980, bindAddress: '0.0.0.0', publicAddress: '', minimumFreeBytes: 1024, createdAt: iso(),
|
||||
connected: true, connectionState: 'online', lastHeartbeat: iso(),
|
||||
enrolled: true, credentialActive: true, mode: 'active', readiness: 'ready', readinessReasons: [],
|
||||
inventory: {}, decommission: null };
|
||||
const base: import('../../src/lib/hosts.js').Host = {
|
||||
id: 'empty-host',
|
||||
name: 'Empty host',
|
||||
hostname: 'empty-host',
|
||||
portMin: 28960,
|
||||
portMax: 28980,
|
||||
bindAddress: '0.0.0.0',
|
||||
publicAddress: '',
|
||||
minimumFreeBytes: 1024,
|
||||
createdAt: iso(),
|
||||
connected: true,
|
||||
connectionState: 'online',
|
||||
lastHeartbeat: iso(),
|
||||
enrolled: true,
|
||||
credentialActive: true,
|
||||
mode: 'active',
|
||||
readiness: 'ready',
|
||||
readinessReasons: [],
|
||||
inventory: {},
|
||||
decommission: null
|
||||
};
|
||||
vi.spyOn(hosts, 'listHosts').mockReturnValue([
|
||||
base,
|
||||
{ ...base, id: 'offline', connected: false },
|
||||
@@ -49,14 +80,16 @@ describe('fleet and server response scope', () => {
|
||||
vi.setSystemTime(now + 91_000);
|
||||
expect(fleetSnapshot().hostAttention).toBe(3);
|
||||
// None of these hosts has an assigned instance in this fixture.
|
||||
expect(fleetSnapshot().servers.every(server => !server.host_id)).toBe(true);
|
||||
expect(fleetSnapshot().servers.every((server) => !server.host_id)).toBe(true);
|
||||
});
|
||||
it('omits completed history from fleet REST and SSR responses', async () => {
|
||||
const { GET } = await import('../../src/routes/api/fleet/+server.js');
|
||||
const { load } = await import('../../src/routes/+page.server.js');
|
||||
const body = await GET().json();
|
||||
expect(body.operations.map((operation: { key: string }) => operation.key).sort())
|
||||
.toEqual(['deployment:one-active', 'deployment:two-active']);
|
||||
expect(body.operations.map((operation: { key: string }) => operation.key).sort()).toEqual([
|
||||
'deployment:one-active',
|
||||
'deployment:two-active'
|
||||
]);
|
||||
expect(load().fleetSnapshot.operations).toEqual(body.operations);
|
||||
});
|
||||
|
||||
@@ -65,16 +98,22 @@ describe('fleet and server response scope', () => {
|
||||
const { load } = await import('../../src/routes/servers/[name]/+page.server.js');
|
||||
const registry = await import('../../src/lib/server/fleet/servers.js');
|
||||
const { fleetSnapshot } = await import('../../src/lib/server/fleet/snapshot.js');
|
||||
const expected = fleetSnapshot().servers.find(server => server.name === 'one');
|
||||
const expected = fleetSnapshot().servers.find((server) => server.name === 'one');
|
||||
const list = vi.spyOn(registry, 'listServers');
|
||||
const snapshot = load({ params: { name: 'one' } }).serverSnapshot;
|
||||
expect(list).not.toHaveBeenCalled();
|
||||
expect(snapshot.servers).toEqual([expected]);
|
||||
expect(snapshot.operations).toHaveLength(103);
|
||||
expect(snapshot.operations.every(operation => operation.server === 'one')).toBe(true);
|
||||
expect(snapshot.operations.map(operation => operation.key)).not.toContain('deployment:one-old');
|
||||
expect(snapshot.operations).toContainEqual(expect.objectContaining({ key: 'campaign:campaign:one', state: 'done' }));
|
||||
expect(snapshot.operations).toContainEqual(expect.objectContaining({ key: 'deployment:one-failed', state: 'attention' }));
|
||||
expect(snapshot.operations.every((operation) => operation.server === 'one')).toBe(true);
|
||||
expect(snapshot.operations.map((operation) => operation.key)).not.toContain(
|
||||
'deployment:one-old'
|
||||
);
|
||||
expect(snapshot.operations).toContainEqual(
|
||||
expect.objectContaining({ key: 'campaign:campaign:one', state: 'done' })
|
||||
);
|
||||
expect(snapshot.operations).toContainEqual(
|
||||
expect.objectContaining({ key: 'deployment:one-failed', state: 'attention' })
|
||||
);
|
||||
const body = await GET({ params: { name: 'one' } }).json();
|
||||
expect(body).toEqual(JSON.parse(JSON.stringify(snapshot)));
|
||||
expect(list).not.toHaveBeenCalled();
|
||||
@@ -86,8 +125,12 @@ describe('fleet and server response scope', () => {
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
db().prepare("UPDATE servers SET destroyed_at = ? WHERE name = 'one'").run(iso());
|
||||
expect(serverSnapshot('one').servers).toEqual([]);
|
||||
db().prepare("UPDATE server_deployments SET server_deleted_at = ? WHERE server = 'one'").run(iso());
|
||||
db().prepare("UPDATE deployment_servers SET server_deleted_at = ? WHERE server = 'one'").run(iso());
|
||||
db()
|
||||
.prepare("UPDATE server_deployments SET server_deleted_at = ? WHERE server = 'one'")
|
||||
.run(iso());
|
||||
db()
|
||||
.prepare("UPDATE deployment_servers SET server_deleted_at = ? WHERE server = 'one'")
|
||||
.run(iso());
|
||||
expect(serverSnapshot('one').operations).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -96,11 +139,17 @@ describe('fleet and server response scope', () => {
|
||||
const { serverOperations } = await import('../../src/lib/server/fleet/overview.js');
|
||||
const prepare = vi.spyOn(db(), 'prepare');
|
||||
serverOperations('one', now);
|
||||
const sql = prepare.mock.calls.map(([sql]) => sql).find(sql => sql.includes('FROM server_deployments'))!;
|
||||
const sql = prepare.mock.calls
|
||||
.map(([sql]) => sql)
|
||||
.find((sql) => sql.includes('FROM server_deployments'))!;
|
||||
prepare.mockRestore();
|
||||
const plan = db().prepare(`EXPLAIN QUERY PLAN ${sql}`).all({ server: 'one', since: iso(-86_400_000) }) as { detail: string }[];
|
||||
const details = plan.map(row => row.detail).join('\n');
|
||||
expect(details).toMatch(/SEARCH server_deployments USING (?:COVERING )?INDEX idx_server_deployments_server/);
|
||||
const plan = db()
|
||||
.prepare(`EXPLAIN QUERY PLAN ${sql}`)
|
||||
.all({ server: 'one', since: iso(-86_400_000) }) as { detail: string }[];
|
||||
const details = plan.map((row) => row.detail).join('\n');
|
||||
expect(details).toMatch(
|
||||
/SEARCH server_deployments USING (?:COVERING )?INDEX idx_server_deployments_server/
|
||||
);
|
||||
expect(details).not.toMatch(/SCAN server_deployments\b/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user