File: ResultStoreTester.cs
Web Access
Project: src\test\Libraries\Microsoft.Extensions.AI.Evaluation.Reporting.Tests\Microsoft.Extensions.AI.Evaluation.Reporting.Tests.csproj (Microsoft.Extensions.AI.Evaluation.Reporting.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.TestUtilities;
using Xunit;
 
namespace Microsoft.Extensions.AI.Evaluation.Reporting.Tests;
 
public abstract class ResultStoreTester
{
    public abstract IResultStore CreateResultStore();
 
    public abstract bool IsConfigured { get; }
 
    private static ScenarioRunResult CreateTestResult(string scenarioName, string iterationName, string executionName)
    {
        BooleanMetric booleanMetric = new BooleanMetric("boolean", value: true);
 
        NumericMetric numericMetric = new NumericMetric("numeric", value: 3);
        numericMetric.AddDiagnostic(EvaluationDiagnostic.Informational("Informational Message"));
 
        StringMetric stringMetric = new StringMetric("string", value: "Good");
 
        return new ScenarioRunResult(
            scenarioName: scenarioName,
            iterationName: iterationName,
            executionName: executionName,
            creationTime: DateTime.UtcNow,
            messages: [new ChatMessage(ChatRole.User, "User prompt")],
            modelResponse: new ChatMessage(ChatRole.Assistant, "LLM response"),
            evaluationResult: new EvaluationResult(booleanMetric, numericMetric, stringMetric));
    }
 
    private static string ScenarioName(int n) => $"Test.Scenario.{n}";
    private static string IterationName(int n) => $"Iteration {n}";
 
    private static async Task<IEnumerable<(string executionName, string scenarioName, string iterationName)>> LoadResultsAsync(int n, IResultStore resultStore)
    {
        List<(string executionName, string scenarioName, string iterationName)> results = [];
        await foreach (string executionName in resultStore.GetLatestExecutionNamesAsync(n))
        {
            await foreach (string scenarioName in resultStore.GetScenarioNamesAsync(executionName))
            {
                await foreach (string iterationName in resultStore.GetIterationNamesAsync(executionName, scenarioName))
                {
                    results.Add((executionName, scenarioName, iterationName));
                }
            }
        }
 
        return results;
    }
 
    private void SkipIfNotConfigured()
    {
        if (!IsConfigured)
        {
            throw new SkipTestException("Test not configured");
        }
    }
 
    [ConditionalFact]
    public async Task WriteAndReadResults()
    {
        SkipIfNotConfigured();
 
        IResultStore resultStore = CreateResultStore();
        Assert.NotNull(resultStore);
 
        string newExecutionName = $"Test Execution {Path.GetRandomFileName()}";
        IEnumerable<ScenarioRunResult> testResults = [
            CreateTestResult(ScenarioName(0), IterationName(0), newExecutionName),
            CreateTestResult(ScenarioName(0), IterationName(1), newExecutionName),
            CreateTestResult(ScenarioName(0), IterationName(2), newExecutionName),
            CreateTestResult(ScenarioName(1), IterationName(0), newExecutionName),
            CreateTestResult(ScenarioName(2), IterationName(4), newExecutionName),
            CreateTestResult(ScenarioName(2), IterationName(5), newExecutionName)
        ];
        await resultStore.WriteResultsAsync(testResults);
 
        (string executionName, string scenarioName, string iterationName)[] results = [.. await LoadResultsAsync(1, resultStore)];
        Assert.Equal(6, results.Length);
 
        Assert.True(results.All(r => r.executionName == newExecutionName));
 
        Assert.Equal(ScenarioName(0), results[0].scenarioName);
        Assert.Equal(ScenarioName(0), results[1].scenarioName);
        Assert.Equal(ScenarioName(0), results[2].scenarioName);
        Assert.Equal(ScenarioName(1), results[3].scenarioName);
        Assert.Equal(ScenarioName(2), results[4].scenarioName);
        Assert.Equal(ScenarioName(2), results[5].scenarioName);
 
        Assert.Equal(IterationName(0), results[0].iterationName);
        Assert.Equal(IterationName(1), results[1].iterationName);
        Assert.Equal(IterationName(2), results[2].iterationName);
        Assert.Equal(IterationName(0), results[3].iterationName);
        Assert.Equal(IterationName(4), results[4].iterationName);
        Assert.Equal(IterationName(5), results[5].iterationName);
    }
 
    [ConditionalFact]
    public async Task DeleteExecutions()
    {
        SkipIfNotConfigured();
 
        IResultStore resultStore = CreateResultStore();
        Assert.NotNull(resultStore);
 
        string executionName = $"Test Execution {Path.GetRandomFileName()}";
        IEnumerable<ScenarioRunResult> testResults = [
            CreateTestResult(ScenarioName(0), IterationName(0), executionName),
            CreateTestResult(ScenarioName(0), IterationName(2), executionName),
            CreateTestResult(ScenarioName(1), IterationName(0), executionName),
            CreateTestResult(ScenarioName(2), IterationName(4), executionName),
            CreateTestResult(ScenarioName(2), IterationName(5), executionName)
        ];
        await resultStore.WriteResultsAsync(testResults);
 
        await resultStore.DeleteResultsAsync(executionName);
 
        (string executionName, string scenarioName, string iterationName)[] results = [.. await LoadResultsAsync(1, resultStore)];
        Assert.Empty(results);
    }
 
    [ConditionalFact]
    public async Task DeleteSomeExecutions()
    {
        SkipIfNotConfigured();
 
        IResultStore resultStore = CreateResultStore();
        Assert.NotNull(resultStore);
 
        string executionName0 = $"Test Execution {Path.GetRandomFileName()}";
        string executionName1 = $"Test Execution {Path.GetRandomFileName()}";
        IEnumerable<ScenarioRunResult> testResults = [
            CreateTestResult(ScenarioName(1), IterationName(0), executionName1),
            CreateTestResult(ScenarioName(2), IterationName(4), executionName1),
            CreateTestResult(ScenarioName(2), IterationName(5), executionName1),
            CreateTestResult(ScenarioName(0), IterationName(0), executionName0),
            CreateTestResult(ScenarioName(0), IterationName(2), executionName0),
        ];
        await resultStore.WriteResultsAsync(testResults);
 
        await resultStore.DeleteResultsAsync(executionName0);
 
        (string executionName, string scenarioName, string iterationName)[] results = [.. await LoadResultsAsync(1, resultStore)];
        Assert.Equal(3, results.Length);
 
        Assert.True(results.All(r => r.executionName == executionName1));
 
        Assert.Equal(ScenarioName(1), results[0].scenarioName);
        Assert.Equal(ScenarioName(2), results[1].scenarioName);
        Assert.Equal(ScenarioName(2), results[2].scenarioName);
 
        Assert.Equal(IterationName(0), results[0].iterationName);
        Assert.Equal(IterationName(4), results[1].iterationName);
        Assert.Equal(IterationName(5), results[2].iterationName);
    }
 
    [ConditionalFact]
    public async Task DeleteScenarios()
    {
        SkipIfNotConfigured();
 
        IResultStore resultStore = CreateResultStore();
        Assert.NotNull(resultStore);
 
        string executionName = $"Test Execution {Path.GetRandomFileName()}";
        IEnumerable<ScenarioRunResult> testResults = [
            CreateTestResult(ScenarioName(0), IterationName(0), executionName),
            CreateTestResult(ScenarioName(0), IterationName(1), executionName),
            CreateTestResult(ScenarioName(0), IterationName(2), executionName),
            CreateTestResult(ScenarioName(1), IterationName(0), executionName),
            CreateTestResult(ScenarioName(2), IterationName(4), executionName),
            CreateTestResult(ScenarioName(2), IterationName(5), executionName)
        ];
        await resultStore.WriteResultsAsync(testResults);
 
        await resultStore.DeleteResultsAsync(executionName, ScenarioName(0));
 
        (string executionName, string scenarioName, string iterationName)[] results = [.. await LoadResultsAsync(1, resultStore)];
        Assert.Equal(3, results.Length);
 
        Assert.True(results.All(r => r.executionName == executionName));
 
        Assert.Equal(ScenarioName(1), results[0].scenarioName);
        Assert.Equal(ScenarioName(2), results[1].scenarioName);
        Assert.Equal(ScenarioName(2), results[2].scenarioName);
 
        Assert.Equal(IterationName(0), results[0].iterationName);
        Assert.Equal(IterationName(4), results[1].iterationName);
        Assert.Equal(IterationName(5), results[2].iterationName);
    }
 
    [ConditionalFact]
    public async Task DeleteIterations()
    {
        SkipIfNotConfigured();
 
        IResultStore resultStore = CreateResultStore();
        Assert.NotNull(resultStore);
 
        string executionName = $"Test Execution {Path.GetRandomFileName()}";
        IEnumerable<ScenarioRunResult> testResults = [
            CreateTestResult(ScenarioName(0), IterationName(0), executionName),
            CreateTestResult(ScenarioName(0), IterationName(1), executionName),
            CreateTestResult(ScenarioName(0), IterationName(2), executionName),
            CreateTestResult(ScenarioName(1), IterationName(0), executionName),
            CreateTestResult(ScenarioName(2), IterationName(4), executionName),
            CreateTestResult(ScenarioName(2), IterationName(5), executionName)
        ];
        await resultStore.WriteResultsAsync(testResults);
 
        await resultStore.DeleteResultsAsync(executionName, ScenarioName(0), IterationName(2));
 
        (string executionName, string scenarioName, string iterationName)[] results = [.. await LoadResultsAsync(1, resultStore)];
        Assert.Equal(5, results.Length);
 
        Assert.True(results.All(r => r.executionName == executionName));
 
        Assert.Equal(ScenarioName(0), results[0].scenarioName);
        Assert.Equal(ScenarioName(0), results[1].scenarioName);
        Assert.Equal(ScenarioName(1), results[2].scenarioName);
        Assert.Equal(ScenarioName(2), results[3].scenarioName);
        Assert.Equal(ScenarioName(2), results[4].scenarioName);
 
        Assert.Equal(IterationName(0), results[0].iterationName);
        Assert.Equal(IterationName(1), results[1].iterationName);
        Assert.Equal(IterationName(0), results[2].iterationName);
        Assert.Equal(IterationName(4), results[3].iterationName);
        Assert.Equal(IterationName(5), results[4].iterationName);
    }
}