agent-ecosystem/test/main/services/team/OpenCodeEventNormalizer.test.ts
2026-04-21 20:28:22 +03:00

240 lines
6.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
mapOpenCodeStatusToDurableState,
normalizeOpenCodeEvent,
normalizeOpenCodeSessionStatus,
} from '../../../../src/main/services/team/opencode/events/OpenCodeEventNormalizer';
describe('OpenCodeEventNormalizer', () => {
it('normalizes v1.14 session.status object', () => {
expect(
normalizeOpenCodeEvent({
type: 'session.status',
properties: {
sessionID: 'ses_1',
status: { type: 'retry', attempt: 2, message: 'rate limited', next: 123 },
},
})
).toMatchObject({
kind: 'session_status',
sessionId: 'ses_1',
status: {
type: 'retry',
retryAttempt: 2,
retryMessage: 'rate limited',
retryNextAt: 123,
rawShape: 'v1.14',
},
});
});
it('normalizes legacy string status and active compatibility status', () => {
expect(normalizeOpenCodeSessionStatus('active')).toMatchObject({
type: 'busy',
rawShape: 'legacy-string',
});
expect(normalizeOpenCodeSessionStatus('idle')).toMatchObject({
type: 'idle',
rawShape: 'legacy-string',
});
expect(normalizeOpenCodeSessionStatus('unexpected')).toMatchObject({
type: 'unknown',
rawShape: 'legacy-string',
});
});
it('normalizes deprecated session.idle as an idle session status', () => {
expect(
normalizeOpenCodeEvent({
type: 'session.idle',
properties: { sessionID: 'ses_1' },
})
).toMatchObject({
kind: 'session_status',
sessionId: 'ses_1',
status: {
type: 'idle',
rawShape: 'v1.14',
},
});
});
it('normalizes global event envelopes without losing directory evidence', () => {
expect(
normalizeOpenCodeEvent({
directory: '/repo',
payload: {
type: 'server.heartbeat',
properties: {},
},
})
).toEqual({
kind: 'server_heartbeat',
scope: 'global',
directory: '/repo',
raw: {
directory: '/repo',
payload: {
type: 'server.heartbeat',
properties: {},
},
},
});
});
it('normalizes message.updated role and message id from info snapshot', () => {
expect(
normalizeOpenCodeEvent({
type: 'message.updated',
properties: {
sessionID: 'ses_1',
info: { id: 'msg_1', role: 'assistant' },
},
})
).toMatchObject({
kind: 'message_updated',
sessionId: 'ses_1',
messageId: 'msg_1',
role: 'assistant',
});
});
it('normalizes message.part.updated snapshots separately from streaming deltas', () => {
expect(
normalizeOpenCodeEvent({
type: 'message.part.updated',
properties: {
sessionID: 'ses_1',
part: {
id: 'part_1',
messageID: 'msg_1',
type: 'text',
text: 'complete text',
},
},
})
).toMatchObject({
kind: 'message_part_updated',
sessionId: 'ses_1',
messageId: 'msg_1',
partId: 'part_1',
partType: 'text',
textSnapshot: 'complete text',
});
});
it('normalizes streaming text from message.part.delta', () => {
expect(
normalizeOpenCodeEvent({
type: 'message.part.delta',
properties: {
sessionID: 'ses_1',
messageID: 'msg_1',
partID: 'part_1',
field: 'text',
delta: 'hello',
},
})
).toMatchObject({
kind: 'message_part_delta',
sessionId: 'ses_1',
messageId: 'msg_1',
partId: 'part_1',
field: 'text',
delta: 'hello',
});
});
it('normalizes permission events across v1.14 and legacy ids', () => {
expect(
normalizeOpenCodeEvent({
type: 'permission.asked',
properties: {
sessionID: 'ses_1',
id: 'perm_1',
},
})
).toMatchObject({
kind: 'permission_asked',
sessionId: 'ses_1',
requestId: 'perm_1',
});
expect(
normalizeOpenCodeEvent({
type: 'permission.replied',
properties: {
sessionID: 'ses_1',
requestID: 'perm_legacy',
},
})
).toMatchObject({
kind: 'permission_replied',
sessionId: 'ses_1',
requestId: 'perm_legacy',
});
});
it('returns unknown event instead of throwing on incomplete known payloads', () => {
expect(
normalizeOpenCodeEvent({
type: 'message.part.delta',
properties: {
sessionID: 'ses_1',
messageID: 'msg_1',
field: 'text',
delta: 'hello',
},
})
).toMatchObject({
kind: 'unknown',
type: 'message.part.delta',
});
});
it('maps normalized status and projections to durable session state', () => {
expect(
mapOpenCodeStatusToDurableState(normalizeOpenCodeSessionStatus({ type: 'busy' }), {
hasPendingPermission: true,
hasLatestAssistantError: false,
replyPendingSinceMessageId: null,
})
).toBe('blocked');
expect(
mapOpenCodeStatusToDurableState(normalizeOpenCodeSessionStatus({ type: 'busy' }), {
hasPendingPermission: false,
hasLatestAssistantError: true,
replyPendingSinceMessageId: null,
})
).toBe('error');
expect(
mapOpenCodeStatusToDurableState(normalizeOpenCodeSessionStatus({ type: 'retry' }), {
hasPendingPermission: false,
hasLatestAssistantError: false,
replyPendingSinceMessageId: null,
})
).toBe('retrying');
expect(
mapOpenCodeStatusToDurableState(normalizeOpenCodeSessionStatus({ type: 'busy' }), {
hasPendingPermission: false,
hasLatestAssistantError: false,
replyPendingSinceMessageId: null,
})
).toBe('running');
expect(
mapOpenCodeStatusToDurableState(normalizeOpenCodeSessionStatus({ type: 'idle' }), {
hasPendingPermission: false,
hasLatestAssistantError: false,
replyPendingSinceMessageId: 'msg_1',
})
).toBe('reply_pending');
expect(
mapOpenCodeStatusToDurableState(normalizeOpenCodeSessionStatus({ type: 'idle' }), {
hasPendingPermission: false,
hasLatestAssistantError: false,
replyPendingSinceMessageId: null,
})
).toBe('idle');
});
});