| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- import { describe, expect, test } from 'bun:test';
- import {
- parseTaskIdFromTaskOutput,
- parseTaskLaunchOutput,
- parseTaskResultFromOutput,
- parseTaskStatusOutput,
- } from './task';
- describe('parseTaskIdFromTaskOutput', () => {
- test('parses task_id line from successful task tool output', () => {
- const output = [
- 'task_id: session-abc-123 (for resuming to continue this task if needed)',
- '',
- '<task_result>',
- 'done',
- '</task_result>',
- ].join('\n');
- expect(parseTaskIdFromTaskOutput(output)).toBe('session-abc-123');
- });
- test('parses task id from XML task output', () => {
- const output = [
- '<task id="ses_123" state="completed">',
- '<task_result>',
- 'done',
- '</task_result>',
- '</task>',
- ].join('\n');
- expect(parseTaskIdFromTaskOutput(output)).toBe('ses_123');
- });
- test('returns undefined when task_id is absent', () => {
- const output = ['<task_result>', 'no task id here', '</task_result>'].join(
- '\n',
- );
- expect(parseTaskIdFromTaskOutput(output)).toBeUndefined();
- });
- });
- describe('parseTaskLaunchOutput', () => {
- test('parses background task launch output only when state is running', () => {
- const output = [
- 'task_id: ses_123',
- 'state: running',
- '',
- '<task_result>',
- 'Background task started.',
- '</task_result>',
- ].join('\n');
- expect(parseTaskLaunchOutput(output)).toEqual({
- taskID: 'ses_123',
- state: 'running',
- result: 'Background task started.',
- });
- });
- test('parses XML background task launch output', () => {
- const output = [
- '<task id="ses_123" state="running">',
- '<task_result>',
- 'Background task started.',
- '</task_result>',
- '</task>',
- ].join('\n');
- expect(parseTaskLaunchOutput(output)).toEqual({
- taskID: 'ses_123',
- state: 'running',
- result: 'Background task started.',
- });
- });
- test('ignores blocking task output without running state', () => {
- const output = [
- 'task_id: ses_123 (for resuming to continue this task if needed)',
- '',
- '<task_result>',
- 'completed result',
- '</task_result>',
- ].join('\n');
- expect(parseTaskLaunchOutput(output)).toBeUndefined();
- });
- test('ignores state lines inside task result body', () => {
- const output = [
- 'task_id: ses_123 (for resuming to continue this task if needed)',
- '',
- '<task_result>',
- 'state: running',
- '</task_result>',
- ].join('\n');
- expect(parseTaskLaunchOutput(output)).toBeUndefined();
- });
- });
- describe('parseTaskStatusOutput', () => {
- test('parses completed status output with task result', () => {
- const output = [
- 'task_id: ses_123',
- 'state: completed',
- '',
- '<task_result>',
- 'done',
- '</task_result>',
- ].join('\n');
- expect(parseTaskStatusOutput(output)).toEqual({
- taskID: 'ses_123',
- state: 'completed',
- timedOut: false,
- result: 'done',
- });
- });
- test('parses XML completed status output with task result', () => {
- const output = [
- '<task id="ses_123" state="completed">',
- '<task_result>',
- 'done',
- '</task_result>',
- '</task>',
- ].join('\n');
- expect(parseTaskStatusOutput(output)).toEqual({
- taskID: 'ses_123',
- state: 'completed',
- timedOut: false,
- result: 'done',
- });
- });
- test('parses error status output with task_error', () => {
- const output = [
- 'task_id: ses_123',
- 'state: error',
- '',
- '<task_error>',
- 'failed hard',
- '</task_error>',
- ].join('\n');
- expect(parseTaskStatusOutput(output)).toEqual({
- taskID: 'ses_123',
- state: 'error',
- timedOut: false,
- result: 'failed hard',
- });
- });
- test('parses cancelled status output with task_error', () => {
- const output = [
- 'task_id: ses_123',
- 'state: cancelled',
- '',
- '<task_error>',
- 'cancelled by user',
- '</task_error>',
- ].join('\n');
- expect(parseTaskStatusOutput(output)).toEqual({
- taskID: 'ses_123',
- state: 'cancelled',
- timedOut: false,
- result: 'cancelled by user',
- });
- });
- test('keeps timeout as running with timedOut overlay', () => {
- const output = [
- 'task_id: ses_123',
- 'state: running',
- '',
- '<task_result>',
- 'Timed out after 120000ms while waiting for task completion.',
- '</task_result>',
- ].join('\n');
- expect(parseTaskStatusOutput(output)).toEqual({
- taskID: 'ses_123',
- state: 'running',
- timedOut: true,
- result: 'Timed out after 120000ms while waiting for task completion.',
- });
- });
- test('returns undefined when state is absent', () => {
- expect(parseTaskStatusOutput('task_id: ses_123')).toBeUndefined();
- });
- });
- describe('parseTaskResultFromOutput', () => {
- test('extracts trimmed task result block', () => {
- expect(
- parseTaskResultFromOutput(
- ['<task_result>', ' hello ', '</task_result>'].join('\n'),
- ),
- ).toBe('hello');
- });
- test('extracts task error block', () => {
- expect(
- parseTaskResultFromOutput(
- ['<task_error>', ' broken ', '</task_error>'].join('\n'),
- ),
- ).toBe('broken');
- });
- test('returns undefined for mismatched tags', () => {
- // Opening with task_result but closing with task_error
- expect(
- parseTaskResultFromOutput(
- ['<task_result>', 'content', '</task_error>'].join('\n'),
- ),
- ).toBeUndefined();
- // Opening with task_error but closing with task_result
- expect(
- parseTaskResultFromOutput(
- ['<task_error>', 'content', '</task_result>'].join('\n'),
- ),
- ).toBeUndefined();
- });
- test('requires matching open and close tags via backreference', () => {
- // Valid: task_result with task_result
- expect(parseTaskResultFromOutput('<task_result>data</task_result>')).toBe(
- 'data',
- );
- // Valid: task_error with task_error
- expect(
- parseTaskResultFromOutput('<task_error>error data</task_error>'),
- ).toBe('error data');
- // Invalid: mismatched
- expect(
- parseTaskResultFromOutput('<task_result>data</task_error>'),
- ).toBeUndefined();
- expect(
- parseTaskResultFromOutput('<task_error>data</task_result>'),
- ).toBeUndefined();
- });
- });
|