package agent_test import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/coder/coder/v2/agent" "github.com/coder/coder/v2/agent/agentcontextconfig" "github.com/coder/coder/v2/agent/agenttest" agentproto "github.com/coder/coder/v2/agent/proto" "github.com/coder/coder/v2/codersdk/agentsdk" "github.com/coder/coder/v2/testutil" ) // TestAgent_ContextStatePushed verifies the agent pushes its workspace // context over the v2.10 PushContextState RPC, and that the readiness // gate (SetReady, wired to the lifecycle transition) holds the push // until startup completes. The first push therefore already contains // the seeded AGENTS.md with Initial=true and no "unreadable" issues. func TestAgent_ContextStatePushed(t *testing.T) { t.Parallel() dir := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(dir, "AGENTS.md"), []byte("test rules"), 0o600)) //nolint:dogsled // setupAgent returns a wide tuple; we only care about the client. _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{Directory: dir}, 0, func(_ *agenttest.Client, opts *agent.Options) { opts.ContextConfig = agentcontextconfig.Config{} }, ) // The push is gated until the agent reaches lifecycle ready. Wait // for that first push to land. var pushes []*agentproto.PushContextStateRequest require.Eventually(t, func() bool { pushes = client.ContextStatePushes() return len(pushes) > 0 }, testutil.WaitMedium, testutil.IntervalFast, "expected a context snapshot push after startup; got %d pushes", len(pushes)) first := pushes[0] assert.True(t, first.GetInitial(), "first push must carry Initial=true") assert.NotEmpty(t, first.GetAggregateHash(), "aggregate_hash must be populated") // The first push must already reflect the ready workspace: the // seeded AGENTS.md is present and no resource is UNREADABLE. var foundAgents bool for _, r := range first.GetResources() { if r.GetInstructionFile() != nil && filepath.Base(r.GetSource()) == "AGENTS.md" { foundAgents = true } assert.NotEqualf(t, agentproto.ContextResource_UNREADABLE, r.GetStatus(), "no resource should be UNREADABLE in the post-ready snapshot: %s", r.GetSource()) } assert.True(t, foundAgents, "first push must already include the seeded AGENTS.md") // Subsequent pushes must not be Initial. for _, p := range pushes[1:] { assert.False(t, p.GetInitial(), "only the first push must be Initial") } }