Files
claude-code/src/jobs/__tests__/state.test.ts
unraid bddffa216a feat: integrate 5 feature branches, upstream fixes, and MIME detection fix
Squashed 5 commits:

Features (from 5 feature branches):
- MCP fix, pipe mute, stub recovery
- KAIROS activation, openclaw autonomy
- Daemon/job command hierarchy + cross-platform bg engine

Upstream fixes:
- fix: Bun.hash compatibility
- chore: chrome dependency update
- docs: browser support guide

MIME detection fix:
- Screenshot detectMimeFromBase64(): decode raw bytes from base64
  instead of broken charCodeAt comparison
- Fixes API 400 on Windows (JPEG) and macOS (PNG) screenshots
2026-04-14 18:32:19 +08:00

92 lines
3.2 KiB
TypeScript

/**
* Tests for src/jobs/state.ts
*
* Uses real temp directories and CLAUDE_CONFIG_DIR env var
* instead of mocking fs, to avoid cross-test mock pollution.
*/
import { describe, expect, test, beforeEach, afterAll } from 'bun:test'
import { mkdtempSync, rmSync, readFileSync, existsSync } from 'fs'
import { join } from 'path'
import { tmpdir } from 'os'
// ─── setup: real temp dir via env var ──────────────────────────────────────
const tempBase = mkdtempSync(join(tmpdir(), 'jobs-state-test-'))
beforeEach(() => {
// Each test gets a fresh config dir
const tempHome = mkdtempSync(join(tempBase, 'home-'))
process.env.CLAUDE_CONFIG_DIR = tempHome
})
afterAll(() => {
delete process.env.CLAUDE_CONFIG_DIR
try {
rmSync(tempBase, { recursive: true, force: true })
} catch {
// best-effort cleanup
}
})
// ─── import ─────────────────────────────────────────────────────────────────
const { createJob, readJobState, appendJobReply, getJobDir } = await import(
'../state.js'
)
// ─── tests ──────────────────────────────────────────────────────────────────
describe('createJob', () => {
test('creates job directory and writes state, template, and input files', () => {
const dir = createJob('job-1', 'my-template', '# Template', 'hello', [
'--flag',
])
expect(dir).toContain('job-1')
expect(existsSync(dir)).toBe(true)
const stateFile = join(dir, 'state.json')
expect(existsSync(stateFile)).toBe(true)
const state = JSON.parse(readFileSync(stateFile, 'utf-8'))
expect(state.jobId).toBe('job-1')
expect(state.templateName).toBe('my-template')
expect(state.status).toBe('created')
expect(state.args).toEqual(['--flag'])
expect(readFileSync(join(dir, 'template.md'), 'utf-8')).toBe('# Template')
expect(readFileSync(join(dir, 'input.txt'), 'utf-8')).toBe('hello')
})
})
describe('readJobState', () => {
test('returns null when job does not exist', () => {
expect(readJobState('nonexistent')).toBeNull()
})
test('returns parsed state when job exists', () => {
createJob('job-2', 'tpl', 'content', 'input', [])
const result = readJobState('job-2')
expect(result).not.toBeNull()
expect(result!.jobId).toBe('job-2')
expect(result!.status).toBe('created')
})
})
describe('appendJobReply', () => {
test('returns false when job does not exist', () => {
expect(appendJobReply('no-job', 'hello')).toBe(false)
})
test('appends reply and updates state', () => {
createJob('job-3', 'tpl', 'content', 'input', [])
const result = appendJobReply('job-3', 'my reply')
expect(result).toBe(true)
const dir = getJobDir('job-3')
const repliesPath = join(dir, 'replies.jsonl')
expect(existsSync(repliesPath)).toBe(true)
const replyLine = JSON.parse(readFileSync(repliesPath, 'utf-8').trim())
expect(replyLine.text).toBe('my reply')
})
})