Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit095dd80

Browse files
committed
Merge remote-tracking branch 'origin/main' into ssncferreira/feat-cancel-pending-prebuilds
2 parents95825cb +5f97ad0 commit095dd80

File tree

29 files changed

+664
-211
lines changed

29 files changed

+664
-211
lines changed

‎cli/exp_task_test.go‎

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,240 @@ package cli_test
22

33
import (
44
"context"
5+
"encoding/json"
56
"net/http"
67
"net/http/httptest"
8+
"slices"
9+
"strings"
710
"sync"
811
"testing"
12+
"time"
913

1014
"github.com/google/uuid"
15+
"github.com/stretchr/testify/assert"
16+
"github.com/stretchr/testify/require"
17+
"golang.org/x/xerrors"
18+
19+
agentapisdk"github.com/coder/agentapi-sdk-go"
1120

1221
"github.com/coder/coder/v2/agent"
1322
"github.com/coder/coder/v2/agent/agenttest"
23+
"github.com/coder/coder/v2/cli/clitest"
1424
"github.com/coder/coder/v2/coderd/coderdtest"
25+
"github.com/coder/coder/v2/coderd/util/ptr"
1526
"github.com/coder/coder/v2/codersdk"
1627
"github.com/coder/coder/v2/codersdk/agentsdk"
1728
"github.com/coder/coder/v2/provisioner/echo"
1829
"github.com/coder/coder/v2/provisionersdk/proto"
30+
"github.com/coder/coder/v2/testutil"
1931
)
2032

