diff --git a/cmd/ateapi/internal/controlapi/functional_test.go b/cmd/ateapi/internal/controlapi/functional_test.go index e95a65589..1ee6572d7 100644 --- a/cmd/ateapi/internal/controlapi/functional_test.go +++ b/cmd/ateapi/internal/controlapi/functional_test.go @@ -1569,7 +1569,7 @@ func TestSuspendActor(t *testing.T) { LatestSnapshotInfo: &ateapipb.SnapshotInfo{ Data: &ateapipb.SnapshotInfo_External{ External: &ateapipb.ExternalSnapshotInfo{ - SnapshotUriPrefix: fmt.Sprintf("gs://fake-fake-fake/%s/", name), + SnapshotUriPrefix: "gs://fake-fake-fake/snapshots/", }, }, }, diff --git a/cmd/ateapi/internal/controlapi/workflow_suspend.go b/cmd/ateapi/internal/controlapi/workflow_suspend.go index 706c97891..1bc9b0151 100644 --- a/cmd/ateapi/internal/controlapi/workflow_suspend.go +++ b/cmd/ateapi/internal/controlapi/workflow_suspend.go @@ -94,7 +94,7 @@ func (s *MarkSuspendingStep) CheckPrerequisite(ctx context.Context, input *Suspe func (s *MarkSuspendingStep) Execute(ctx context.Context, input *SuspendInput, state *SuspendState) error { state.Actor.Status = ateapipb.Actor_STATUS_SUSPENDING snapshotID := time.Now().Format(time.RFC3339) + "-" + rand.Text() - state.Actor.InProgressSnapshot = strings.TrimSuffix(state.ActorTemplate.Spec.SnapshotsConfig.Location, "/") + "/" + input.ActorName + "/" + snapshotID + state.Actor.InProgressSnapshot = strings.TrimSuffix(state.ActorTemplate.Spec.SnapshotsConfig.Location, "/") + "/snapshots/" + snapshotID updatedActor, err := s.store.UpdateActor(ctx, state.Actor, state.Actor.GetMetadata().GetVersion()) if err != nil { return err diff --git a/cmd/ateapi/internal/controlapi/workflow_suspend_test.go b/cmd/ateapi/internal/controlapi/workflow_suspend_test.go index 387087d83..64d9f3681 100644 --- a/cmd/ateapi/internal/controlapi/workflow_suspend_test.go +++ b/cmd/ateapi/internal/controlapi/workflow_suspend_test.go @@ -16,11 +16,13 @@ package controlapi import ( "context" + "strings" "testing" "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" "github.com/agent-substrate/substrate/cmd/ateapi/internal/store/ateredis" "github.com/agent-substrate/substrate/cmd/ateapi/internal/store/storetest" + atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1" "github.com/agent-substrate/substrate/pkg/proto/ateapipb" "github.com/alicebob/miniredis/v2" "github.com/redis/go-redis/v9" @@ -29,6 +31,36 @@ import ( "k8s.io/client-go/tools/cache" ) +func TestMarkSuspendingStep_SnapshotLocation(t *testing.T) { + ctx := context.Background() + persistence := newTestPersistence(t) + actor, err := persistence.CreateActor(ctx, &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "actor-1"}, + Status: ateapipb.Actor_STATUS_RUNNING, + }) + if err != nil { + t.Fatalf("CreateActor: %v", err) + } + state := &SuspendState{ + Actor: actor, + ActorTemplate: &atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{ + SnapshotsConfig: atev1alpha1.SnapshotsConfig{Location: "gs://bucket/root/"}, + }}, + } + if err := (&MarkSuspendingStep{store: persistence}).Execute(ctx, &SuspendInput{Atespace: "team-a", ActorName: "actor-1"}, state); err != nil { + t.Fatalf("Execute: %v", err) + } + + const prefix = "gs://bucket/root/snapshots/" + snapshotID, ok := strings.CutPrefix(state.Actor.GetInProgressSnapshot(), prefix) + if !ok { + t.Fatalf("snapshot location = %q, want prefix %q", state.Actor.GetInProgressSnapshot(), prefix) + } + if snapshotID == "" { + t.Fatal("snapshot ID is empty") + } +} + // TestSuspendActorWorkflow_RejectedAndIdempotentPaths covers the two // short-circuit paths of the suspend workflow: rejection by // MarkSuspendingStep's CheckPrerequisite and the IsComplete idempotent