Files

93 lines
4.1 KiB
JavaScript

import { test, before } from 'node:test'
import assert from 'node:assert/strict'
import { mkdtempSync, readFileSync, existsSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
before(() => {
process.env.DSHO_HOME = mkdtempSync(join(tmpdir(), 'dsho-store-'))
})
const util = await import('../src/util.mjs')
const store = await import('../src/store.mjs')
test('newId has prefix and is unique', () => {
const ids = new Set(Array.from({ length: 500 }, () => util.newId('j')))
assert.equal(ids.size, 500)
for (const id of ids) assert.match(id, /^j-[0-9a-z]{10,}$/)
})
test('parseDuration accepts s, m, h and bare seconds', () => {
assert.equal(util.parseDuration('90s'), 90)
assert.equal(util.parseDuration('20m'), 1200)
assert.equal(util.parseDuration('1h'), 3600)
assert.equal(util.parseDuration('1.5h'), 5400)
assert.equal(util.parseDuration('300'), 300)
assert.throws(() => util.parseDuration('soon'), /duration/)
})
test('slug keeps umlauts, drops punctuation, limits length', () => {
assert.equal(util.slug('Größte KI-Trends 2026: Überblick!'), 'größte-ki-trends-2026-überblick')
assert.equal(util.slug('a'.repeat(100), 10), 'aaaaaaaaaa')
assert.equal(util.slug('???'), 'task')
})
test('writeJsonAtomic and readJson roundtrip', () => {
const file = join(process.env.DSHO_HOME, 'x', 'a.json')
util.writeJsonAtomic(file, { a: 1 })
assert.deepEqual(util.readJson(file), { a: 1 })
assert.equal(util.readJson(join(process.env.DSHO_HOME, 'missing.json'), null), null)
})
test('pidAlive detects own and dead pids', () => {
assert.equal(util.pidAlive(process.pid), true)
assert.equal(util.pidAlive(999999), false)
assert.equal(util.pidAlive(undefined), false)
})
test('fmtDuration formats compactly', () => {
assert.equal(util.fmtDuration(45_000), '45s')
assert.equal(util.fmtDuration(83_000), '1m 23s')
assert.equal(util.fmtDuration(3_720_000), '1h 02m')
})
test('createJob, updateJob, readJob', () => {
const job = store.createJob({ prompt: 'hello', cwd: '/tmp' })
assert.equal(job.status, 'queued')
assert.ok(existsSync(join(process.env.DSHO_HOME, 'jobs', job.id, 'job.json')))
store.updateJob(job.id, { status: 'running', ownerPid: process.pid })
store.updateJob(job.id, (j) => ({ ...j, progress: { steps: 2 } }))
const read = store.readJob(job.id)
assert.equal(read.status, 'running')
assert.equal(read.progress.steps, 2)
assert.equal(JSON.parse(readFileSync(join(process.env.DSHO_HOME, 'jobs', job.id, 'job.json'), 'utf8')).status, 'running')
})
test('readJob reports lost when the owner died', () => {
const job = store.createJob({ prompt: 'x', cwd: '/tmp' })
store.updateJob(job.id, { status: 'running', ownerPid: 999999 })
assert.equal(store.readJob(job.id).status, 'lost')
})
test('resolveRef: exact, prefix, last, name, missing', () => {
const a = store.createJob({ prompt: 'a', cwd: '/tmp', name: 'alpha-recherche' })
const b = store.createBatch({ name: 'meine-batch', root: '/tmp', parallel: 2, jobs: [a.id] })
assert.deepEqual(store.resolveRef(a.id), { kind: 'job', id: a.id })
assert.deepEqual(store.resolveRef(a.id.slice(0, -2)), { kind: 'job', id: a.id })
assert.deepEqual(store.resolveRef('last'), { kind: 'job', id: a.id })
assert.deepEqual(store.resolveRef('last-batch'), { kind: 'batch', id: b.id })
assert.deepEqual(store.resolveRef(b.id), { kind: 'batch', id: b.id })
assert.deepEqual(store.resolveRef('alpha-recherche'), { kind: 'job', id: a.id })
assert.deepEqual(store.resolveRef('meine-batch'), { kind: 'batch', id: b.id })
assert.throws(() => store.resolveRef('gibtsnicht'), (e) => e.code === 3)
})
test('listJobs filters by batch and sorts newest first', () => {
const b = store.createBatch({ name: 'filter', root: '/tmp', parallel: 1, jobs: [] })
const j1 = store.createJob({ prompt: '1', cwd: '/tmp', batch: b.id, index: 1 })
const j2 = store.createJob({ prompt: '2', cwd: '/tmp', batch: b.id, index: 2 })
const list = store.listJobs({ batch: b.id })
assert.deepEqual(list.map((j) => j.id).sort(), [j1.id, j2.id].sort())
assert.deepEqual(store.listJobs({ batch: b.id, order: 'index' }).map((j) => j.id), [j1.id, j2.id])
})