Files
wehub-resource-sync db620d33df
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:25 +08:00

255 lines
8.4 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Linq;
using System.Text.Json;
namespace Microsoft.Agents.AI.Foundry.UnitTests;
/// <summary>
/// Tests for <see cref="FoundryEvals"/> internal helpers.
/// </summary>
public sealed class FoundryEvalsTests
{
[Fact]
public void FilterToolEvaluators_AllToolEvaluators_NoTools_ThrowsArgumentException()
{
// All configured evaluators are tool-type, but no items have tools.
var evaluators = new FoundryEvaluatorSpec[] { "tool_call_accuracy", "tool_selection" };
var ex = Assert.Throws<ArgumentException>(
() => FoundryEvals.FilterToolEvaluators(evaluators, hasTools: false));
Assert.Contains("tool definitions", ex.Message);
}
[Fact]
public void FilterToolEvaluators_MixedEvaluators_NoTools_FiltersToolOnes()
{
var evaluators = new FoundryEvaluatorSpec[] { "relevance", "tool_call_accuracy", "coherence" };
var result = FoundryEvals.FilterToolEvaluators(evaluators, hasTools: false);
Assert.Equal(2, result.Length);
Assert.Contains((FoundryEvaluatorSpec)"relevance", result);
Assert.Contains((FoundryEvaluatorSpec)"coherence", result);
Assert.DoesNotContain((FoundryEvaluatorSpec)"tool_call_accuracy", result);
}
[Fact]
public void FilterToolEvaluators_HasTools_ReturnsAllEvaluators()
{
var evaluators = new FoundryEvaluatorSpec[] { "relevance", "tool_call_accuracy" };
var result = FoundryEvals.FilterToolEvaluators(evaluators, hasTools: true);
Assert.Equal(evaluators, result);
}
[Fact]
public void FilterToolEvaluators_PreservesRubricRefs_WhenNoTools()
{
// Rubric refs are tool-aware but never tool-required, so they must survive filtering
// when no items carry tool definitions.
var rubric = new GeneratedEvaluatorRef("policy-rubric", Version: "3");
var evaluators = new FoundryEvaluatorSpec[] { "relevance", rubric, "tool_call_accuracy" };
var result = FoundryEvals.FilterToolEvaluators(evaluators, hasTools: false);
Assert.Equal(2, result.Length);
Assert.Contains((FoundryEvaluatorSpec)"relevance", result);
Assert.Contains(result, s => s.IsRubric && s.GeneratedRef!.Name == "policy-rubric");
Assert.DoesNotContain((FoundryEvaluatorSpec)"tool_call_accuracy", result);
}
// ---------------------------------------------------------------
// FoundryEvals.ParseRubricScores tests
// ---------------------------------------------------------------
[Fact]
public void ParseRubricScores_CanonicalDimensionScoresKey_ParsesAllFields()
{
// Per Microsoft Learn docs, runtime output uses properties.dimension_scores.
const string Json = """
{
"properties": {
"dimension_scores": [
{ "id": "intent_recognition", "score": 5, "applicable": true, "weight": 9, "reason": "Identified correctly." },
{ "id": "general_quality", "score": 4, "applicable": true, "weight": 5, "reason": "Strong overall." }
]
}
}
""";
using var doc = JsonDocument.Parse(Json);
var result = FoundryEvals.ParseRubricScores(doc.RootElement);
Assert.NotNull(result);
Assert.Equal(2, result!.Count);
Assert.Equal(["intent_recognition", "general_quality"], result.Select(r => r.Id));
Assert.Equal([5, 4], result.Select(r => r.Score));
Assert.Equal([9, 5], result.Select(r => r.Weight));
Assert.True(result[0].Applicable);
Assert.Equal("Identified correctly.", result[0].Reason);
}
[Fact]
public void ParseRubricScores_LegacyRubricScoresKey_StillSupported()
{
// Preview builds used the rubric_scores key; we still accept it for back-compat.
const string Json = """
{
"properties": {
"rubric_scores": [
{ "id": "a", "score": 3, "applicable": true, "weight": 1, "reason": "r" }
]
}
}
""";
using var doc = JsonDocument.Parse(Json);
var result = FoundryEvals.ParseRubricScores(doc.RootElement);
Assert.NotNull(result);
Assert.Single(result!);
Assert.Equal("a", result[0].Id);
}
[Fact]
public void ParseRubricScores_TopLevelKey_FallsBack()
{
// Defensive fallback when SDK shape omits the 'properties' wrapper.
const string Json = """
{
"dimension_scores": [
{ "id": "x", "score": 2, "applicable": true, "weight": 1, "reason": "" }
]
}
""";
using var doc = JsonDocument.Parse(Json);
var result = FoundryEvals.ParseRubricScores(doc.RootElement);
Assert.NotNull(result);
Assert.Single(result!);
Assert.Equal("x", result[0].Id);
}
[Fact]
public void ParseRubricScores_NoRubricKeys_ReturnsNull()
{
const string Json = """
{ "properties": { "other_field": [] } }
""";
using var doc = JsonDocument.Parse(Json);
var result = FoundryEvals.ParseRubricScores(doc.RootElement);
Assert.Null(result);
}
[Fact]
public void ParseRubricScores_SkipsMalformedEntries()
{
// Entries missing weight or applicable are skipped, but well-formed siblings are kept.
const string Json = """
{
"properties": {
"dimension_scores": [
{ "id": "good", "score": 3, "applicable": true, "weight": 1, "reason": "ok" },
{ "id": "bad-no-weight", "score": 2, "applicable": true, "reason": "x" },
{ "id": "bad-no-applicable", "score": 2, "weight": 1, "reason": "x" }
]
}
}
""";
using var doc = JsonDocument.Parse(Json);
var result = FoundryEvals.ParseRubricScores(doc.RootElement);
Assert.NotNull(result);
Assert.Single(result!);
Assert.Equal("good", result[0].Id);
}
[Fact]
public void ParseRubricScores_NonApplicableDimension_KeepsNullScoreWhenMissing()
{
// Non-applicable dimensions can legitimately omit score (or set it to null).
const string Json = """
{
"properties": {
"dimension_scores": [
{ "id": "skipped", "applicable": false, "weight": 5, "reason": "n/a" }
]
}
}
""";
using var doc = JsonDocument.Parse(Json);
var result = FoundryEvals.ParseRubricScores(doc.RootElement);
Assert.NotNull(result);
Assert.Single(result!);
Assert.Equal("skipped", result[0].Id);
Assert.False(result[0].Applicable);
Assert.Null(result[0].Score);
}
// ---------------------------------------------------------------
// FoundryEvaluatorSpec validation tests
// ---------------------------------------------------------------
[Fact]
public void FoundryEvaluatorSpec_Default_IsNotValid()
{
var spec = default(FoundryEvaluatorSpec);
Assert.False(spec.IsValid);
Assert.Null(spec.BuiltinName);
Assert.Null(spec.GeneratedRef);
}
[Fact]
public void FoundryEvaluatorSpec_EnsureValid_DefaultThrows()
{
var spec = default(FoundryEvaluatorSpec);
var ex = Assert.Throws<ArgumentException>(() => spec.EnsureValid("evaluators"));
Assert.Equal("evaluators", ex.ParamName);
}
[Fact]
public void FoundryEvaluatorSpec_EnsureValid_BuiltinPasses()
{
var spec = (FoundryEvaluatorSpec)"relevance";
spec.EnsureValid(); // does not throw
}
[Fact]
public void FoundryEvaluatorSpec_EnsureValid_RubricPasses()
{
var spec = (FoundryEvaluatorSpec)new GeneratedEvaluatorRef("r", "1");
spec.EnsureValid(); // does not throw
}
[Fact]
public void EnsureAllSpecsValid_DefaultEntry_ThrowsWithParamName()
{
var specs = new FoundryEvaluatorSpec[] { "relevance", default };
var ex = Assert.Throws<ArgumentException>(
() => FoundryEvals.EnsureAllSpecsValid(specs, "evaluators"));
Assert.Equal("evaluators", ex.ParamName);
Assert.Contains("index 1", ex.Message);
}
[Fact]
public void EnsureAllSpecsValid_AllValid_DoesNotThrow()
{
var specs = new FoundryEvaluatorSpec[]
{
"relevance",
new GeneratedEvaluatorRef("policy", "1"),
};
FoundryEvals.EnsureAllSpecsValid(specs, "evaluators");
}
}