conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

commit ec8b30c68cb2fd243bd1b9e33037555a438bb921
parent fa8048840c6c88f07c68b1eef9278f66f133f907
Author: finwo <finwo@pm.me>
Date:   Sun, 20 Sep 2026 03:26:11 +0200

Finish the rename in constants and role fixtures the pass missed

Diffstat:
Msrc/conductor/ui/layout.js | 4++--
Msrc/conductor/ui/pages.js | 8++++----
Mtest/auth.test.js | 2+-
Mtest/oidc.test.js | 8++++----
Mtest/ui.test.js | 6+++---
5 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/conductor/ui/layout.js b/src/conductor/ui/layout.js @@ -104,5 +104,5 @@ export function duration(from, to) { return `${Math.floor(seconds / 60)}m ${seconds % 60}s`; } -export const ACTIVE_RUN_STATES = ['pending', 'running']; -export const ACTIVE_JOB_STATES = ['queued', 'running']; +export const ACTIVE_JOB_STATES = ['pending', 'running']; +export const ACTIVE_TASK_STATES = ['queued', 'running']; diff --git a/src/conductor/ui/pages.js b/src/conductor/ui/pages.js @@ -6,7 +6,7 @@ // polling stops by itself rather than needing to be cancelled. import { html, raw } from './html.js'; -import { badge, notice, oneTimeSecret, shortSha, ago, duration, ACTIVE_RUN_STATES, ACTIVE_JOB_STATES } from './layout.js'; +import { badge, notice, oneTimeSecret, shortSha, ago, duration, ACTIVE_JOB_STATES, ACTIVE_TASK_STATES } from './layout.js'; const poll = (url, seconds = 3) => raw(`hx-get="${url}" hx-trigger="every ${seconds}s" hx-swap="outerHTML"`); @@ -21,7 +21,7 @@ export function jobsPage({ jobs, user, anonymous }) { } export function jobsTable(jobs) { - const live = jobs.some((r) => ACTIVE_RUN_STATES.includes(r.state)); + const live = jobs.some((r) => ACTIVE_JOB_STATES.includes(r.state)); return html`<div id="jobs" ${live ? poll('/partials/jobs', 4) : ''}> <table> <thead><tr> @@ -68,7 +68,7 @@ export function jobPage({ job, tasks, canManage }) { } export function tasksTable(job, tasks) { - const live = ACTIVE_RUN_STATES.includes(job.state); + const live = ACTIVE_JOB_STATES.includes(job.state); // Grouped by graph depth, which is how the pipeline reads. const byId = new Map(tasks.map((j) => [j.id, j])); @@ -138,7 +138,7 @@ export function taskPage({ task, artifacts, log }) { } export function taskLog(task, log) { - const live = ACTIVE_JOB_STATES.includes(task.state); + const live = ACTIVE_TASK_STATES.includes(task.state); return html`<pre id="log" class="log" ${live ? poll(`/partials/tasks/${task.id}/log`, 2) : ''}>${ log || (live ? 'Waiting for output.' : 'No output.') }</pre>`; diff --git a/test/auth.test.js b/test/auth.test.js @@ -62,7 +62,7 @@ test('a token signed with another secret is refused', () => { }); test('a tampered payload is refused', () => { - const token = issueToken('secret', { sub: 'u1', name: 'a', role: 'viewer' }); + const token = issueToken('secret', { sub: 'u1', name: 'a', role: 'user' }); const [header, payload, signature] = token.split('.'); const forged = Buffer.from(JSON.stringify({ ...JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')), diff --git a/test/oidc.test.js b/test/oidc.test.js @@ -120,13 +120,13 @@ test('a token carrying the admin role authenticates as an administrator', apiGon }); }); -test('a token without the admin role is a viewer', apiGone, async () => { +test('a token without the admin role is an ordinary user', apiGone, async () => { await withOidcConductor({}, async (h) => { const headers = await oidc.headers('persona-viewer'); const me = await h.app.inject({ method: 'GET', url: '/api/auth/me', headers }); assert.equal(me.json().user.username, 'bob'); - assert.equal(me.json().user.role, 'viewer'); + assert.equal(me.json().user.role, 'user'); assert.equal((await h.app.inject({ method: 'GET', url: '/api/admin/users', headers })).statusCode, 403); }); @@ -211,10 +211,10 @@ test('a role revoked at the provider is lost on the next request', apiGone, asyn // The provider stops asserting the role for that subject. const account = await h.services.users.byExternalId(`${oidc.issuer}|alice-sub`); await h.services.users.upsertExternal({ - externalId: `${oidc.issuer}|alice-sub`, username: 'alice', role: 'viewer', + externalId: `${oidc.issuer}|alice-sub`, username: 'alice', role: 'user', }); const refreshed = await h.services.users.get(account.id); - assert.equal(refreshed.role, 'viewer'); + assert.equal(refreshed.role, 'user'); }); }); diff --git a/test/ui.test.js b/test/ui.test.js @@ -195,7 +195,7 @@ test('a user cannot open or delete a project they do not own', async () => { await withUi({}, async (h) => { const admin = await signIn(h); await h.app.inject({ - method: 'POST', url: '/users', ...form(admin, { username: 'mallory', password: 'mallory-password', role: 'viewer' }), + method: 'POST', url: '/users', ...form(admin, { username: 'mallory', password: 'mallory-password', role: 'user' }), }); const other = await signIn(h, 'mallory', 'mallory-password'); @@ -234,7 +234,7 @@ test('an ordinary user cannot create shared capacity from the form', async () => await withUi({}, async (h) => { const admin = await signIn(h); await h.app.inject({ - method: 'POST', url: '/users', ...form(admin, { username: 'carol', password: 'carol-password', role: 'viewer' }), + method: 'POST', url: '/users', ...form(admin, { username: 'carol', password: 'carol-password', role: 'user' }), }); const carol = await signIn(h, 'carol', 'carol-password'); @@ -252,7 +252,7 @@ test('the users page is administrator only and warns about what deletion destroy await withUi({}, async (h) => { const admin = await signIn(h); await h.app.inject({ - method: 'POST', url: '/users', ...form(admin, { username: 'dave', password: 'dave-password', role: 'viewer' }), + method: 'POST', url: '/users', ...form(admin, { username: 'dave', password: 'dave-password', role: 'user' }), }); const dave = await h.services.users.byUsername('dave'); await h.services.projects.setOwner('demo', dave.id);