test(agent): complete model ownership migration coverage

This commit is contained in:
_Kerman committed 2026-10-21 10:43:20 +08:00
1 parent 364226ebd8
commit cdb3a76b6a
5 files changed
+44 -8

No files matched your search

@@ -229,6 +229,28 @@ describe('Session file uploads', () => {
expect(saveImages).not.toHaveBeenCalled()
})
it('admits an image before the Agent has a model selection', async () => {
const { ctx, controller, agent, followup, saveImages } = await uploadHarness()
delete agent.options.provider
delete agent.options.model
expect(agent.model.current()).toBeUndefined()
const resolveModel = vi.spyOn(ctx.llm, 'resolveModelInfo')
const image: ImageAttachmentRef = {
attachmentId: AttachmentId(`sha256:${'ab'.repeat(32)}`),
mediaType: 'image/png', bytes: 3, width: 1, height: 1,
}
saveImages.mockResolvedValueOnce([image])
await expect(controller.prompt(promptRequest([
{ type: 'image', mediaType: 'image/png', data: 'AAAA' },
]), USER_SOURCE)).resolves.toEqual({ accepted: true })
expect(resolveModel).not.toHaveBeenCalled()
expect(followup).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({
content: [{ type: 'image', attachment: image }],
}))
})
it('publishes no receipt when its exact Agent is disposed during storage', async () => {
const { uploads, agent, saveFile, disposeAgent } = await uploadHarness()
const saved = Promise.withResolvers<FileAttachmentRef>()
@@ -2,11 +2,12 @@ import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { Context } from '@deepseek-ai/cordis'
import AgentRegistry from '@deepseek-ai/dsh-agent'
import type { Agent } from '@deepseek-ai/dsh-agent'
import { mountAgentLoopTestDependencies, mountAgentLoopTestHarness } from '@deepseek-ai/dsh-agent-loop-testkit'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import SessionStore, { SESSION_FORMAT_VERSION, SessionId, SessionLogOffset } from '@deepseek-ai/dsh-session'
import type { SessionEvent, SessionHeader } from '@deepseek-ai/dsh-session'
import { RemoteError } from '@deepseek-ai/dsh-typert-protocol'
import { describe, expect, it, vi } from 'vitest'
import { describe, expect, it, onTestFinished, vi } from 'vitest'
import SessionController from '../src/index.ts'
import type { ApiSessionAgentController } from '../src/agent.ts'
import { createSessionTestController, testSessionPersistence } from './test-remote.ts'
@@ -23,9 +24,9 @@ describe('SessionController facade', () => {
it('owns Host service methods and publishes Agent lifecycle projections', async () => {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(SessionProjectionRegistry)
await ctx.plugin(AgentRegistry)
onTestFinished(() => ctx.fiber.dispose())
await mountAgentLoopTestDependencies(ctx)
await mountAgentLoopTestHarness(ctx)
const sessionId = SessionId('controller-session')
const header: SessionHeader = {
version: SESSION_FORMAT_VERSION,
@@ -1,7 +1,7 @@
/** Session Controller fork boundaries, lineage, and inherited model routing. */
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { describe, expect, it, vi } from 'vitest'
import { describe, expect, it, onTestFinished, vi } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import AgentRegistry from '@deepseek-ai/dsh-agent'
import type { Agent, AgentHandle, CreateAgentOptions } from '@deepseek-ai/dsh-agent'
@@ -633,8 +633,12 @@ describe('sessions.fork', () => {
})
it('inherits model selection through the completed turn and excludes later changes', async () => {
const ctx = await composed()
const source = await liveAgent(ctx, 'session-routed', 0)
const ctx = new Context()
onTestFinished(() => ctx.fiber.dispose())
await mountAgentLoopTestDependencies(ctx)
const harness = await mountAgentLoopTestHarness(ctx)
ctx.provide('workspaceRegistry', { list: () => [], archivedSessionIds: [] } as never)
const { session: source } = await harness.create(sid('session-routed'))
source.append('turn/start', { turn: 1 })
source.append('request/header', {
header: {
@@ -666,6 +670,5 @@ describe('sessions.fork', () => {
expect(child.model.current()).toEqual({
provider: 'inherited-provider', model: 'inherited-model', reasoningEffort: 'high',
})
await ctx.fiber.dispose()
})
})
@@ -481,6 +481,7 @@ describe('config-driven session id', () => {
})
it.each([
{ label: 'preserves the recorded effort on the same configured route', route: { provider: 'mock', model: 'mock' }, expected: { provider: 'mock', model: 'mock', reasoningEffort: 'high' }, appended: 0 },
{ label: 'applies an explicit configured effort', route: { provider: 'mock', model: 'mock', reasoningEffort: ReasoningEffortId('low') }, expected: { provider: 'mock', model: 'mock', reasoningEffort: 'low' }, appended: 1 },
{ label: 'keeps the recorded choice without a configured route', route: {}, expected: { provider: 'mock', model: 'mock', reasoningEffort: 'high' }, appended: 0 },
])('config-driven resume $label', async ({ route, expected, appended }) => {
@@ -12,6 +12,7 @@ import {
mountAgentLoopTestDependencies,
mountAgentLoopTestHarness,
unsupportedInbox,
unsupportedModel,
} from '../src/index.ts'
function message(text: string) {
@@ -19,6 +20,14 @@ function message(text: string) {
}
describe('dsh-agent-loop-testkit', () => {
it('rejects reads and changes through an unsupported Agent stub Model', () => {
const model = unsupportedModel()
expect(() => model.current()).toThrow('this test Agent does not support model selection')
expect(() => { model.select({ provider: 'mock', model: 'mock' }) })
.toThrow('this test Agent does not support model selection')
})
it('rejects mutations through an unsupported Agent stub Inbox', () => {
const inbox = unsupportedInbox()