diff --git a/cmd/ateapi/internal/controlapi/update_actor.go b/cmd/ateapi/internal/controlapi/update_actor.go index 987a2ca86..28d756267 100644 --- a/cmd/ateapi/internal/controlapi/update_actor.go +++ b/cmd/ateapi/internal/controlapi/update_actor.go @@ -32,6 +32,7 @@ func (s *Service) UpdateActor(ctx context.Context, req *ateapipb.UpdateActorRequ return nil, toGRPCStatusError(errs) } actorRef := resources.ActorRefFromObjectRef(req.GetActor()) + setSpanActorRefAttributes(ctx, actorRef) actor, err := s.persistence.GetActor(ctx, actorRef) if err != nil { @@ -50,6 +51,7 @@ func (s *Service) UpdateActor(ctx context.Context, req *ateapipb.UpdateActorRequ return nil, fmt.Errorf("while updating actor: %w", err) } + setSpanActorAttributes(ctx, updated) return &ateapipb.UpdateActorResponse{Actor: updated}, nil } diff --git a/cmd/ateapi/internal/controlapi/update_actor_test.go b/cmd/ateapi/internal/controlapi/update_actor_test.go index a24a2ff57..0f890d43e 100644 --- a/cmd/ateapi/internal/controlapi/update_actor_test.go +++ b/cmd/ateapi/internal/controlapi/update_actor_test.go @@ -15,10 +15,16 @@ package controlapi import ( + "context" "testing" - "github.com/agent-substrate/substrate/pkg/proto/ateapipb" + "go.opentelemetry.io/otel/attribute" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/agent-substrate/substrate/internal/ateattr" + "github.com/agent-substrate/substrate/pkg/proto/ateapipb" ) func TestValidateUpdateActorRequest(t *testing.T) { @@ -89,3 +95,64 @@ func TestValidateUpdateActorRequest(t *testing.T) { }) } } + +func TestUpdateActor_StampsFullSpanIdentity(t *testing.T) { + ns := namespaceForTest("ns-span-update") + tc := setupTest(t, ns) + defer tc.cleanup() + createTemplate(t, tc, ns) + + if _, err := tc.service.CreateActor(context.Background(), &ateapipb.CreateActorRequest{ + Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: testActorID}, + ActorTemplateNamespace: ns, + ActorTemplateName: "tmpl1", + }, + }); err != nil { + t.Fatalf("seed CreateActor: %v", err) + } + + attrs := recordRootSpanAttrs(t, func(ctx context.Context) { + if _, err := tc.service.UpdateActor(ctx, &ateapipb.UpdateActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: testActorID}, + WorkerSelector: &ateapipb.Selector{ + MatchLabels: map[string]string{"env": "prod"}, + }, + }); err != nil { + t.Fatalf("UpdateActor: %v", err) + } + }) + + assertSpanStr(t, attrs, ateattr.AtespaceKey, testAtespace) + assertSpanStr(t, attrs, ateattr.ActorNameKey, testActorID) + assertSpanStr(t, attrs, ateattr.TemplateNameKey, "tmpl1") + assertSpanStr(t, attrs, ateattr.TemplateNamespaceKey, ns) + if v, ok := attrs[ateattr.ActorUIDKey]; !ok || v.Type() != attribute.STRING || v.AsString() == "" { + t.Errorf("%s = %v, want non-empty server-assigned uid", ateattr.ActorUIDKey, v.Emit()) + } + if v, ok := attrs[ateattr.ActorVersionKey]; !ok || v.Type() != attribute.INT64 || v.AsInt64() != 2 { + t.Errorf("%s = %v, want int64 2 (updated version)", ateattr.ActorVersionKey, v.Emit()) + } +} + +func TestUpdateActor_FailedLookupStampsRefIdentityOnly(t *testing.T) { + ns := namespaceForTest("ns-span-update-err") + tc := setupTest(t, ns) + defer tc.cleanup() + + attrs := recordRootSpanAttrs(t, func(ctx context.Context) { + if _, err := tc.service.UpdateActor(ctx, &ateapipb.UpdateActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: testActorID}, + }); status.Code(err) != codes.NotFound { + t.Fatalf("UpdateActor(missing) error = %v, want code NotFound", err) + } + }) + + assertSpanStr(t, attrs, ateattr.AtespaceKey, testAtespace) + assertSpanStr(t, attrs, ateattr.ActorNameKey, testActorID) + for _, k := range []attribute.Key{ateattr.ActorUIDKey, ateattr.TemplateNameKey, ateattr.TemplateNamespaceKey, ateattr.ActorVersionKey} { + if _, ok := attrs[k]; ok { + t.Errorf("unexpected %s on failed-update span", k) + } + } +}