summaryrefslogtreecommitdiff
path: root/web/test/start-next-task.test.mjs
blob: eaf30873650fca796297691bf4429d2a6562009e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// start-next-task.test.mjs — contract tests for startNextTask fetch helper
// Run: node --test web/test/start-next-task.test.mjs

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';

// ── Contract: startNextTask(basePath, fetchFn) ─────────────────────────────────
// POSTs to ${basePath}/api/scripts/start-next-task
// Returns {output, exit_code} on HTTP 2xx
// Throws on HTTP error

async function startNextTask(basePath, fetchFn, agent) {
  const url = agent && agent !== 'auto'
    ? `${basePath}/api/scripts/start-next-task?agent=${agent}`
    : `${basePath}/api/scripts/start-next-task`;
  const res = await fetchFn(url, { method: 'POST' });
  if (!res.ok) {
    let msg = `HTTP ${res.status}`;
    try { const body = await res.json(); msg = body.error || msg; } catch {}
    throw new Error(msg);
  }
  return res.json();
}

describe('startNextTask', () => {
  it('POSTs to /api/scripts/start-next-task', async () => {
    let captured = null;
    const mockFetch = (url, opts) => {
      captured = { url, opts };
      return Promise.resolve({
        ok: true,
        json: () => Promise.resolve({ output: 'claudomator start abc-123\n', exit_code: 0 }),
      });
    };

    await startNextTask('http://localhost:8484', mockFetch);
    assert.equal(captured.url, 'http://localhost:8484/api/scripts/start-next-task');
    assert.equal(captured.opts.method, 'POST');
  });

  it('appends agent as query parameter when provided', async () => {
    let captured = null;
    const mockFetch = (url, opts) => {
      captured = { url, opts };
      return Promise.resolve({
        ok: true,
        json: () => Promise.resolve({ output: 'claudomator start --agent claude abc-123\n', exit_code: 0 }),
      });
    };

    await startNextTask('http://localhost:8484', mockFetch, 'claude');
    assert.equal(captured.url, 'http://localhost:8484/api/scripts/start-next-task?agent=claude');
  });

  it('returns output and exit_code on success', async () => {
    const mockFetch = () => Promise.resolve({
      ok: true,
      json: () => Promise.resolve({ output: 'claudomator start abc-123\n', exit_code: 0 }),
    });

    const result = await startNextTask('', mockFetch);
    assert.equal(result.output, 'claudomator start abc-123\n');
    assert.equal(result.exit_code, 0);
  });

  it('returns output when no task available', async () => {
    const mockFetch = () => Promise.resolve({
      ok: true,
      json: () => Promise.resolve({ output: 'No task to start.\n', exit_code: 0 }),
    });

    const result = await startNextTask('', mockFetch);
    assert.equal(result.output, 'No task to start.\n');
  });

  it('throws with server error message on HTTP error', async () => {
    const mockFetch = () => Promise.resolve({
      ok: false,
      status: 500,
      json: () => Promise.resolve({ error: 'script execution failed' }),
    });

    await assert.rejects(
      () => startNextTask('', mockFetch),
      /script execution failed/,
    );
  });

  it('throws with HTTP status on non-JSON error response', async () => {
    const mockFetch = () => Promise.resolve({
      ok: false,
      status: 503,
      json: () => Promise.reject(new Error('not json')),
    });

    await assert.rejects(
      () => startNextTask('', mockFetch),
      /HTTP 503/,
    );
  });
});