feat(tess): add Mos coordination boundary (#735)
This commit was merged in pull request #735.
This commit is contained in:
217
apps/gateway/src/coord/mos-coordination.service.test.ts
Normal file
217
apps/gateway/src/coord/mos-coordination.service.test.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import {
|
||||
InMemoryMosCoordinationPort,
|
||||
type MosCoordinationPort,
|
||||
type MosHandoff,
|
||||
} from '@mosaicstack/coord';
|
||||
import type { RuntimeProviderRequestContext } from '../agent/runtime-provider-registry.service.js';
|
||||
import {
|
||||
MosCoordinationService,
|
||||
type MosCoordinationConfig,
|
||||
type MosCoordinationGatewayError,
|
||||
} from './mos-coordination.service.js';
|
||||
|
||||
const context: RuntimeProviderRequestContext = {
|
||||
actorScope: { userId: 'operator-1', tenantId: 'tenant-a' },
|
||||
channelId: 'cli',
|
||||
correlationId: 'corr-1',
|
||||
};
|
||||
|
||||
const config: MosCoordinationConfig = {
|
||||
interactionAgentId: 'Nova',
|
||||
orchestrationAgentId: 'Conductor',
|
||||
};
|
||||
|
||||
function service(
|
||||
port: MosCoordinationPort = new InMemoryMosCoordinationPort(),
|
||||
options: {
|
||||
config?: MosCoordinationConfig;
|
||||
handoffIdFactory?: () => string;
|
||||
} = {},
|
||||
): MosCoordinationService {
|
||||
return new MosCoordinationService(
|
||||
port,
|
||||
options.config ?? config,
|
||||
options.handoffIdFactory ?? (() => 'handoff-1'),
|
||||
);
|
||||
}
|
||||
|
||||
describe('MosCoordinationService authority boundary', (): void => {
|
||||
it('derives identity and actor/tenant scope server-side, then round-trips the native adapter', async (): Promise<void> => {
|
||||
const adapter = new InMemoryMosCoordinationPort();
|
||||
const coordination = service(adapter);
|
||||
|
||||
await expect(
|
||||
coordination.handoff(
|
||||
{ idempotencyKey: 'request-1', summary: 'Implement the requested feature' },
|
||||
context,
|
||||
),
|
||||
).resolves.toEqual({
|
||||
handoffId: 'handoff-1',
|
||||
targetAgentId: 'Conductor',
|
||||
status: 'queued',
|
||||
correlationId: 'corr-1',
|
||||
});
|
||||
|
||||
adapter.recordActivity('handoff-1', 'running', 'Mos accepted the request');
|
||||
adapter.recordResult('handoff-1', 'completed', 'Merged by Mos');
|
||||
|
||||
const followUpContext = { ...context, correlationId: 'corr-2' };
|
||||
await expect(coordination.observe('handoff-1', followUpContext)).resolves.toMatchObject({
|
||||
targetAgentId: 'Conductor',
|
||||
status: 'completed',
|
||||
});
|
||||
await expect(coordination.result('handoff-1', followUpContext)).resolves.toMatchObject({
|
||||
targetAgentId: 'Conductor',
|
||||
status: 'completed',
|
||||
summary: 'Merged by Mos',
|
||||
});
|
||||
});
|
||||
|
||||
it('fails closed without calling a port when the interaction requester is unconfigured', async (): Promise<void> => {
|
||||
const adapter = new InMemoryMosCoordinationPort();
|
||||
const handoff = vi.spyOn(adapter, 'handoff');
|
||||
const coordination = service(adapter, {
|
||||
config: { interactionAgentId: '', orchestrationAgentId: 'Conductor' },
|
||||
});
|
||||
|
||||
await expect(
|
||||
coordination.handoff(
|
||||
{ idempotencyKey: 'request-1', summary: 'Implement the requested feature' },
|
||||
context,
|
||||
),
|
||||
).rejects.toMatchObject({
|
||||
code: 'unconfigured_requester',
|
||||
} satisfies Partial<MosCoordinationGatewayError>);
|
||||
expect(handoff).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects self-delegation configuration before delivering work', async (): Promise<void> => {
|
||||
const adapter = new InMemoryMosCoordinationPort();
|
||||
const handoff = vi.spyOn(adapter, 'handoff');
|
||||
const coordination = service(adapter, {
|
||||
config: { interactionAgentId: 'Nova', orchestrationAgentId: 'Nova' },
|
||||
});
|
||||
|
||||
await expect(
|
||||
coordination.handoff(
|
||||
{ idempotencyKey: 'request-1', summary: 'Implement the requested feature' },
|
||||
context,
|
||||
),
|
||||
).rejects.toThrow('Interaction and orchestration identities must differ');
|
||||
expect(handoff).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('denies cross-tenant observe and result before calling the adapter', async (): Promise<void> => {
|
||||
const adapter = new InMemoryMosCoordinationPort();
|
||||
const observe = vi.spyOn(adapter, 'observe');
|
||||
const result = vi.spyOn(adapter, 'result');
|
||||
const coordination = service(adapter);
|
||||
await coordination.handoff(
|
||||
{ idempotencyKey: 'request-1', summary: 'Implement the requested feature' },
|
||||
context,
|
||||
);
|
||||
|
||||
const otherTenant = {
|
||||
...context,
|
||||
actorScope: { ...context.actorScope, tenantId: 'tenant-b' },
|
||||
};
|
||||
await expect(coordination.observe('handoff-1', otherTenant)).rejects.toMatchObject({
|
||||
code: 'cross_tenant_forbidden',
|
||||
} satisfies Partial<MosCoordinationGatewayError>);
|
||||
await expect(coordination.result('handoff-1', otherTenant)).rejects.toMatchObject({
|
||||
code: 'cross_tenant_forbidden',
|
||||
} satisfies Partial<MosCoordinationGatewayError>);
|
||||
expect(observe).not.toHaveBeenCalled();
|
||||
expect(result).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('scopes idempotency by actor and joins concurrent retries without duplicate delivery', async (): Promise<void> => {
|
||||
let handoffSequence = 0;
|
||||
let release: (() => void) | undefined;
|
||||
const delivered = new Promise<void>((resolve: () => void): void => {
|
||||
release = resolve;
|
||||
});
|
||||
const adapter: MosCoordinationPort = {
|
||||
handoff: vi.fn(async (handoff: MosHandoff) => {
|
||||
await delivered;
|
||||
return {
|
||||
handoffId: handoff.handoffId,
|
||||
targetAgentId: handoff.targetAgentId,
|
||||
status: 'queued' as const,
|
||||
correlationId: handoff.scope.correlationId,
|
||||
};
|
||||
}),
|
||||
observe: vi.fn(),
|
||||
result: vi.fn(),
|
||||
};
|
||||
const coordination = service(adapter, {
|
||||
handoffIdFactory: (): string => `handoff-${++handoffSequence}`,
|
||||
});
|
||||
const request = { idempotencyKey: 'request-1', summary: 'Implement the requested feature' };
|
||||
|
||||
const first = coordination.handoff(request, context);
|
||||
const retry = coordination.handoff(request, context);
|
||||
expect(adapter.handoff).toHaveBeenCalledTimes(1);
|
||||
release?.();
|
||||
await expect(Promise.all([first, retry])).resolves.toEqual([
|
||||
expect.objectContaining({ handoffId: 'handoff-1' }),
|
||||
expect.objectContaining({ handoffId: 'handoff-1' }),
|
||||
]);
|
||||
|
||||
await expect(
|
||||
coordination.handoff(request, {
|
||||
...context,
|
||||
actorScope: { ...context.actorScope, userId: 'operator-2' },
|
||||
}),
|
||||
).resolves.toMatchObject({ handoffId: 'handoff-2' });
|
||||
expect(adapter.handoff).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('rejects idempotency-key payload drift and malformed handoff input before delivery', async (): Promise<void> => {
|
||||
const adapter = new InMemoryMosCoordinationPort();
|
||||
const handoff = vi.spyOn(adapter, 'handoff');
|
||||
const coordination = service(adapter);
|
||||
await coordination.handoff(
|
||||
{ idempotencyKey: 'request-1', summary: 'Implement the requested feature' },
|
||||
context,
|
||||
);
|
||||
|
||||
await expect(
|
||||
coordination.handoff({ idempotencyKey: 'request-1', summary: 'Different work' }, context),
|
||||
).rejects.toMatchObject({
|
||||
code: 'handoff_conflict',
|
||||
} satisfies Partial<MosCoordinationGatewayError>);
|
||||
await expect(
|
||||
coordination.handoff({ idempotencyKey: 'request-2', summary: '' }, context),
|
||||
).rejects.toMatchObject({
|
||||
code: 'invalid_request',
|
||||
} satisfies Partial<MosCoordinationGatewayError>);
|
||||
await expect(
|
||||
coordination.handoff({ idempotencyKey: 'request-3', summary: 'x'.repeat(2_049) }, context),
|
||||
).rejects.toMatchObject({
|
||||
code: 'invalid_request',
|
||||
} satisfies Partial<MosCoordinationGatewayError>);
|
||||
expect(handoff).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('fails closed when the port reports a target that drifts from configuration', async (): Promise<void> => {
|
||||
const adapter: MosCoordinationPort = {
|
||||
handoff: vi.fn(async (handoff: MosHandoff) => ({
|
||||
handoffId: handoff.handoffId,
|
||||
targetAgentId: 'Unexpected',
|
||||
status: 'accepted' as const,
|
||||
correlationId: handoff.scope.correlationId,
|
||||
})),
|
||||
observe: vi.fn(),
|
||||
result: vi.fn(),
|
||||
};
|
||||
|
||||
await expect(
|
||||
service(adapter).handoff(
|
||||
{ idempotencyKey: 'request-1', summary: 'Implement the requested feature' },
|
||||
context,
|
||||
),
|
||||
).rejects.toMatchObject({ code: 'target_drift' });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user