Files
Trung Nguyen b4de8a00bd fix stale comments on the API server and core runtime paths
Comments and doc comments that drifted from the code after refactors, limited to the serve api request path (sessions, run, steer, resume, elicitation, permissions) and the core runtime it drives: the agent loop, streaming, hooks and tool dispatch, compaction, forked skills, and model overrides.
2026-11-02 11:31:33 +01:00

144 lines
4.3 KiB
Go

package runtime
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/docker/docker-agent/pkg/agent"
"github.com/docker/docker-agent/pkg/team"
"github.com/docker/docker-agent/pkg/tools"
)
// runRuntimeContract exercises the shared non-streaming Runtime expectations
// against any factory. pkg/runtime/remote has a mirrored copy for its Runtime.
//
// Streaming methods (RunStream, Run, Summarize) are exercised by the
// type-specific test files because they need real models or providers.
func runRuntimeContract(t *testing.T, newRT func(t *testing.T) Runtime) {
t.Helper()
t.Run("CurrentAgentName not empty", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
assert.NotEmpty(t, rt.CurrentAgentName(t.Context()))
})
t.Run("CurrentAgentInfo carries the current name", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
info := rt.CurrentAgentInfo(t.Context())
assert.Equal(t, rt.CurrentAgentName(t.Context()), info.Name)
})
t.Run("SetCurrentAgent rejects unknown agents", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
err := rt.SetCurrentAgent(t.Context(), "nonexistent-agent")
assert.Error(t, err)
})
t.Run("CurrentAgentTools returns slice or ErrUnsupported", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
_, err := rt.CurrentAgentTools(t.Context())
if err != nil {
assert.ErrorIs(t, err, ErrUnsupported, "unexpected error type: %v", err)
}
})
t.Run("CurrentAgentToolsetStatuses does not panic", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
_ = rt.CurrentAgentToolsetStatuses()
})
t.Run("RestartToolset on unknown name returns error", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
err := rt.RestartToolset(t.Context(), "nonexistent-toolset")
assert.Error(t, err)
})
t.Run("CurrentMCPPrompts returns a non-nil map", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
prompts := rt.CurrentMCPPrompts(t.Context())
assert.NotNil(t, prompts)
})
t.Run("ExecuteMCPPrompt for unknown prompt returns error", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
_, err := rt.ExecuteMCPPrompt(t.Context(), "nonexistent-prompt", nil)
assert.Error(t, err)
})
t.Run("Steer accepts a message", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
err := rt.Steer(t.Context(), QueuedMessage{Content: "hello"})
if err != nil {
assert.ErrorIs(t, err, ErrUnsupported, "unexpected error type: %v", err)
}
})
t.Run("FollowUp accepts a message", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
err := rt.FollowUp(t.Context(), QueuedMessage{Content: "hello"})
if err != nil {
assert.ErrorIs(t, err, ErrUnsupported, "unexpected error type: %v", err)
}
})
t.Run("ResetStartupInfo is idempotent", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
rt.ResetStartupInfo()
rt.ResetStartupInfo()
})
t.Run("Close is idempotent", func(t *testing.T) {
rt := newRT(t)
require.NoError(t, rt.Close())
// A second Close may error or not; both are acceptable as long as it
// doesn't panic.
_ = rt.Close()
})
t.Run("Resume on a fresh runtime is non-blocking", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
// No tool call is in flight; Resume must not deadlock.
ctx, cancel := context.WithCancel(t.Context())
cancel()
rt.Resume(ctx, ResumeApprove())
})
t.Run("ResumeElicitation outside an elicitation does not panic", func(t *testing.T) {
rt := newRT(t)
t.Cleanup(func() { _ = rt.Close() })
ctx, cancel := context.WithCancel(t.Context())
cancel()
_ = rt.ResumeElicitation(ctx, tools.ElicitationActionDecline, nil, "")
})
}
func TestLocalRuntime_Contract(t *testing.T) {
t.Parallel()
runRuntimeContract(t, func(t *testing.T) Runtime {
t.Helper()
prov := &mockProvider{id: "test/mock-model", stream: &mockStream{}}
root := agent.New("root", "You are a test agent", agent.WithModel(prov))
tm := team.New(team.WithAgents(root))
rt, err := New(t.Context(), tm, WithSessionCompaction(false), WithModelStore(mockModelStore{}))
require.NoError(t, err)
return rt
})
}