mirror of
https://github.com/dotnet/skills.git
synced 2026-09-20 09:49:54 +08:00
b92dde1854
Reorganize the skill-validator project from layer-based grouping (Commands/, Services/, Models/, Utilities/) to feature-based slices (Check/, Evaluate/, Consolidate/, Shared/). Key changes: - Check/: CheckCommand, SkillProfiler, AgentProfiler, PluginValidator, ExternalDependencyChecker, ReferenceScanner - Evaluate/: EvaluateCommand, RejudgeCommand (now a subcommand of evaluate), AgentRunner, Judge, PairwiseJudge, and other eval services - Consolidate/: ConsolidateCommand - Shared/: Models, SkillDiscovery, PluginParser (extracted from PluginValidator), Reporter, and utilities Split PluginValidator: parsing helpers (ParsePluginJson, TryGetSafeSubdirectory) moved to Shared/PluginParser.cs; validation logic stays in Check/PluginValidator.cs. Move RawFrontmatter and RawAgentFrontmatter types from EvalSchema into SkillDiscovery since they are only consumed by discovery. Wire RejudgeCommand as subcommand: skill-validator evaluate rejudge. Co-authored-by: Viktor Hofer <vihofer@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
115 lines
3.9 KiB
C#
115 lines
3.9 KiB
C#
using System.Text.Json;
|
|
using SkillValidator.Evaluate;
|
|
using SkillValidator.Shared;
|
|
|
|
namespace SkillValidator.Tests;
|
|
|
|
public class ParseJudgeResponseTests
|
|
{
|
|
private static readonly string ValidJson = JsonSerializer.Serialize(new
|
|
{
|
|
rubric_scores = new[]
|
|
{
|
|
new { criterion = "Correctness", score = 4, reasoning = "Mostly correct" },
|
|
},
|
|
overall_score = 4,
|
|
overall_reasoning = "Good work overall",
|
|
});
|
|
|
|
[Fact]
|
|
public void ParsesValidJsonInCodeBlock()
|
|
{
|
|
var content = "```json\n" + ValidJson + "\n```";
|
|
var result = Judge.ParseJudgeResponse(content, ["Correctness"]);
|
|
Assert.Equal(4, result.OverallScore);
|
|
Assert.Single(result.RubricScores);
|
|
Assert.Equal(4, result.RubricScores[0].Score);
|
|
}
|
|
|
|
[Fact]
|
|
public void ParsesValidJsonWithoutCodeBlock()
|
|
{
|
|
var content = "Here is my evaluation:\n" + ValidJson;
|
|
var result = Judge.ParseJudgeResponse(content, ["Correctness"]);
|
|
Assert.Equal(4, result.OverallScore);
|
|
}
|
|
|
|
[Fact]
|
|
public void HandlesInvalidEscapeSequences()
|
|
{
|
|
var raw = """
|
|
{
|
|
"rubric_scores": [
|
|
{"criterion": "Quality", "score": 5, "reasoning": "It\'s excellent and has \a great structure"}
|
|
],
|
|
"overall_score": 5,
|
|
"overall_reasoning": "The agent\'s work is outstanding"
|
|
}
|
|
""";
|
|
Assert.ThrowsAny<JsonException>(() => JsonDocument.Parse(raw));
|
|
|
|
var result = Judge.ParseJudgeResponse(raw, ["Quality"]);
|
|
Assert.Equal(5, result.OverallScore);
|
|
Assert.Contains("excellent", result.RubricScores[0].Reasoning);
|
|
}
|
|
|
|
[Fact]
|
|
public void ThrowsWhenContentHasOnlyMalformedJson()
|
|
{
|
|
var malformed = "{\"overall_score\": 4, broken}";
|
|
var ex = Assert.Throws<InvalidOperationException>(
|
|
() => Judge.ParseJudgeResponse(malformed, []));
|
|
Assert.Contains("contained no JSON", ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void ThrowsWhenContentHasMalformedJsonWithInvalidEscapes()
|
|
{
|
|
var malformed = "{\"overall_score\": \"4\\x\", broken}";
|
|
var ex = Assert.Throws<InvalidOperationException>(
|
|
() => Judge.ParseJudgeResponse(malformed, []));
|
|
Assert.Contains("contained no JSON", ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void ThrowsWhenContentHasNoJson()
|
|
{
|
|
var ex = Assert.Throws<InvalidOperationException>(
|
|
() => Judge.ParseJudgeResponse("no json here", []));
|
|
Assert.Contains("contained no JSON", ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void ClampsScoresTo1To5Range()
|
|
{
|
|
var json = JsonSerializer.Serialize(new
|
|
{
|
|
rubric_scores = new[] { new { criterion = "Q", score = 10, reasoning = "" } },
|
|
overall_score = -1,
|
|
overall_reasoning = "",
|
|
});
|
|
var result = Judge.ParseJudgeResponse(json, ["Q"]);
|
|
Assert.Equal(5, result.RubricScores[0].Score);
|
|
Assert.Equal(1, result.OverallScore);
|
|
}
|
|
|
|
[Fact]
|
|
public void DefaultsMissingScoresTo3()
|
|
{
|
|
var json = JsonSerializer.Serialize(new
|
|
{
|
|
rubric_scores = new[] { new { criterion = "Q", score = 0, reasoning = "" } },
|
|
overall_score = 0,
|
|
overall_reasoning = "",
|
|
});
|
|
// The C# code clamps to [1,5], so score=0 becomes 1, not 3.
|
|
// In TS, missing fields default to 3. But C# requires explicit fields.
|
|
// Let's test with actual missing score - but JsonSerializer won't omit a field.
|
|
// The TS test passes score as undefined. C# source reads GetDouble which throws if missing.
|
|
// So we test clamping of 0 to 1 instead, matching the actual C# behavior.
|
|
var result = Judge.ParseJudgeResponse(json, ["Q"]);
|
|
Assert.Equal(1, result.RubricScores[0].Score);
|
|
Assert.Equal(1, result.OverallScore);
|
|
}
|
|
}
|