Add Prettier and ESLint tooling (Refs #77)
This commit is contained in:
@@ -3,42 +3,70 @@ import { newFixture, type Fixture } from '../helpers/fixture.js';
|
||||
import { prepareNativeHost } from '../helpers/native-host.js';
|
||||
|
||||
let fixture: Fixture;
|
||||
beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); });
|
||||
afterEach(async () => { await fixture.cleanup(); });
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
fixture = await newFixture([]);
|
||||
});
|
||||
afterEach(async () => {
|
||||
await fixture.cleanup();
|
||||
});
|
||||
|
||||
it('persists the manual requester while preserving source filtering and historical records', async () => {
|
||||
const { withRequestUsername } = await import('../../src/lib/server/auth/request-context.js');
|
||||
const { beginDeployment, getDeployment, listDeployments } = await import('../../src/lib/server/provisioning/history.js');
|
||||
const { beginDeployment, getDeployment, listDeployments } =
|
||||
await import('../../src/lib/server/provisioning/history.js');
|
||||
const { db, closeDb } = await import('../../src/lib/server/db/index.js');
|
||||
const legacy = beginDeployment('legacy', 'restart');
|
||||
const [manual, automatic, rebalance] = await withRequestUsername('alice', async () => [
|
||||
beginDeployment('manual', 'restart'), beginDeployment('automatic', 'restart', 'watchdog:incident'),
|
||||
beginDeployment('manual', 'restart'),
|
||||
beginDeployment('automatic', 'restart', 'watchdog:incident'),
|
||||
beginDeployment('rebalance', 'restart', 'rebalance:run')
|
||||
]);
|
||||
closeDb();
|
||||
expect(getDeployment(manual)?.deployment).toMatchObject({ triggered_by: 'ui', requested_by: 'alice' });
|
||||
expect(getDeployment(automatic)?.deployment).toMatchObject({ triggered_by: 'watchdog:incident', requested_by: null });
|
||||
expect(getDeployment(rebalance)?.deployment).toMatchObject({ triggered_by: 'rebalance:run', requested_by: 'alice' });
|
||||
expect(getDeployment(legacy)?.deployment).toMatchObject({ triggered_by: 'ui', requested_by: null });
|
||||
expect(listDeployments({ source: 'manual' }).map(row => row.id)).toEqual(expect.arrayContaining([manual, legacy]));
|
||||
expect(listDeployments({ source: 'manual' }).map(row => row.id)).not.toContain(automatic);
|
||||
expect(getDeployment(manual)?.deployment).toMatchObject({
|
||||
triggered_by: 'ui',
|
||||
requested_by: 'alice'
|
||||
});
|
||||
expect(getDeployment(automatic)?.deployment).toMatchObject({
|
||||
triggered_by: 'watchdog:incident',
|
||||
requested_by: null
|
||||
});
|
||||
expect(getDeployment(rebalance)?.deployment).toMatchObject({
|
||||
triggered_by: 'rebalance:run',
|
||||
requested_by: 'alice'
|
||||
});
|
||||
expect(getDeployment(legacy)?.deployment).toMatchObject({
|
||||
triggered_by: 'ui',
|
||||
requested_by: null
|
||||
});
|
||||
expect(listDeployments({ source: 'manual' }).map((row) => row.id)).toEqual(
|
||||
expect.arrayContaining([manual, legacy])
|
||||
);
|
||||
expect(listDeployments({ source: 'manual' }).map((row) => row.id)).not.toContain(automatic);
|
||||
const { GET } = await import('../../src/routes/api/provisioning/+server.js');
|
||||
for (const view of ['active', 'history']) {
|
||||
if (view === 'history') db().prepare("UPDATE server_deployments SET state='done'").run();
|
||||
const result = await GET({ url: new URL(`http://localhost/api/provisioning?view=${view}`) }).json();
|
||||
expect(result.deployments).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({ id: manual, requested_by: 'alice' }),
|
||||
expect.objectContaining({ id: automatic, requested_by: null }),
|
||||
expect.objectContaining({ id: rebalance, requested_by: 'alice' }),
|
||||
expect.objectContaining({ id: legacy, requested_by: null })
|
||||
]));
|
||||
const result = await GET({
|
||||
url: new URL(`http://localhost/api/provisioning?view=${view}`)
|
||||
}).json();
|
||||
expect(result.deployments).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ id: manual, requested_by: 'alice' }),
|
||||
expect.objectContaining({ id: automatic, requested_by: null }),
|
||||
expect.objectContaining({ id: rebalance, requested_by: 'alice' }),
|
||||
expect.objectContaining({ id: legacy, requested_by: null })
|
||||
])
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('isolates overlapping users and stops attributing deferred work after the request returns', async () => {
|
||||
const { withRequestUsername, currentRequestUsername } = await import('../../src/lib/server/auth/request-context.js');
|
||||
const { withRequestUsername, currentRequestUsername } =
|
||||
await import('../../src/lib/server/auth/request-context.js');
|
||||
let release!: () => void;
|
||||
const gate = new Promise<void>(resolve => { release = resolve; });
|
||||
const gate = new Promise<void>((resolve) => {
|
||||
release = resolve;
|
||||
});
|
||||
let deferred!: Promise<string | null>;
|
||||
const first = withRequestUsername('alice', async () => {
|
||||
deferred = gate.then(() => currentRequestUsername());
|
||||
@@ -57,24 +85,41 @@ it('isolates overlapping users and stops attributing deferred work after the req
|
||||
|
||||
it('records the operator adopting watchdog work but retains the first requester on repeated manual requests', async () => {
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { beginDeployment, getDeployment } = await import('../../src/lib/server/provisioning/history.js');
|
||||
const { beginDeployment, getDeployment } =
|
||||
await import('../../src/lib/server/provisioning/history.js');
|
||||
const { requestLifecycle } = await import('../../src/lib/server/provisioning/requests.js');
|
||||
const { withRequestUsername } = await import('../../src/lib/server/auth/request-context.js');
|
||||
db().prepare("INSERT INTO servers(name) VALUES('server')").run();
|
||||
await prepareNativeHost(['server']);
|
||||
const id = beginDeployment('server', 'restart', 'watchdog:incident');
|
||||
db().prepare("UPDATE server_deployments SET state='waiting' WHERE id=?").run(id);
|
||||
db().prepare(`INSERT INTO watchdog_incidents(id,server,kind,state,summary,details,first_seen_at,last_seen_at)
|
||||
VALUES('incident','server','health','recovering','Unhealthy','{}',?,?)`).run(new Date().toISOString(), new Date().toISOString());
|
||||
db().prepare("INSERT INTO provisioning_sources(operation_id,kind,source_id,server) VALUES(?,'watchdog','incident','server')").run(id);
|
||||
expect(await withRequestUsername('alice', async () => requestLifecycle('server', 'restart'))).toMatchObject({ ok: true, deploymentId: id });
|
||||
expect(await withRequestUsername('bob', async () => requestLifecycle('server', 'restart'))).toMatchObject({ ok: true, deploymentId: id });
|
||||
expect(getDeployment(id)?.deployment).toMatchObject({ requested_by: 'alice', triggered_by: 'ui' });
|
||||
db()
|
||||
.prepare(
|
||||
`INSERT INTO watchdog_incidents(id,server,kind,state,summary,details,first_seen_at,last_seen_at)
|
||||
VALUES('incident','server','health','recovering','Unhealthy','{}',?,?)`
|
||||
)
|
||||
.run(new Date().toISOString(), new Date().toISOString());
|
||||
db()
|
||||
.prepare(
|
||||
"INSERT INTO provisioning_sources(operation_id,kind,source_id,server) VALUES(?,'watchdog','incident','server')"
|
||||
)
|
||||
.run(id);
|
||||
expect(
|
||||
await withRequestUsername('alice', async () => requestLifecycle('server', 'restart'))
|
||||
).toMatchObject({ ok: true, deploymentId: id });
|
||||
expect(
|
||||
await withRequestUsername('bob', async () => requestLifecycle('server', 'restart'))
|
||||
).toMatchObject({ ok: true, deploymentId: id });
|
||||
expect(getDeployment(id)?.deployment).toMatchObject({
|
||||
requested_by: 'alice',
|
||||
triggered_by: 'ui'
|
||||
});
|
||||
});
|
||||
|
||||
it('adds the requester column to existing databases without inventing historical usernames', async () => {
|
||||
const { db, closeDb } = await import('../../src/lib/server/db/index.js');
|
||||
const { beginDeployment, getDeployment } = await import('../../src/lib/server/provisioning/history.js');
|
||||
const { beginDeployment, getDeployment } =
|
||||
await import('../../src/lib/server/provisioning/history.js');
|
||||
const id = beginDeployment('legacy', 'restart');
|
||||
db().exec('ALTER TABLE server_deployments DROP COLUMN requested_by');
|
||||
closeDb();
|
||||
|
||||
Reference in New Issue
Block a user