import test from 'node:test' import assert from 'node:assert/strict' import fs from 'node:fs' import os from 'node:os' import path from 'node:path' import * as plugin from '../index.js' import { ADAPTERS } from '../lib/session.js' function harness() { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'omp-debug-')) const tools = new Map() const effects = [] const ctx = { tools: { register(d) { tools.set(d.name, d); return () => {} } }, effect(fn) { effects.push(fn()) } } plugin.apply(ctx, { stopWaitMs: 15000 }) const exec = { agent: { session: { header: { cwd: dir } } } } const call = async (args) => (await tools.get('debug').execute(args, exec)).text return { dir, call, cleanup: async () => { await call({ action: 'terminate' }).catch(() => {}); fs.rmSync(dir, { recursive: true, force: true }) } } } test('adapters lists availability', async () => { const h = harness() const t = await h.call({ action: 'adapters' }) assert.match(t, /debugpy/) await h.cleanup() }) test('python: breakpoint, inspect, evaluate, step, continue to exit', { skip: !ADAPTERS.debugpy.detect(), timeout: 60000 }, async () => { const h = harness() fs.writeFileSync(path.join(h.dir, 'p.py'), 'def add(a, b):\n c = a + b\n return c\n\nx = add(2, 3)\nprint("result", x)\n') assert.match(await h.call({ action: 'set_breakpoint', file: 'p.py', line: 2 }), /queued/) const l = await h.call({ action: 'launch', program: 'p.py' }) assert.match(l, /Stopped \(breakpoint\).* in add at .*p\.py:2/s) assert.match(l, /→2: c = a \+ b/) const sc = await h.call({ action: 'scopes' }) assert.match(sc, /a: int = 2/) assert.equal((await h.call({ action: 'evaluate', expression: 'a * 10' })).split(' ')[0], '20') const st = await h.call({ action: 'step_over' }) assert.match(st, /p\.py:3/) const tr = await h.call({ action: 'stack_trace' }) assert.match(tr, /#0 add/) const c = await h.call({ action: 'continue' }) assert.match(c, /terminated/i) assert.match(c, /result 5/) assert.doesNotMatch(c, /Traceback|NoMoreMessages|E\+\d{5}/) await h.cleanup() }) test('python: terminate a stopped session cleanly', { skip: !ADAPTERS.debugpy.detect(), timeout: 60000 }, async () => { const h = harness() fs.writeFileSync(path.join(h.dir, 't.py'), 'import time\nfor i in range(100):\n time.sleep(0.05)\nprint("done")\n') await h.call({ action: 'set_breakpoint', file: 't.py', line: 3 }) assert.match(await h.call({ action: 'launch', program: 't.py' }), /Stopped \(breakpoint\)/) const t = await h.call({ action: 'terminate' }) assert.match(t, /^Session dbg\d+ terminated\./) assert.doesNotMatch(t, /Traceback|NoMoreMessages|E\+\d{5}|couldn't handle/) assert.match(await h.call({ action: 'sessions' }), /terminated/) await h.cleanup() }) test('node: breakpoint and evaluate via js-debug', { skip: !ADAPTERS.node.detect(), timeout: 90000 }, async () => { const h = harness() fs.writeFileSync(path.join(h.dir, 'n.js'), 'function mul(a, b) {\n const r = a * b\n return r\n}\nconsole.log("got", mul(6, 7))\n') await h.call({ action: 'set_breakpoint', file: 'n.js', line: 3 }) const l = await h.call({ action: 'launch', program: 'n.js' }) assert.match(l, /Stopped.*n\.js:3/s, l) assert.match(await h.call({ action: 'evaluate', expression: 'r + 1' }), /^43/) const c = await h.call({ action: 'continue' }) assert.match(c, /got 42|terminated|running/i) await h.cleanup() })