33+
// This test performs an integration-style test for tasks functionality.
34+
//
35+
//nolint:tparallel // The sub-tests of this test must be run sequentially.
36+
funcTest_Tasks(t*testing.T) {
37+
t.Parallel()
38+
39+
// Given: a template configured for tasks
40+
var (
41+
ctx=testutil.Context(t,testutil.WaitLong)
42+
client=coderdtest.New(t,&coderdtest.Options{IncludeProvisionerDaemon:true})
43+
owner=coderdtest.CreateFirstUser(t,client)
44+
userClient,_=coderdtest.CreateAnotherUser(t,client,owner.OrganizationID)
45+
initMsg= agentapisdk.Message{
46+
Content:"test task input for "+t.Name(),
47+
Id:0,
48+
Role:"user",
49+
Time:time.Now().UTC(),
50+
}
51+
authToken=uuid.NewString()
52+
echoAgentAPI=startFakeAgentAPI(t,fakeAgentAPIEcho(ctx,t,initMsg,"hello"))
53+
taskTpl=createAITaskTemplate(t,client,owner.OrganizationID,withAgentToken(authToken),withSidebarURL(echoAgentAPI.URL()))
54+
taskName=strings.ReplaceAll(testutil.GetRandomName(t),"_","-")
55+
)
56+
57+
//nolint:paralleltest // The sub-tests of this test must be run sequentially.
58+
for_,tc:=range []struct {
59+
namestring
60+
cmdArgs []string
61+
assertFnfunc(stdoutstring,userClient*codersdk.Client)
62+
}{
63+
{
64+
name:"create task",
65+
cmdArgs: []string{"exp","task","create","test task input for "+t.Name(),"--name",taskName,"--template",taskTpl.Name},
66+
assertFn:func(stdoutstring,userClient*codersdk.Client) {
67+
require.Contains(t,stdout,taskName,"task name should be in output")
68+
},
69+
},
70+
{
71+
name:"list tasks after create",
72+
cmdArgs: []string{"exp","task","list","--output","json"},
73+
assertFn:func(stdoutstring,userClient*codersdk.Client) {
74+
vartasks []codersdk.Task
75+
err:=json.NewDecoder(strings.NewReader(stdout)).Decode(&tasks)
76+
require.NoError(t,err,"list output should unmarshal properly")
77+
require.Len(t,tasks,1,"expected one task")
78+
require.Equal(t,taskName,tasks[0].Name,"task name should match")
79+
require.Equal(t,initMsg.Content,tasks[0].InitialPrompt,"initial prompt should match")
80+
require.True(t,tasks[0].WorkspaceID.Valid,"workspace should be created")
81+
// For the next test, we need to wait for the workspace to be healthy
82+
ws:=coderdtest.MustWorkspace(t,userClient,tasks[0].WorkspaceID.UUID)
83+
coderdtest.AwaitWorkspaceBuildJobCompleted(t,client,ws.LatestBuild.ID)
84+
agentClient:=agentsdk.New(client.URL,agentsdk.WithFixedToken(authToken))
85+
_=agenttest.New(t,client.URL,authToken,func(o*agent.Options) {
86+
o.Client=agentClient
87+
})
88+
coderdtest.NewWorkspaceAgentWaiter(t,userClient,tasks[0].WorkspaceID.UUID).WithContext(ctx).WaitFor(coderdtest.AgentsReady)
89+
},
90+
},
91+
{
92+
name:"get task status after create",
93+
cmdArgs: []string{"exp","task","status",taskName,"--output","json"},
94+
assertFn:func(stdoutstring,userClient*codersdk.Client) {
95+
vartask codersdk.Task
96+
require.NoError(t,json.NewDecoder(strings.NewReader(stdout)).Decode(&task),"should unmarshal task status")
97+
require.Equal(t,task.Name,taskName,"task name should match")
98+
// NOTE: task status changes type, this is so this test works with both old and new model
99+
require.Contains(t, []string{"active","running"},string(task.Status),"task should be active")
100+
},
101+
},
102+
{
103+
name:"send task message",
104+
cmdArgs: []string{"exp","task","send",taskName,"hello"},
105+
// Assertions for this happen in the fake agent API handler.
106+
},
107+
{
108+
name:"read task logs",
109+
cmdArgs: []string{"exp","task","logs",taskName,"--output","json"},
110+
assertFn:func(stdoutstring,userClient*codersdk.Client) {
111+
varlogs []codersdk.TaskLogEntry
112+
require.NoError(t,json.NewDecoder(strings.NewReader(stdout)).Decode(&logs),"should unmarshal task logs")
113+
require.Len(t,logs,3,"should have 3 logs")
114+
require.Equal(t,logs[0].Content,initMsg.Content,"first message should be the init message")
115+
require.Equal(t,logs[0].Type,codersdk.TaskLogTypeInput,"first message should be an input")
116+
require.Equal(t,logs[1].Content,"hello","second message should be the sent message")
117+
require.Equal(t,logs[1].Type,codersdk.TaskLogTypeInput,"second message should be an input")
118+
require.Equal(t,logs[2].Content,"hello","third message should be the echoed message")
119+
require.Equal(t,logs[2].Type,codersdk.TaskLogTypeOutput,"third message should be an output")
120+
},
121+
},
122+
{
123+
name:"delete task",
124+
cmdArgs: []string{"exp","task","delete",taskName,"--yes"},
125+
assertFn:func(stdoutstring,userClient*codersdk.Client) {
126+
// The task should eventually no longer show up in the list of tasks
127+
testutil.Eventually(ctx,t,func(ctx context.Context)bool {
128+
expClient:=codersdk.NewExperimentalClient(userClient)
129+
tasks,err:=expClient.Tasks(ctx,&codersdk.TasksFilter{})
130+
if!assert.NoError(t,err) {
131+
returnfalse
132+
}
133+
returnslices.IndexFunc(tasks,func(task codersdk.Task)bool {
134+
returntask.Name==taskName
135+
})==-1
136+
},testutil.IntervalMedium)
137+
},
138+
},
139+
} {
140+
t.Run(tc.name,func(t*testing.T) {
141+
varstdout strings.Builder
142+
inv,root:=clitest.New(t,tc.cmdArgs...)
143+
inv.Stdout=&stdout
144+
clitest.SetupConfig(t,userClient,root)
145+
require.NoError(t,inv.WithContext(ctx).Run())
146+
iftc.assertFn!=nil {
147+
tc.assertFn(stdout.String(),userClient)
148+
}
149+
})
150+
}
151+
}
152+
153+
funcfakeAgentAPIEcho(ctx context.Context,t testing.TB,initMsg agentapisdk.Message,want...string)map[string]http.HandlerFunc {
154+
t.Helper()
155+
varmmu sync.RWMutex
156+
msgs:= []agentapisdk.Message{initMsg}
157+
wantCpy:=make([]string,len(want))
158+
copy(wantCpy,want)
159+
t.Cleanup(func() {
160+
mmu.Lock()
161+
defermmu.Unlock()
162+
if!t.Failed() {
163+
assert.Empty(t,wantCpy,"not all expected messages received: missing %v",wantCpy)
164+
}
165+
})
166+
writeAgentAPIError:=func(w http.ResponseWriter,errerror,statusint) {
167+
w.WriteHeader(status)
168+
_=json.NewEncoder(w).Encode(agentapisdk.ErrorModel{
169+
Errors:ptr.Ref([]agentapisdk.ErrorDetail{
170+
{
171+
Message:ptr.Ref(err.Error()),
172+
},
173+
}),
174+
})
175+
}
176+
returnmap[string]http.HandlerFunc{
177+
"/status":func(w http.ResponseWriter,r*http.Request) {
178+
w.Header().Set("Content-Type","application/json")
179+
_=json.NewEncoder(w).Encode(agentapisdk.GetStatusResponse{
180+
Status:"stable",
181+
})
182+
},
183+
"/messages":func(w http.ResponseWriter,r*http.Request) {
184+
w.Header().Set("Content-Type","application/json")
185+
mmu.RLock()
186+
defermmu.RUnlock()
187+
bs,err:=json.Marshal(agentapisdk.GetMessagesResponse{
188+
Messages:msgs,
189+
})
190+
iferr!=nil {
191+
writeAgentAPIError(w,err,http.StatusBadRequest)
192+
return
193+
}
194+
_,_=w.Write(bs)
195+
},
196+
"/message":func(w http.ResponseWriter,r*http.Request) {
197+
mmu.Lock()
198+
defermmu.Unlock()
199+
varparams agentapisdk.PostMessageParams
200+
w.Header().Set("Content-Type","application/json")
201+
err:=json.NewDecoder(r.Body).Decode(&params)
202+
if!assert.NoError(t,err,"decode message") {
203+
writeAgentAPIError(w,err,http.StatusBadRequest)
204+
return
205+
}
206+
207+
iflen(wantCpy)==0 {
208+
assert.Fail(t,"unexpected message","received message %v, but no more expected messages",params)
209+
writeAgentAPIError(w,xerrors.New("no more expected messages"),http.StatusBadRequest)
210+
return
211+
}
212+
exp:=wantCpy[0]
213+
wantCpy=wantCpy[1:]
214+
215+
if!assert.Equal(t,exp,params.Content,"message content mismatch") {
216+
writeAgentAPIError(w,xerrors.New("unexpected message content: expected "+exp+", got "+params.Content),http.StatusBadRequest)
217+
return
218+
}
219+
220+
msgs=append(msgs, agentapisdk.Message{
221+
Id:int64(len(msgs)+1),
222+
Content:params.Content,
223+
Role:agentapisdk.RoleUser,
224+
Time:time.Now().UTC(),
225+
})
226+
msgs=append(msgs, agentapisdk.Message{
227+
Id:int64(len(msgs)+1),
228+
Content:params.Content,
229+
Role:agentapisdk.RoleAgent,
230+
Time:time.Now().UTC(),
231+
})
232+
assert.NoError(t,json.NewEncoder(w).Encode(agentapisdk.PostMessageResponse{
233+
Ok:true,
234+
}))
235+
},
236+
}
237+
}
238+
21239
// setupCLITaskTest creates a test workspace with an AI task template and agent,
22240
// with a fake agent API configured with the provided set of handlers.
23241
// Returns the user client and workspace.

