feat(metrics): Add new histograms for chunk latency, results, and sources in AdvisoryAiMetrics
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

feat(telemetry): Record chunk latency, result count, and source count in AdvisoryAiTelemetry

fix(endpoint): Include telemetry source count in advisory chunks endpoint response

test(metrics): Enhance WebServiceEndpointsTests to validate new metrics for chunk latency, results, and sources

refactor(tests): Update test utilities for Deno language analyzer tests

chore(tests): Add performance tests for AdvisoryGuardrail with scenarios and blocked phrases

docs: Archive Sprint 137 design document for scanner and surface enhancements
This commit is contained in:
master
2025-11-10 22:26:43 +02:00
parent 56c687253f
commit b059bc7675
22 changed files with 427 additions and 37 deletions

View File

@@ -0,0 +1,56 @@
using StellaOps.Scanner.Analyzers.Lang;
namespace StellaOps.Scanner.Analyzers.Lang.Deno.Tests.TestUtilities;
internal static class LanguageAnalyzerTestHarness
{
public static async Task<string> RunToJsonAsync(
string fixturePath,
IEnumerable<ILanguageAnalyzer> analyzers,
CancellationToken cancellationToken = default,
LanguageUsageHints? usageHints = null,
IServiceProvider? services = null)
{
if (string.IsNullOrWhiteSpace(fixturePath))
{
throw new ArgumentException("Fixture path is required", nameof(fixturePath));
}
var engine = new LanguageAnalyzerEngine(analyzers ?? Array.Empty<ILanguageAnalyzer>());
var context = new LanguageAnalyzerContext(fixturePath, TimeProvider.System, usageHints, services);
var result = await engine.AnalyzeAsync(context, cancellationToken).ConfigureAwait(false);
return result.ToJson(indent: true);
}
public static async Task AssertDeterministicAsync(
string fixturePath,
string goldenPath,
IEnumerable<ILanguageAnalyzer> analyzers,
CancellationToken cancellationToken = default,
LanguageUsageHints? usageHints = null,
IServiceProvider? services = null)
{
var actual = await RunToJsonAsync(fixturePath, analyzers, cancellationToken, usageHints, services).ConfigureAwait(false);
var expected = await File.ReadAllTextAsync(goldenPath, cancellationToken).ConfigureAwait(false);
actual = NormalizeLineEndings(actual).TrimEnd();
expected = NormalizeLineEndings(expected).TrimEnd();
if (!string.Equals(expected, actual, StringComparison.Ordinal))
{
var actualPath = goldenPath + ".actual";
var directory = Path.GetDirectoryName(actualPath);
if (!string.IsNullOrEmpty(directory))
{
Directory.CreateDirectory(directory);
}
await File.WriteAllTextAsync(actualPath, actual, cancellationToken).ConfigureAwait(false);
}
Assert.Equal(expected, actual);
}
private static string NormalizeLineEndings(string value)
=> value.Replace("\r\n", "\n", StringComparison.Ordinal);
}

View File

@@ -0,0 +1,61 @@
namespace StellaOps.Scanner.Analyzers.Lang.Deno.Tests.TestUtilities;
internal static class TestPaths
{
public static string ResolveFixture(params string[] segments)
{
var baseDirectory = AppContext.BaseDirectory;
var parts = new List<string> { baseDirectory };
parts.Add("Fixtures");
if (segments is not null && segments.Length > 0)
{
parts.AddRange(segments);
}
return Path.GetFullPath(Path.Combine(parts.ToArray()));
}
public static string CreateTemporaryDirectory()
{
var root = Path.Combine(AppContext.BaseDirectory, "tmp", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(root);
return root;
}
public static void SafeDelete(string directory)
{
if (string.IsNullOrWhiteSpace(directory) || !Directory.Exists(directory))
{
return;
}
try
{
Directory.Delete(directory, recursive: true);
}
catch
{
// best-effort cleanup; avoid masking upstream test failures
}
}
public static string ResolveProjectRoot()
{
var directory = AppContext.BaseDirectory;
while (!string.IsNullOrEmpty(directory))
{
var matches = Directory.EnumerateFiles(
directory,
"StellaOps.Scanner.Analyzers.Lang.Deno.Tests.csproj",
SearchOption.TopDirectoryOnly);
if (matches.Any())
{
return directory;
}
directory = Path.GetDirectoryName(directory) ?? string.Empty;
}
throw new InvalidOperationException("Unable to locate project root.");
}
}