mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
Fixes: coder/internal#1441 - Move `contextConfigAPI` init from `handleManifest` to `init()`, matching all other API fields - Change `agentcontextconfig.NewAPI` to accept `func() string` closure (lazy directory evaluation) - `Config()` and HTTP handler now compute on demand via `a.manifest.Load().Directory` - Widen `TestAgent_Reconnect` to loop 5 reconnections with a non-empty manifest directory - Add `TestContextConfigAPI_InitOnce` internal test verifying lazy eval across manifest changes - Add `TestNewAPI_LazyDirectory` unit test for the lazy contract > 🤖 Written by a Coder Agent. Reviewed by a human.
97 lines
3.3 KiB
Go
97 lines
3.3 KiB
Go
package agent
|
|
|
|
import (
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"cdr.dev/slog/v3"
|
|
"cdr.dev/slog/v3/sloggers/slogtest"
|
|
"github.com/coder/coder/v2/agent/agentcontextconfig"
|
|
"github.com/coder/coder/v2/agent/proto"
|
|
agentsdk "github.com/coder/coder/v2/codersdk/agentsdk"
|
|
"github.com/coder/coder/v2/testutil"
|
|
)
|
|
|
|
// platformAbsPath constructs an absolute path that is valid
|
|
// on the current platform. On Windows, paths must include a
|
|
// drive letter to be considered absolute.
|
|
func platformAbsPath(parts ...string) string {
|
|
if runtime.GOOS == "windows" {
|
|
return `C:\` + filepath.Join(parts...)
|
|
}
|
|
return "/" + filepath.Join(parts...)
|
|
}
|
|
|
|
// TestReportConnectionEmpty tests that reportConnection() doesn't choke if given an empty IP string, which is what we
|
|
// send if we cannot get the remote address.
|
|
func TestReportConnectionEmpty(t *testing.T) {
|
|
t.Parallel()
|
|
connID := uuid.UUID{1}
|
|
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
|
|
ctx := testutil.Context(t, testutil.WaitShort)
|
|
|
|
uut := &agent{
|
|
hardCtx: ctx,
|
|
logger: logger,
|
|
}
|
|
disconnected := uut.reportConnection(connID, proto.Connection_TYPE_UNSPECIFIED, "")
|
|
|
|
require.Len(t, uut.reportConnections, 1)
|
|
req0 := uut.reportConnections[0]
|
|
require.Equal(t, proto.Connection_TYPE_UNSPECIFIED, req0.GetConnection().GetType())
|
|
require.Equal(t, "", req0.GetConnection().Ip)
|
|
require.Equal(t, connID[:], req0.GetConnection().GetId())
|
|
require.Equal(t, proto.Connection_CONNECT, req0.GetConnection().GetAction())
|
|
|
|
disconnected(0, "because")
|
|
require.Len(t, uut.reportConnections, 2)
|
|
req1 := uut.reportConnections[1]
|
|
require.Equal(t, proto.Connection_TYPE_UNSPECIFIED, req1.GetConnection().GetType())
|
|
require.Equal(t, "", req1.GetConnection().Ip)
|
|
require.Equal(t, connID[:], req1.GetConnection().GetId())
|
|
require.Equal(t, proto.Connection_DISCONNECT, req1.GetConnection().GetAction())
|
|
require.Equal(t, "because", req1.GetConnection().GetReason())
|
|
}
|
|
|
|
func TestContextConfigAPI_InitOnce(t *testing.T) {
|
|
// Not parallel: uses t.Setenv to clear env vars.
|
|
|
|
// Clear env vars so defaults are used and the test is
|
|
// hermetic regardless of the surrounding environment.
|
|
t.Setenv(agentcontextconfig.EnvInstructionsDirs, "")
|
|
t.Setenv(agentcontextconfig.EnvInstructionsFile, "")
|
|
t.Setenv(agentcontextconfig.EnvSkillsDirs, "")
|
|
t.Setenv(agentcontextconfig.EnvSkillMetaFile, "")
|
|
t.Setenv(agentcontextconfig.EnvMCPConfigFiles, "")
|
|
|
|
// After the fix, contextConfigAPI is set once in init() and
|
|
// never reassigned. Config() evaluates lazily via the
|
|
// manifest, so there is no concurrent write to race with.
|
|
dir1 := platformAbsPath("dir1")
|
|
dir2 := platformAbsPath("dir2")
|
|
|
|
a := &agent{}
|
|
a.manifest.Store(&agentsdk.Manifest{Directory: dir1})
|
|
a.contextConfigAPI = agentcontextconfig.NewAPI(func() string {
|
|
if m := a.manifest.Load(); m != nil {
|
|
return m.Directory
|
|
}
|
|
return ""
|
|
})
|
|
|
|
cfg1 := a.contextConfigAPI.Config()
|
|
require.NotEmpty(t, cfg1.MCPConfigFiles)
|
|
require.Contains(t, cfg1.MCPConfigFiles[0], dir1)
|
|
|
|
// Simulate manifest update on reconnection — no field
|
|
// reassignment needed, the lazy closure picks it up.
|
|
a.manifest.Store(&agentsdk.Manifest{Directory: dir2})
|
|
cfg2 := a.contextConfigAPI.Config()
|
|
require.NotEmpty(t, cfg2.MCPConfigFiles)
|
|
require.Contains(t, cfg2.MCPConfigFiles[0], dir2)
|
|
}
|