‎coderd/apidoc/docs.go‎

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json‎

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎codersdk/deployment.go‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ func DefaultSupportLinks(docsURL string) []LinkConfig {
985985
},
986986
{
987987
Name:"Join the Coder Discord",
988-
Target:"https://coder.com/chat?utm_source=coder&utm_medium=coder&utm_campaign=server-footer",
988+
Target:"https://discord.gg/coder",
989989
Icon:"chat",
990990
},
991991
{
@@ -3339,7 +3339,9 @@ type SupportConfig struct {
33393339
typeLinkConfigstruct {
33403340
Namestring`json:"name" yaml:"name"`
33413341
Targetstring`json:"target" yaml:"target"`
3342-
Iconstring`json:"icon" yaml:"icon" enums:"bug,chat,docs"`
3342+
Iconstring`json:"icon" yaml:"icon" enums:"bug,chat,docs,star"`
3343+
3344+
Locationstring`json:"location,omitempty" yaml:"location,omitempty" enums:"navbar,dropdown"`
33433345
}
33443346

33453347
// Validate checks cross-field constraints for deployment values.

‎compose.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ services:
22
coder:
33
# This MUST be stable for our documentation and
44
# other automations.
5-
image:ghcr.io/coder/coder:${CODER_VERSION:-latest}
5+
image:${CODER_REPO:-ghcr.io/coder/coder}:${CODER_VERSION:-latest}
66
ports:
77
-"7080:7080"
88
environment:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp