From bf50ac021a7f60d6ea4d14945494c3de317df440 Mon Sep 17 00:00:00 2001 From: huimiu Date: Wed, 22 Jul 2026 17:44:19 +0800 Subject: [PATCH] fix: validate unified agent definitions in doctor --- .../internal/cmd/doctor/checks_local.go | 7 +- .../internal/cmd/doctor/checks_local_test.go | 2 +- .../internal/cmd/doctor/checks_manual_env.go | 5 +- .../cmd/doctor/checks_manual_env_test.go | 2 +- .../internal/cmd/doctor/checks_project.go | 113 +++++----- .../cmd/doctor/checks_project_test.go | 205 +++++++++++++++--- .../internal/cmd/doctor_format_test.go | 8 +- 7 files changed, 236 insertions(+), 106 deletions(-) diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_local.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_local.go index 27e89140d7a..05f10440b32 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_local.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_local.go @@ -157,8 +157,9 @@ type Dependencies struct { // NewLocalChecks returns the canonical sequence of local doctor checks // in execution order. Phase 4.2 covered checks 1-3; Phase 4.3 added -// checks 4-6 (agent service detected, project endpoint set, agent.yaml -// valid). Phase 5 C9 appends check 7 (manual env vars set). Phase 5 +// checks 4-6 (agent service detected, project endpoint set, agent +// definition valid). Phase 5 C9 appends the manual env check. +// Phase 5 // C14 appends check 8 (`local.toolboxes`) which reads per-toolbox MCP // endpoint env vars; it is local because it does not call ARM / // Foundry (only the active azd environment). @@ -169,7 +170,7 @@ func NewLocalChecks(deps Dependencies) []Check { newCheckEnvironmentSelected(deps), newCheckAgentServiceDetected(deps), newCheckProjectEndpointSet(deps), - newCheckAgentYAMLValid(deps), + newCheckAgentDefinitionValid(deps), newCheckManualEnvVars(deps), newCheckToolboxes(deps), } diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_local_test.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_local_test.go index 69a5a6f9fce..3a05a0bf529 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_local_test.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_local_test.go @@ -455,7 +455,7 @@ func TestNewLocalChecks_OrderAndIDs(t *testing.T) { {"local.environment-selected", "azd environment selected", false}, {"local.agent-service-detected", "agent service in azure.yaml", false}, {"local.project-endpoint-set", "FOUNDRY_PROJECT_ENDPOINT set", false}, - {"local.agent-yaml-valid", "agent.yaml valid (per service)", false}, + {"local.agent-yaml-valid", "agent definition valid (per service)", false}, {"local.manual-env-vars", "manual env vars set", false}, {"local.toolboxes", "Manifest toolboxes have endpoint env vars set", false}, } diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_manual_env.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_manual_env.go index 7a26416ee6e..d21d0d9408e 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_manual_env.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_manual_env.go @@ -74,7 +74,10 @@ func newCheckManualEnvVars(deps Dependencies) Check { return Result{Status: StatusSkip, Message: "skipped: azd extension not reachable"} } if priorBlocked(prior, "local.agent-yaml-valid") { - return Result{Status: StatusSkip, Message: "skipped: agent.yaml check failed or skipped"} + return Result{ + Status: StatusSkip, + Message: "skipped: agent definition check failed or skipped", + } } if priorBlocked(prior, "local.environment-selected") { // Without an azd env, AssembleState's detectMissingVars diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_manual_env_test.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_manual_env_test.go index a3cfc386071..c3c758a95e8 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_manual_env_test.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_manual_env_test.go @@ -62,7 +62,7 @@ func TestCheckManualEnvVars_PriorAgentYAMLFailed_Skips(t *testing.T) { got := check.Fn(t.Context(), Options{}, prior) require.Equal(t, StatusSkip, got.Status) - require.Contains(t, got.Message, "agent.yaml check failed") + require.Contains(t, got.Message, "agent definition check failed") } func TestCheckManualEnvVars_PriorAgentYAMLSkipped_AlsoSkips(t *testing.T) { diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project.go index b3058d1a922..4e7e9dbbc3a 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project.go @@ -6,12 +6,11 @@ package doctor import ( "context" "fmt" - "os" + "slices" "sort" "strings" - "azureaiagent/internal/pkg/agents/agent_yaml" - "azureaiagent/internal/pkg/paths" + "azureaiagent/internal/project" "github.com/azure/azure-dev/cli/azd/pkg/azdext" ) @@ -154,20 +153,16 @@ func newCheckProjectEndpointSet(deps Dependencies) Check { } } -// newCheckAgentYAMLValid produces Check `local.agent-yaml-valid`. For -// each agent service in azure.yaml, it reads `//agent.yaml` -// and parses it as `agent_yaml.ContainerAgent`. Fails when any service's -// file is missing, unreadable, or fails to parse — collecting all errors -// rather than short-circuiting so multi-service projects get a single -// actionable report. +// newCheckAgentDefinitionValid produces Check +// `local.agent-yaml-valid`. It resolves each agent definition through +// the same path used by run and deploy. // // Skips when the gRPC client is unavailable or when -// `local.agent-service-detected` failed (no services to validate). The -// suggestion mirrors the spec's "fix YAML" guidance. -func newCheckAgentYAMLValid(deps Dependencies) Check { +// `local.agent-service-detected` failed (no services to validate). +func newCheckAgentDefinitionValid(deps Dependencies) Check { return Check{ ID: "local.agent-yaml-valid", - Name: "agent.yaml valid (per service)", + Name: "agent definition valid (per service)", Fn: func(ctx context.Context, _ Options, prior []Result) Result { if deps.AzdClient == nil { return Result{Status: StatusSkip, Message: "skipped: azd extension not reachable"} @@ -196,80 +191,74 @@ func newCheckAgentYAMLValid(deps Dependencies) Check { } } - projectPath := resp.Project.Path - // Collect agent service entries in a stable order. protobuf - // `Services` is a map, so iteration order is non-deterministic - // — sorting by service name keeps the failure list (and the - // validatedPaths Detail) reproducible. - type agentSvc struct { - name string - rel string - } - var agents []agentSvc - for _, s := range resp.Project.Services { - if s == nil || s.Host != agentHost { - continue - } - agents = append(agents, agentSvc{name: s.Name, rel: s.RelativePath}) - } - sort.Slice(agents, func(i, j int) bool { return agents[i].name < agents[j].name }) - - var validatedPaths []string + agents := collectSortedAgentServices(resp.Project.Services) + validatedServices := make([]string, 0, len(agents)) var failures []string - for _, a := range agents { - yamlPath, err := paths.JoinAllowRoot(projectPath, a.rel, "agent.yaml") + for _, svc := range agents { + _, _, _, err := project.LoadAgentDefinition( + svc, + resp.Project.Path, + ) if err != nil { - failures = append(failures, fmt.Sprintf("%s: %v", a.name, err)) - continue - } - if pathErr := validateAgentYAML(yamlPath); pathErr != nil { - failures = append(failures, fmt.Sprintf("%s: %v", a.name, pathErr)) + failures = append( + failures, + fmt.Sprintf("%s: %v", svc.Name, err), + ) continue } - validatedPaths = append(validatedPaths, yamlPath) + + validatedServices = append(validatedServices, svc.Name) } if len(failures) > 0 { return Result{ Status: StatusFail, Message: fmt.Sprintf( - "agent.yaml validation failed for %d service(s): %s", + "agent definition validation failed for %d service(s): %s", len(failures), strings.Join(failures, "; ")), - Suggestion: "Fix the YAML syntax or ensure agent.yaml exists in each service directory.", + Suggestion: "Fix the agent definition at its source " + + "(azure.yaml, a referenced file, or a legacy " + + "agent.yaml/agent.yml), or re-run " + + "`azd ai agent init`.", Details: map[string]any{ - "failures": failures, - "validatedPaths": validatedPaths, + "failures": failures, + "validatedServices": validatedServices, }, } } return Result{ - Status: StatusPass, - Message: fmt.Sprintf("agent.yaml valid for %d service(s)", len(validatedPaths)), + Status: StatusPass, + Message: fmt.Sprintf( + "agent definition valid for %d service(s)", + len(validatedServices), + ), Details: map[string]any{ - "validatedPaths": validatedPaths, + "validatedServices": validatedServices, }, } }, } } -// validateAgentYAML reads the file at path and runs the same validation -// (`agent_yaml.ValidateAgentDefinition`) that the deploy path uses, so a -// PASS here implies the manifest will not be rejected by deploy for any -// of: missing/invalid `kind`, missing/invalid `name`, or kind-specific -// structural problems. Returns the underlying read/validate error -// verbatim so the caller can attribute it to the offending service. -func validateAgentYAML(path string) error { - //nolint:gosec // path is validated under the project root before this helper is called. - data, err := os.ReadFile(path) - if err != nil { - return fmt.Errorf("read %s: %w", path, err) - } - if err := agent_yaml.ValidateAgentDefinition(data); err != nil { - return fmt.Errorf("validate %s: %w", path, err) +func collectSortedAgentServices( + services map[string]*azdext.ServiceConfig, +) []*azdext.ServiceConfig { + var agents []*azdext.ServiceConfig + for _, svc := range services { + if svc == nil || svc.Host != agentHost { + continue + } + agents = append(agents, svc) } - return nil + sortAgentServices(agents) + return agents +} + +func sortAgentServices(services []*azdext.ServiceConfig) { + slices.SortFunc(services, func(a, b *azdext.ServiceConfig) int { + return strings.Compare(a.Name, b.Name) + }) } // priorBlocked reports whether the prior results contain a Fail or Skip diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project_test.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project_test.go index abb41d5864c..774689c1c82 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project_test.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor/checks_project_test.go @@ -7,12 +7,14 @@ import ( "errors" "os" "path/filepath" + "strings" "testing" "github.com/azure/azure-dev/cli/azd/pkg/azdext" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/structpb" ) // ---- Check `local.agent-service-detected` ---- @@ -287,20 +289,20 @@ func TestCheckProjectEndpointSet_ValidValue_Passes(t *testing.T) { // ---- Check `local.agent-yaml-valid` ---- -func TestCheckAgentYAMLValid_NoClient_Skips(t *testing.T) { +func TestCheckAgentDefinitionValid_NoClient_Skips(t *testing.T) { t.Parallel() - check := newCheckAgentYAMLValid(Dependencies{AzdClient: nil}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: nil}) got := check.Fn(t.Context(), Options{}, nil) require.Equal(t, StatusSkip, got.Status) } -func TestCheckAgentYAMLValid_PriorAgentDetectionFailed_Skips(t *testing.T) { +func TestCheckAgentDefinitionValid_PriorAgentDetectionFailed_Skips(t *testing.T) { t.Parallel() client := newTestAzdClient(t, &fakeProjectServer{}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) prior := []Result{{ID: "local.agent-service-detected", Status: StatusFail}} got := check.Fn(t.Context(), Options{}, prior) @@ -309,7 +311,7 @@ func TestCheckAgentYAMLValid_PriorAgentDetectionFailed_Skips(t *testing.T) { require.Contains(t, got.Message, "no agent services detected") } -func TestCheckAgentYAMLValid_PriorAgentDetectionSkipped_AlsoSkips(t *testing.T) { +func TestCheckAgentDefinitionValid_PriorAgentDetectionSkipped_AlsoSkips(t *testing.T) { // Covers the cascade: azure-yaml fails -> agent-service-detected skips -> // agent-yaml-valid must also skip. Without this propagation, check 6 // would re-fetch the project (failing identically to check 2) and @@ -320,7 +322,7 @@ func TestCheckAgentYAMLValid_PriorAgentDetectionSkipped_AlsoSkips(t *testing.T) // Server set up to fail if reached, to ensure the guard short-circuits. &fakeProjectServer{err: errors.New("should not be called")}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) prior := []Result{{ID: "local.agent-service-detected", Status: StatusSkip}} got := check.Fn(t.Context(), Options{}, prior) @@ -329,13 +331,13 @@ func TestCheckAgentYAMLValid_PriorAgentDetectionSkipped_AlsoSkips(t *testing.T) require.Contains(t, got.Message, "no agent services detected or upstream check blocked") } -func TestCheckAgentYAMLValid_GRPCError_Fails(t *testing.T) { +func TestCheckAgentDefinitionValid_GRPCError_Fails(t *testing.T) { t.Parallel() client := newTestAzdClient(t, &fakeProjectServer{err: errors.New("rpc boom")}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) got := check.Fn(t.Context(), Options{}, nil) @@ -343,13 +345,13 @@ func TestCheckAgentYAMLValid_GRPCError_Fails(t *testing.T) { require.Contains(t, got.Message, "failed to get project config") } -func TestCheckAgentYAMLValid_TransportError_SwapsSuggestion(t *testing.T) { +func TestCheckAgentDefinitionValid_TransportError_SwapsSuggestion(t *testing.T) { t.Parallel() client := newTestAzdClient(t, &fakeProjectServer{err: status.Error(codes.Unavailable, "transport boom")}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) got := check.Fn(t.Context(), Options{}, nil) @@ -357,7 +359,7 @@ func TestCheckAgentYAMLValid_TransportError_SwapsSuggestion(t *testing.T) { require.Contains(t, got.Suggestion, "gRPC channel") } -func TestCheckAgentYAMLValid_OneServiceValid_Passes(t *testing.T) { +func TestCheckAgentDefinitionValid_OneServiceValid_Passes(t *testing.T) { t.Parallel() projectPath := t.TempDir() @@ -382,15 +384,96 @@ protocols: }, }}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) got := check.Fn(t.Context(), Options{}, nil) require.Equal(t, StatusPass, got.Status) - require.Contains(t, got.Message, "agent.yaml valid for 1 service(s)") + require.Contains(t, got.Message, "agent definition valid for 1 service(s)") + validated, ok := got.Details["validatedServices"].([]string) + require.True(t, ok) + require.Equal(t, []string{"echo-agent"}, validated) +} + +func TestCheckAgentDefinitionValid_InlineWithoutFile_Passes( + t *testing.T, +) { + t.Parallel() + + props, err := structpb.NewStruct(map[string]any{ + "kind": "hosted", + "name": "echo-agent", + }) + require.NoError(t, err) + + client := newTestAzdClient(t, + &fakeProjectServer{resp: &azdext.GetProjectResponse{ + Project: &azdext.ProjectConfig{ + Path: t.TempDir(), + Services: map[string]*azdext.ServiceConfig{ + "echo-agent": { + Name: "echo-agent", + Host: agentHost, + RelativePath: "src/agent", + AdditionalProperties: props, + }, + }, + }, + }}, + &fakeEnvironmentServer{}) + check := newCheckAgentDefinitionValid( + Dependencies{AzdClient: client}, + ) + + got := check.Fn(t.Context(), Options{}, nil) + + require.Equal(t, StatusPass, got.Status) + require.Contains(t, got.Message, "agent definition valid") } -func TestCheckAgentYAMLValid_NonAgentServicesIgnored(t *testing.T) { +func TestCheckAgentDefinitionValid_InlineWinsOverStaleFile( + t *testing.T, +) { + t.Parallel() + + projectPath := t.TempDir() + writeYAML( + t, + projectPath, + "src/agent/agent.yaml", + "name: broken\n bad-indent: oops\n", + ) + props, err := structpb.NewStruct(map[string]any{ + "kind": "hosted", + "name": "echo-agent", + }) + require.NoError(t, err) + + client := newTestAzdClient(t, + &fakeProjectServer{resp: &azdext.GetProjectResponse{ + Project: &azdext.ProjectConfig{ + Path: projectPath, + Services: map[string]*azdext.ServiceConfig{ + "echo-agent": { + Name: "echo-agent", + Host: agentHost, + RelativePath: "src/agent", + AdditionalProperties: props, + }, + }, + }, + }}, + &fakeEnvironmentServer{}) + check := newCheckAgentDefinitionValid( + Dependencies{AzdClient: client}, + ) + + got := check.Fn(t.Context(), Options{}, nil) + + require.Equal(t, StatusPass, got.Status) +} + +func TestCheckAgentDefinitionValid_NonAgentServicesIgnored(t *testing.T) { t.Parallel() projectPath := t.TempDir() @@ -408,18 +491,17 @@ func TestCheckAgentYAMLValid_NonAgentServicesIgnored(t *testing.T) { }, }}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) got := check.Fn(t.Context(), Options{}, nil) - require.Equal(t, StatusPass, got.Status, "api service has no agent.yaml — must be skipped, not failed") - paths, ok := got.Details["validatedPaths"].([]string) + require.Equal(t, StatusPass, got.Status) + validated, ok := got.Details["validatedServices"].([]string) require.True(t, ok) - require.Len(t, paths, 1) - require.Contains(t, paths[0], "src"+string(filepath.Separator)+"agent") + require.Equal(t, []string{"echo-agent"}, validated) } -func TestCheckAgentYAMLValid_MissingFile_Fails(t *testing.T) { +func TestCheckAgentDefinitionValid_MissingDefinition_Fails(t *testing.T) { t.Parallel() projectPath := t.TempDir() @@ -435,19 +517,19 @@ func TestCheckAgentYAMLValid_MissingFile_Fails(t *testing.T) { }, }}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) got := check.Fn(t.Context(), Options{}, nil) require.Equal(t, StatusFail, got.Status) require.Contains(t, got.Message, "echo-agent") - require.Contains(t, got.Suggestion, "Fix the YAML") + require.Contains(t, got.Suggestion, "azure.yaml") failures, ok := got.Details["failures"].([]string) require.True(t, ok) require.Len(t, failures, 1) } -func TestCheckAgentYAMLValid_MalformedYAML_Fails(t *testing.T) { +func TestCheckAgentDefinitionValid_MalformedLegacyYAML_Fails(t *testing.T) { t.Parallel() projectPath := t.TempDir() @@ -464,7 +546,7 @@ func TestCheckAgentYAMLValid_MalformedYAML_Fails(t *testing.T) { }, }}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) got := check.Fn(t.Context(), Options{}, nil) @@ -475,7 +557,7 @@ func TestCheckAgentYAMLValid_MalformedYAML_Fails(t *testing.T) { require.Len(t, failures, 1) } -func TestCheckAgentYAMLValid_MixedValidAndInvalid_Fails(t *testing.T) { +func TestCheckAgentDefinitionValid_MixedValidAndInvalid_Fails(t *testing.T) { t.Parallel() projectPath := t.TempDir() @@ -496,7 +578,7 @@ func TestCheckAgentYAMLValid_MixedValidAndInvalid_Fails(t *testing.T) { }, }}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) got := check.Fn(t.Context(), Options{}, nil) @@ -509,9 +591,64 @@ func TestCheckAgentYAMLValid_MixedValidAndInvalid_Fails(t *testing.T) { require.True(t, ok) require.Len(t, failures, 1) - validated, ok := got.Details["validatedPaths"].([]string) + validated, ok := got.Details["validatedServices"].([]string) require.True(t, ok) - require.Len(t, validated, 1) + require.Equal(t, []string{"ok-agent"}, validated) +} + +func TestCheckAgentDefinitionValid_MultipleFailures_Aggregates( + t *testing.T, +) { + t.Parallel() + + client := newTestAzdClient(t, + &fakeProjectServer{resp: &azdext.GetProjectResponse{ + Project: &azdext.ProjectConfig{ + Path: t.TempDir(), + Services: map[string]*azdext.ServiceConfig{ + "agent-b": { + Name: "agent-b", + Host: agentHost, + RelativePath: "src/b", + }, + "agent-a": { + Name: "agent-a", + Host: agentHost, + RelativePath: "src/a", + }, + }, + }, + }}, + &fakeEnvironmentServer{}) + check := newCheckAgentDefinitionValid( + Dependencies{AzdClient: client}, + ) + + got := check.Fn(t.Context(), Options{}, nil) + + require.Equal(t, StatusFail, got.Status) + failures, ok := got.Details["failures"].([]string) + require.True(t, ok) + require.Len(t, failures, 2) + require.Contains(t, strings.Join(failures, "\n"), "agent-a:") + require.Contains(t, strings.Join(failures, "\n"), "agent-b:") +} + +func TestSortAgentServices(t *testing.T) { + t.Parallel() + + services := []*azdext.ServiceConfig{ + {Name: "agent-b", Host: agentHost}, + {Name: "agent-a", Host: agentHost}, + } + + sortAgentServices(services) + + require.Equal( + t, + []string{"agent-a", "agent-b"}, + []string{services[0].Name, services[1].Name}, + ) } // ---- helper: priorBlocked ---- @@ -552,7 +689,7 @@ func TestPriorBlocked(t *testing.T) { } } -func TestCheckAgentYAMLValid_MissingKind_Fails(t *testing.T) { +func TestCheckAgentDefinitionValid_MissingKind_Fails(t *testing.T) { // Without explicit `kind:`, ValidateAgentDefinition rejects the manifest // because kind is required. Doctor must catch this pre-flight rather // than letting deploy be the first place that surfaces it. @@ -571,7 +708,7 @@ func TestCheckAgentYAMLValid_MissingKind_Fails(t *testing.T) { }, }}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) got := check.Fn(t.Context(), Options{}, nil) @@ -583,7 +720,7 @@ func TestCheckAgentYAMLValid_MissingKind_Fails(t *testing.T) { require.Contains(t, failures[0], "kind") } -func TestCheckAgentYAMLValid_InvalidKind_Fails(t *testing.T) { +func TestCheckAgentDefinitionValid_InvalidKind_Fails(t *testing.T) { // A `kind` that isn't in ValidAgentKinds() (hosted/workflow) must be // rejected. Bare yaml.Unmarshal would silently accept this; the // production deploy path rejects it via ValidateAgentDefinition. @@ -602,7 +739,7 @@ func TestCheckAgentYAMLValid_InvalidKind_Fails(t *testing.T) { }, }}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) got := check.Fn(t.Context(), Options{}, nil) @@ -613,7 +750,7 @@ func TestCheckAgentYAMLValid_InvalidKind_Fails(t *testing.T) { require.Contains(t, failures[0], "kind") } -func TestCheckAgentYAMLValid_InvalidName_Fails(t *testing.T) { +func TestCheckAgentDefinitionValid_InvalidName_Fails(t *testing.T) { // Agent name must match `^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$` // (DNS-style). An underscore is invalid for deployable agent names. // Doctor must surface this before deploy, not after. @@ -632,7 +769,7 @@ func TestCheckAgentYAMLValid_InvalidName_Fails(t *testing.T) { }, }}, &fakeEnvironmentServer{}) - check := newCheckAgentYAMLValid(Dependencies{AzdClient: client}) + check := newCheckAgentDefinitionValid(Dependencies{AzdClient: client}) got := check.Fn(t.Context(), Options{}, nil) diff --git a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor_format_test.go b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor_format_test.go index 36393b280e5..bb92cd30eba 100644 --- a/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor_format_test.go +++ b/cli/azd/extensions/azure.ai.agents/internal/cmd/doctor_format_test.go @@ -403,10 +403,10 @@ func TestCapitalize(t *testing.T) { "1 of 1 agents": "1 of 1 agents", "`azure.ai.agent`": "`azure.ai.agent`", // Brand-name leads stay lowercase. - "skipped: upstream blocked": "skipped: upstream blocked", - "azd extension reachable": "azd extension reachable", - "azure.yaml parsed": "azure.yaml parsed", - "agent.yaml valid for service": "agent.yaml valid for service", + "skipped: upstream blocked": "skipped: upstream blocked", + "azd extension reachable": "azd extension reachable", + "azure.yaml parsed": "azure.yaml parsed", + "agent definition valid for service": "Agent definition valid for service", // Generic lowercase leads do get capitalized. "cancelled by user": "Cancelled by user", "no manual env vars are missing": "No manual env vars are missing",