@@ -90,7 +90,7 @@ var mcpDeployTargets = []deployTarget{
9090 },
9191}
9292
93- func TestAgentDeploy (t * testing.T ) {
93+ func TestAgentDeployCreate (t * testing.T ) {
9494 for _ , target := range agentDeployTargets {
9595 t .Run (target .name , func (t * testing.T ) {
9696 if target .name == "kubernetes" && ! IsK8sBackend () {
@@ -101,10 +101,6 @@ func TestAgentDeploy(t *testing.T) {
101101 agentName := UniqueAgentName ("e2edpl" + target .name [:3 ])
102102 agentImage := fmt .Sprintf ("localhost:5001/%s:e2e" , agentName )
103103
104- // Register cleanup at the parent level so it runs after all
105- // subtests (including verify) complete, not after deploy alone.
106- // Remove deployment record first (LIFO) so ReconcileAll in
107- // subsequent tests doesn't try to reconcile stale deployments.
108104 t .Cleanup (func () { RemoveDeploymentsByServerName (t , regURL , agentName ) })
109105 if target .cleanup != nil {
110106 t .Cleanup (func () { target .cleanup (t , agentName ) })
@@ -140,14 +136,22 @@ func TestAgentDeploy(t *testing.T) {
140136 RequireSuccess (t , result )
141137 })
142138
143- t .Run ("deploy " , func (t * testing.T ) {
139+ t .Run ("deploy_create " , func (t * testing.T ) {
144140 t .Logf ("Deploying agent %q (target: %s)..." , agentName , target .name )
145- args := []string {"agent" , "deploy" , agentName , "--registry-url" , regURL }
141+ args := []string {
142+ "deploy" , "create" , agentName ,
143+ "--type" , "agent" ,
144+ "--registry-url" , regURL ,
145+ }
146146 args = append (args , target .deplArgs ... )
147147 result := RunArctl (t , tmpDir , args ... )
148148 RequireSuccess (t , result )
149149 })
150150
151+ t .Run ("deploy_list" , func (t * testing.T ) {
152+ verifyDeploymentInList (t , tmpDir , regURL , agentName , "agent" )
153+ })
154+
151155 if target .verify != nil {
152156 t .Run ("verify" , func (t * testing.T ) {
153157 t .Logf ("Verifying deployment health (target: %s)..." , target .name )
@@ -158,7 +162,7 @@ func TestAgentDeploy(t *testing.T) {
158162 }
159163}
160164
161- func TestMCPDeploy (t * testing.T ) {
165+ func TestMCPDeployCreate (t * testing.T ) {
162166 for _ , target := range mcpDeployTargets {
163167 t .Run (target .name , func (t * testing.T ) {
164168 if target .name == "kubernetes" && ! IsK8sBackend () {
@@ -173,10 +177,6 @@ func TestMCPDeploy(t *testing.T) {
173177
174178 // Delete any stale server entry from a previous interrupted run.
175179 RunArctl (t , tmpDir , "mcp" , "delete" , serverName , "--version" , version , "--registry-url" , regURL )
176- // Register cleanup at the parent level so it runs after all
177- // subtests (including verify) complete, not after deploy alone.
178- // Remove deployment record first (LIFO) so ReconcileAll in
179- // subsequent tests doesn't try to reconcile stale deployments.
180180 t .Cleanup (func () { RemoveDeploymentsByServerName (t , regURL , serverName ) })
181181 t .Cleanup (func () {
182182 RunArctl (t , tmpDir , "mcp" , "delete" , serverName , "--version" , version , "--registry-url" , regURL )
@@ -219,14 +219,23 @@ func TestMCPDeploy(t *testing.T) {
219219 RequireSuccess (t , result )
220220 })
221221
222- t .Run ("deploy " , func (t * testing.T ) {
222+ t .Run ("deploy_create " , func (t * testing.T ) {
223223 t .Logf ("Deploying MCP server %q (target: %s)..." , serverName , target .name )
224- args := []string {"mcp" , "deploy" , serverName , "--version" , version , "--registry-url" , regURL }
224+ args := []string {
225+ "deploy" , "create" , serverName ,
226+ "--type" , "mcp" ,
227+ "--version" , version ,
228+ "--registry-url" , regURL ,
229+ }
225230 args = append (args , target .deplArgs ... )
226231 result := RunArctl (t , tmpDir , args ... )
227232 RequireSuccess (t , result )
228233 })
229234
235+ t .Run ("deploy_list" , func (t * testing.T ) {
236+ verifyDeploymentInList (t , tmpDir , regURL , serverName , "mcp" )
237+ })
238+
230239 if target .verify != nil {
231240 t .Run ("verify" , func (t * testing.T ) {
232241 t .Logf ("Verifying deployment health (target: %s)..." , target .name )
@@ -237,6 +246,124 @@ func TestMCPDeploy(t *testing.T) {
237246 }
238247}
239248
249+ // TestDeployDeleteLifecycle exercises the full CLI lifecycle:
250+ // create a deployment, extract its ID via "deploy list", delete it via
251+ // "arctl deploy delete <prefix>" (testing ID-prefix resolution), and verify
252+ // it no longer appears in "deploy list".
253+ func TestDeployDeleteLifecycle (t * testing.T ) {
254+ regURL := RegistryURL (t )
255+ tmpDir := t .TempDir ()
256+ agentName := UniqueAgentName ("e2edeldpl" )
257+ agentImage := fmt .Sprintf ("localhost:5001/%s:e2e" , agentName )
258+
259+ t .Cleanup (func () { RemoveDeploymentsByServerName (t , regURL , agentName ) })
260+ t .Cleanup (func () { removeLocalDeployment (t ) })
261+
262+ // 1. Init, build, and publish
263+ result := RunArctl (t , tmpDir ,
264+ "agent" , "init" , "adk" , "python" ,
265+ "--model-name" , "gemini-2.5-flash" ,
266+ "--image" , agentImage ,
267+ agentName ,
268+ )
269+ RequireSuccess (t , result )
270+
271+ result = RunArctl (t , tmpDir , "agent" , "build" , agentName ,
272+ "--image" , agentImage )
273+ RequireSuccess (t , result )
274+
275+ agentDir := filepath .Join (tmpDir , agentName )
276+ result = RunArctl (t , tmpDir ,
277+ "agent" , "publish" , agentDir ,
278+ "--registry-url" , regURL ,
279+ )
280+ RequireSuccess (t , result )
281+
282+ // 2. Deploy (local provider)
283+ result = RunArctl (t , tmpDir ,
284+ "deploy" , "create" , agentName ,
285+ "--type" , "agent" ,
286+ "--registry-url" , regURL ,
287+ )
288+ RequireSuccess (t , result )
289+
290+ // 3. Extract the deployment ID from "deploy list -o json"
291+ deploymentID := extractDeploymentID (t , tmpDir , regURL , agentName , "agent" )
292+ if deploymentID == "" {
293+ t .Fatal ("Could not find deployment ID after deploy create" )
294+ }
295+ t .Logf ("Deployment ID: %s" , deploymentID )
296+
297+ // 4. Delete using truncated ID prefix (tests ID-prefix resolution)
298+ prefix := deploymentID [:8 ]
299+ result = RunArctl (t , tmpDir ,
300+ "deploy" , "delete" , prefix ,
301+ "--registry-url" , regURL ,
302+ )
303+ RequireSuccess (t , result )
304+ RequireOutputContains (t , result , "deleted" )
305+
306+ // 5. Verify deployment is gone from "deploy list"
307+ result = RunArctl (t , tmpDir ,
308+ "deploy" , "list" ,
309+ "--type" , "agent" ,
310+ "-o" , "json" ,
311+ "--registry-url" , regURL ,
312+ )
313+ RequireSuccess (t , result )
314+
315+ if strings .TrimSpace (result .Stdout ) != "" {
316+ var remaining []struct {
317+ ID string `json:"id"`
318+ ServerName string `json:"serverName"`
319+ }
320+ if err := json .Unmarshal ([]byte (result .Stdout ), & remaining ); err == nil {
321+ for _ , d := range remaining {
322+ if d .ID == deploymentID {
323+ t .Fatalf ("Deployment %s still present after delete" , deploymentID )
324+ }
325+ }
326+ }
327+ }
328+
329+ // 6. Verify deleting the same ID again fails
330+ result = RunArctl (t , tmpDir ,
331+ "deploy" , "delete" , prefix ,
332+ "--registry-url" , regURL ,
333+ )
334+ RequireFailure (t , result )
335+ }
336+
337+ // extractDeploymentID runs "deploy list -o json" and returns the ID of the
338+ // deployment matching the given resource name and type.
339+ func extractDeploymentID (t * testing.T , workDir , regURL , resourceName , resourceType string ) string {
340+ t .Helper ()
341+
342+ result := RunArctl (t , workDir ,
343+ "deploy" , "list" ,
344+ "--type" , resourceType ,
345+ "-o" , "json" ,
346+ "--registry-url" , regURL ,
347+ )
348+ RequireSuccess (t , result )
349+
350+ var deployments []struct {
351+ ID string `json:"id"`
352+ ServerName string `json:"serverName"`
353+ ResourceType string `json:"resourceType"`
354+ }
355+ if err := json .Unmarshal ([]byte (result .Stdout ), & deployments ); err != nil {
356+ t .Fatalf ("Failed to parse deploy list JSON: %v\n Output: %s" , err , result .Stdout )
357+ }
358+
359+ for _ , d := range deployments {
360+ if d .ServerName == resourceName && d .ResourceType == resourceType {
361+ return d .ID
362+ }
363+ }
364+ return ""
365+ }
366+
240367// waitForComposeService polls until a container with the given service name in
241368// the agentregistry_runtime compose project is running, or fails after timeout.
242369// Uses docker ps with label filters instead of docker compose ps, because the
@@ -377,7 +504,8 @@ func TestDeleteDeploymentRemovesKubernetesResources(t *testing.T) {
377504
378505 t .Log ("Deploying agent to Kubernetes..." )
379506 result = RunArctl (t , tmpDir ,
380- "agent" , "deploy" , agentName ,
507+ "deploy" , "create" , agentName ,
508+ "--type" , "agent" ,
381509 "--registry-url" , regURL ,
382510 "--provider-id" , "kubernetes-default" ,
383511 "--namespace" , "default" ,
@@ -516,6 +644,39 @@ func escapeSQLLiteral(value string) string {
516644 return strings .ReplaceAll (value , "'" , "''" )
517645}
518646
647+ // verifyDeploymentInList runs "arctl deploy list" with JSON output and asserts
648+ // that a deployment with the given resource name and type appears in the results.
649+ func verifyDeploymentInList (t * testing.T , workDir , regURL , resourceName , resourceType string ) {
650+ t .Helper ()
651+
652+ result := RunArctl (t , workDir ,
653+ "deploy" , "list" ,
654+ "--type" , resourceType ,
655+ "-o" , "json" ,
656+ "--registry-url" , regURL ,
657+ )
658+ RequireSuccess (t , result )
659+
660+ var deployments []struct {
661+ ServerName string `json:"serverName"`
662+ ResourceType string `json:"resourceType"`
663+ Status string `json:"status"`
664+ }
665+ if err := json .Unmarshal ([]byte (result .Stdout ), & deployments ); err != nil {
666+ t .Fatalf ("Failed to parse deploy list JSON output: %v\n Output: %s" , err , result .Stdout )
667+ }
668+
669+ for _ , d := range deployments {
670+ if d .ServerName == resourceName && d .ResourceType == resourceType {
671+ t .Logf ("Found deployment: name=%s type=%s status=%s" , d .ServerName , d .ResourceType , d .Status )
672+ return
673+ }
674+ }
675+
676+ t .Fatalf ("Expected deployment name=%q type=%q not found in deploy list output (%d deployments)" ,
677+ resourceName , resourceType , len (deployments ))
678+ }
679+
519680func dockerContainerRunning (name string ) bool {
520681 ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Second )
521682 defer cancel ()
@@ -843,8 +1004,12 @@ func TestAgentDeployWithPrompts(t *testing.T) {
8431004 RequireSuccess (t , result )
8441005 })
8451006
846- t .Run ("deploy" , func (t * testing.T ) {
847- args := []string {"agent" , "deploy" , agentName , "--registry-url" , regURL }
1007+ t .Run ("deploy_create" , func (t * testing.T ) {
1008+ args := []string {
1009+ "deploy" , "create" , agentName ,
1010+ "--type" , "agent" ,
1011+ "--registry-url" , regURL ,
1012+ }
8481013 args = append (args , target .deplArgs ... )
8491014 result := RunArctl (t , tmpDir , args ... )
8501015 RequireSuccess (t , result )
0 commit comments