stela ops usage fixes roles propagation and timoeut, one account to support multi tenants, migrations consolidation, search to support documentation, doctor and open api vector db search
This commit is contained in:
@@ -132,4 +132,19 @@ public sealed class CommandFactoryTests
|
||||
var sbomLake = Assert.Single(analytics.Subcommands, command => string.Equals(command.Name, "sbom-lake", StringComparison.Ordinal));
|
||||
Assert.Contains(sbomLake.Subcommands, command => string.Equals(command.Name, "suppliers", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_ExposesKnowledgeSearchCommands()
|
||||
{
|
||||
using var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.None));
|
||||
var services = new ServiceCollection().BuildServiceProvider();
|
||||
var root = CommandFactory.Create(services, new StellaOpsCliOptions(), CancellationToken.None, loggerFactory);
|
||||
|
||||
var search = Assert.Single(root.Subcommands, command => string.Equals(command.Name, "search", StringComparison.Ordinal));
|
||||
Assert.NotNull(search);
|
||||
|
||||
var advisoryAi = Assert.Single(root.Subcommands, command => string.Equals(command.Name, "advisoryai", StringComparison.Ordinal));
|
||||
var index = Assert.Single(advisoryAi.Subcommands, command => string.Equals(command.Name, "index", StringComparison.Ordinal));
|
||||
Assert.Contains(index.Subcommands, command => string.Equals(command.Name, "rebuild", StringComparison.Ordinal));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,22 @@ public sealed class DoctorCommandGroupTests
|
||||
fixCommand!.Description.Should().Contain("fix");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildDoctorCommand_HasSuggestSubcommand()
|
||||
{
|
||||
// Arrange
|
||||
var services = CreateTestServices();
|
||||
var verboseOption = new Option<bool>("--verbose");
|
||||
|
||||
// Act
|
||||
var command = DoctorCommandGroup.BuildDoctorCommand(services, verboseOption, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
var suggestCommand = command.Subcommands.FirstOrDefault(c => c.Name == "suggest");
|
||||
suggestCommand.Should().NotBeNull();
|
||||
suggestCommand!.Description.Should().Contain("Suggest");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Run Subcommand Options Tests
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using StellaOps.Cli.Commands;
|
||||
using StellaOps.Cli.Services;
|
||||
using StellaOps.Cli.Services.Models.AdvisoryAi;
|
||||
using StellaOps.TestKit;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Cli.Tests.Commands;
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
public sealed class KnowledgeSearchCommandGroupTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SearchCommand_JsonOutput_UsesDeterministicPayloadAndNormalizedFilters()
|
||||
{
|
||||
AdvisoryKnowledgeSearchRequestModel? capturedRequest = null;
|
||||
var backend = new Mock<IBackendOperationsClient>(MockBehavior.Strict);
|
||||
backend
|
||||
.Setup(client => client.SearchAdvisoryKnowledgeAsync(
|
||||
It.IsAny<AdvisoryKnowledgeSearchRequestModel>(),
|
||||
It.IsAny<CancellationToken>()))
|
||||
.Callback<AdvisoryKnowledgeSearchRequestModel, CancellationToken>((request, _) => capturedRequest = request)
|
||||
.ReturnsAsync(CreateSearchResponse());
|
||||
|
||||
using var services = new ServiceCollection()
|
||||
.AddSingleton(backend.Object)
|
||||
.BuildServiceProvider();
|
||||
|
||||
var root = new RootCommand();
|
||||
root.Add(KnowledgeSearchCommandGroup.BuildSearchCommand(
|
||||
services,
|
||||
new Option<bool>("--verbose"),
|
||||
CancellationToken.None));
|
||||
|
||||
var invocation = await InvokeWithCapturedConsoleAsync(
|
||||
root,
|
||||
"search \"OIDC cert failure\" --type doctor,api --type docs --tag Auth --tag oidc,cert --product stella --version 1.2.3 --service gateway --k 7 --json");
|
||||
|
||||
Assert.Equal(0, invocation.ExitCode);
|
||||
Assert.NotNull(capturedRequest);
|
||||
Assert.Equal("OIDC cert failure", capturedRequest!.Q);
|
||||
Assert.Equal(7, capturedRequest.K);
|
||||
Assert.NotNull(capturedRequest.Filters);
|
||||
Assert.Equal(new[] { "api", "docs", "doctor" }, capturedRequest.Filters!.Type);
|
||||
Assert.Equal(new[] { "auth", "cert", "oidc" }, capturedRequest.Filters!.Tags);
|
||||
Assert.Equal("stella", capturedRequest.Filters.Product);
|
||||
Assert.Equal("1.2.3", capturedRequest.Filters.Version);
|
||||
Assert.Equal("gateway", capturedRequest.Filters.Service);
|
||||
Assert.False(capturedRequest.IncludeDebug);
|
||||
|
||||
using var payload = JsonDocument.Parse(invocation.StdOut);
|
||||
var rootElement = payload.RootElement;
|
||||
Assert.Equal("OIDC cert failure", rootElement.GetProperty("query").GetString());
|
||||
Assert.Equal(7, rootElement.GetProperty("topK").GetInt32());
|
||||
var results = rootElement.GetProperty("results");
|
||||
Assert.Equal(3, results.GetArrayLength());
|
||||
Assert.Equal("doctor", results[0].GetProperty("type").GetString());
|
||||
Assert.Equal("check.auth.oidc.cert", results[0].GetProperty("open").GetProperty("doctor").GetProperty("checkCode").GetString());
|
||||
|
||||
backend.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DoctorSuggestCommand_RendersGroupedOutput()
|
||||
{
|
||||
AdvisoryKnowledgeSearchRequestModel? capturedRequest = null;
|
||||
var backend = new Mock<IBackendOperationsClient>(MockBehavior.Strict);
|
||||
backend
|
||||
.Setup(client => client.SearchAdvisoryKnowledgeAsync(
|
||||
It.IsAny<AdvisoryKnowledgeSearchRequestModel>(),
|
||||
It.IsAny<CancellationToken>()))
|
||||
.Callback<AdvisoryKnowledgeSearchRequestModel, CancellationToken>((request, _) => capturedRequest = request)
|
||||
.ReturnsAsync(CreateSearchResponse());
|
||||
|
||||
using var services = new ServiceCollection()
|
||||
.AddSingleton(backend.Object)
|
||||
.BuildServiceProvider();
|
||||
|
||||
var root = new RootCommand();
|
||||
root.Add(DoctorCommandGroup.BuildDoctorCommand(
|
||||
services,
|
||||
new Option<bool>("--verbose"),
|
||||
CancellationToken.None));
|
||||
|
||||
var invocation = await InvokeWithCapturedConsoleAsync(
|
||||
root,
|
||||
"doctor suggest \"x509: certificate signed by unknown authority\" --k 5");
|
||||
|
||||
Assert.Equal(0, invocation.ExitCode);
|
||||
Assert.NotNull(capturedRequest);
|
||||
Assert.Equal("x509: certificate signed by unknown authority", capturedRequest!.Q);
|
||||
Assert.Equal(5, capturedRequest.K);
|
||||
Assert.Null(capturedRequest.Filters);
|
||||
|
||||
Assert.Contains("Recommended checks:", invocation.StdOut, StringComparison.Ordinal);
|
||||
Assert.Contains("doctor: check.auth.oidc.cert", invocation.StdOut, StringComparison.Ordinal);
|
||||
Assert.Contains("Related docs:", invocation.StdOut, StringComparison.Ordinal);
|
||||
Assert.Contains("docs: docs/operations/oidc.md#tls_trust_chain", invocation.StdOut, StringComparison.Ordinal);
|
||||
Assert.Contains("Related endpoints:", invocation.StdOut, StringComparison.Ordinal);
|
||||
Assert.Contains("api: POST /api/v1/authority/oidc/test", invocation.StdOut, StringComparison.Ordinal);
|
||||
|
||||
backend.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdvisoryAiRebuildCommand_JsonOutput_ContainsCounts()
|
||||
{
|
||||
var backend = new Mock<IBackendOperationsClient>(MockBehavior.Strict);
|
||||
backend
|
||||
.Setup(client => client.RebuildAdvisoryKnowledgeIndexAsync(It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new AdvisoryKnowledgeRebuildResponseModel
|
||||
{
|
||||
DocumentCount = 120,
|
||||
ChunkCount = 640,
|
||||
ApiSpecCount = 8,
|
||||
ApiOperationCount = 114,
|
||||
DoctorProjectionCount = 36,
|
||||
DurationMs = 2485
|
||||
});
|
||||
|
||||
using var services = new ServiceCollection()
|
||||
.AddSingleton(backend.Object)
|
||||
.BuildServiceProvider();
|
||||
|
||||
var root = new RootCommand();
|
||||
root.Add(KnowledgeSearchCommandGroup.BuildAdvisoryAiCommand(
|
||||
services,
|
||||
new Option<bool>("--verbose"),
|
||||
CancellationToken.None));
|
||||
|
||||
var invocation = await InvokeWithCapturedConsoleAsync(root, "advisoryai index rebuild --json");
|
||||
|
||||
Assert.Equal(0, invocation.ExitCode);
|
||||
using var payload = JsonDocument.Parse(invocation.StdOut);
|
||||
var rootElement = payload.RootElement;
|
||||
Assert.Equal(120, rootElement.GetProperty("documentCount").GetInt32());
|
||||
Assert.Equal(640, rootElement.GetProperty("chunkCount").GetInt32());
|
||||
Assert.Equal(8, rootElement.GetProperty("apiSpecCount").GetInt32());
|
||||
Assert.Equal(114, rootElement.GetProperty("apiOperationCount").GetInt32());
|
||||
Assert.Equal(36, rootElement.GetProperty("doctorProjectionCount").GetInt32());
|
||||
Assert.Equal(2485, rootElement.GetProperty("durationMs").GetInt64());
|
||||
|
||||
backend.VerifyAll();
|
||||
}
|
||||
|
||||
private static AdvisoryKnowledgeSearchResponseModel CreateSearchResponse()
|
||||
{
|
||||
return new AdvisoryKnowledgeSearchResponseModel
|
||||
{
|
||||
Query = "OIDC cert failure",
|
||||
TopK = 7,
|
||||
Diagnostics = new AdvisoryKnowledgeSearchDiagnosticsModel
|
||||
{
|
||||
FtsMatches = 18,
|
||||
VectorMatches = 0,
|
||||
DurationMs = 14,
|
||||
UsedVector = false,
|
||||
Mode = "fts-only"
|
||||
},
|
||||
Results =
|
||||
[
|
||||
new AdvisoryKnowledgeSearchResultModel
|
||||
{
|
||||
Type = "doctor",
|
||||
Title = "check.auth.oidc.cert - Validate OIDC trust chain",
|
||||
Snippet = "OIDC issuer TLS certificate chain is untrusted.",
|
||||
Score = 0.991,
|
||||
Open = new AdvisoryKnowledgeOpenActionModel
|
||||
{
|
||||
Kind = "doctor",
|
||||
Doctor = new AdvisoryKnowledgeOpenDoctorActionModel
|
||||
{
|
||||
CheckCode = "check.auth.oidc.cert",
|
||||
Severity = "fail",
|
||||
CanRun = true,
|
||||
RunCommand = "stella doctor run --check check.auth.oidc.cert"
|
||||
}
|
||||
},
|
||||
Debug = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["rrf"] = "1.0"
|
||||
}
|
||||
},
|
||||
new AdvisoryKnowledgeSearchResultModel
|
||||
{
|
||||
Type = "docs",
|
||||
Title = "OIDC troubleshooting - trust chain",
|
||||
Snippet = "Import the issuer CA bundle and verify trust store ordering.",
|
||||
Score = 0.876,
|
||||
Open = new AdvisoryKnowledgeOpenActionModel
|
||||
{
|
||||
Kind = "docs",
|
||||
Docs = new AdvisoryKnowledgeOpenDocActionModel
|
||||
{
|
||||
Path = "docs/operations/oidc.md",
|
||||
Anchor = "tls_trust_chain",
|
||||
SpanStart = 122,
|
||||
SpanEnd = 168
|
||||
}
|
||||
}
|
||||
},
|
||||
new AdvisoryKnowledgeSearchResultModel
|
||||
{
|
||||
Type = "api",
|
||||
Title = "POST /api/v1/authority/oidc/test",
|
||||
Snippet = "Validates OIDC configuration and returns certificate diagnostics.",
|
||||
Score = 0.744,
|
||||
Open = new AdvisoryKnowledgeOpenActionModel
|
||||
{
|
||||
Kind = "api",
|
||||
Api = new AdvisoryKnowledgeOpenApiActionModel
|
||||
{
|
||||
Service = "authority",
|
||||
Method = "POST",
|
||||
Path = "/api/v1/authority/oidc/test",
|
||||
OperationId = "Authority_TestOidcConfiguration"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task<CommandInvocationResult> InvokeWithCapturedConsoleAsync(
|
||||
RootCommand root,
|
||||
string commandLine)
|
||||
{
|
||||
var originalOut = Console.Out;
|
||||
var originalError = Console.Error;
|
||||
var stdout = new StringWriter(CultureInfo.InvariantCulture);
|
||||
var stderr = new StringWriter(CultureInfo.InvariantCulture);
|
||||
try
|
||||
{
|
||||
Console.SetOut(stdout);
|
||||
Console.SetError(stderr);
|
||||
var exitCode = await root.Parse(commandLine).InvokeAsync();
|
||||
return new CommandInvocationResult(exitCode, stdout.ToString(), stderr.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.SetOut(originalOut);
|
||||
Console.SetError(originalError);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record CommandInvocationResult(int ExitCode, string StdOut, string StdErr);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Cli.Services;
|
||||
using StellaOps.Platform.Database;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Cli.Tests.Commands;
|
||||
@@ -9,6 +9,6 @@ public class MigrationCommandHandlersTests
|
||||
[Fact]
|
||||
public void Registry_Has_All_Modules()
|
||||
{
|
||||
Assert.Equal(6, MigrationModuleRegistry.Modules.Count);
|
||||
Assert.Equal(10, MigrationModuleRegistry.Modules.Count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Linq;
|
||||
using StellaOps.Cli.Services;
|
||||
using StellaOps.Platform.Database;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Cli.Tests.Commands;
|
||||
@@ -10,14 +10,34 @@ public class MigrationModuleRegistryTests
|
||||
public void Modules_Populated_With_All_Postgres_Modules()
|
||||
{
|
||||
var modules = MigrationModuleRegistry.Modules;
|
||||
Assert.Equal(6, modules.Count);
|
||||
Assert.Equal(10, modules.Count);
|
||||
Assert.Contains(modules, m => m.Name == "AirGap" && m.SchemaName == "airgap");
|
||||
Assert.Contains(modules, m => m.Name == "Authority" && m.SchemaName == "authority");
|
||||
Assert.Contains(modules, m => m.Name == "Scheduler" && m.SchemaName == "scheduler");
|
||||
Assert.Contains(modules, m => m.Name == "Concelier" && m.SchemaName == "vuln");
|
||||
Assert.Contains(modules, m => m.Name == "Policy" && m.SchemaName == "policy");
|
||||
Assert.Contains(modules, m => m.Name == "Notify" && m.SchemaName == "notify");
|
||||
Assert.Contains(modules, m => m.Name == "Excititor" && m.SchemaName == "vex");
|
||||
Assert.Equal(6, MigrationModuleRegistry.ModuleNames.Count());
|
||||
Assert.Contains(modules, m => m.Name == "Platform" && m.SchemaName == "release");
|
||||
Assert.Contains(modules, m => m.Name == "Scanner" && m.SchemaName == "scanner");
|
||||
Assert.Contains(modules, m => m.Name == "TimelineIndexer" && m.SchemaName == "timeline");
|
||||
Assert.Equal(10, MigrationModuleRegistry.ModuleNames.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Modules_Are_AutoDiscovered_From_Plugins()
|
||||
{
|
||||
var pluginTypes = typeof(MigrationModuleRegistry)
|
||||
.Assembly
|
||||
.GetTypes()
|
||||
.Where(static type =>
|
||||
typeof(IMigrationModulePlugin).IsAssignableFrom(type) &&
|
||||
!type.IsAbstract &&
|
||||
!type.IsInterface)
|
||||
.ToArray();
|
||||
|
||||
Assert.NotEmpty(pluginTypes);
|
||||
Assert.Equal(pluginTypes.Length, MigrationModuleRegistry.Modules.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -40,6 +60,6 @@ public class MigrationModuleRegistryTests
|
||||
public void GetModules_All_Returns_All()
|
||||
{
|
||||
var result = MigrationModuleRegistry.GetModules(null);
|
||||
Assert.Equal(6, result.Count());
|
||||
Assert.Equal(10, result.Count());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using StellaOps.Cli.Commands;
|
||||
using StellaOps.Cli.Services;
|
||||
using StellaOps.Platform.Database;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Cli.Tests.Commands;
|
||||
@@ -23,12 +24,16 @@ public class SystemCommandBuilderTests
|
||||
[Fact]
|
||||
public void ModuleNames_Contains_All_Modules()
|
||||
{
|
||||
Assert.Contains("AirGap", MigrationModuleRegistry.ModuleNames);
|
||||
Assert.Contains("Authority", MigrationModuleRegistry.ModuleNames);
|
||||
Assert.Contains("Scheduler", MigrationModuleRegistry.ModuleNames);
|
||||
Assert.Contains("Concelier", MigrationModuleRegistry.ModuleNames);
|
||||
Assert.Contains("Policy", MigrationModuleRegistry.ModuleNames);
|
||||
Assert.Contains("Notify", MigrationModuleRegistry.ModuleNames);
|
||||
Assert.Contains("Excititor", MigrationModuleRegistry.ModuleNames);
|
||||
Assert.Contains("Platform", MigrationModuleRegistry.ModuleNames);
|
||||
Assert.Contains("Scanner", MigrationModuleRegistry.ModuleNames);
|
||||
Assert.Contains("TimelineIndexer", MigrationModuleRegistry.ModuleNames);
|
||||
}
|
||||
|
||||
private static Command BuildSystemCommand()
|
||||
|
||||
@@ -5,6 +5,7 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| SPRINT_20260222_051-MGC-04-W1-TESTS | DONE | Updated migration registry/system command tests for platform-owned 10-module coverage and validated with `dotnet test` (1182 passed on 2026-02-22). |
|
||||
| AUDIT-0143-M | DONE | Revalidated 2026-01-06. |
|
||||
| AUDIT-0143-T | DONE | Revalidated 2026-01-06. |
|
||||
| AUDIT-0143-A | DONE | Waived (test project; revalidated 2026-01-06). |
|
||||
|
||||
Reference in New Issue
Block a user