release orchestrator v1 draft and build fixes

This commit is contained in:
master
2026-01-12 12:24:17 +02:00
parent f3de858c59
commit 9873f80830
1598 changed files with 240385 additions and 5944 deletions

View File

@@ -0,0 +1,180 @@
namespace StellaOps.Plugin.Samples.HelloWorld.Tests;
using FluentAssertions;
using StellaOps.Plugin.Abstractions;
using StellaOps.Plugin.Abstractions.Health;
using StellaOps.Plugin.Testing;
using Xunit;
/// <summary>
/// Tests for HelloWorldPlugin demonstrating the Plugin.Testing library usage.
/// </summary>
[Trait("Category", "Unit")]
public sealed class HelloWorldPluginTests : PluginTestBase<HelloWorldPlugin>
{
protected override Dictionary<string, object> GetConfiguration() => new()
{
["Greeting"] = "Hello",
["LogGreetings"] = true
};
[Fact]
public void Info_ReturnsCorrectPluginInfo()
{
// Assert - Plugin is already initialized by base class
Plugin.Info.Id.Should().Be("org.stellaops.samples.helloworld");
Plugin.Info.Name.Should().Be("Hello World Sample");
Plugin.Info.Version.Should().Be("1.0.0");
Plugin.Info.Vendor.Should().Be("Stella Ops");
}
[Fact]
public void Capabilities_ReturnsNone()
{
// Assert - this is a sample plugin with no special capabilities
Plugin.Capabilities.Should().Be(PluginCapabilities.None);
}
[Fact]
public void State_AfterInitialization_IsActive()
{
// Assert - base class initializes the plugin
Plugin.State.Should().Be(StellaOps.Plugin.Abstractions.Lifecycle.PluginLifecycleState.Active);
}
[Fact]
public void Greet_WithName_ReturnsGreeting()
{
// Act
var result = Plugin.Greet("World");
// Assert
result.Should().Be("Hello, World!");
}
[Fact]
public async Task HealthCheckAsync_WhenInitialized_ReturnsHealthy()
{
// Act
var health = await Plugin.HealthCheckAsync(CancellationToken.None);
// Assert
health.Status.Should().Be(HealthStatus.Healthy);
health.Details.Should().ContainKey("greeting");
health.Details!["greeting"].Should().Be("Hello");
}
[Fact]
public void Greet_LogsMessage()
{
// Act
Plugin.Greet("Test");
// Assert
Logger.HasLoggedContaining("Generated greeting").Should().BeTrue();
}
}
/// <summary>
/// Tests that require custom configuration.
/// </summary>
[Trait("Category", "Unit")]
public sealed class HelloWorldPluginCustomConfigTests : PluginTestBase<HelloWorldPlugin>
{
protected override Dictionary<string, object> GetConfiguration() => new()
{
["Greeting"] = "Howdy",
["LogGreetings"] = false
};
[Fact]
public void Greet_WithCustomGreeting_UsesConfiguredGreeting()
{
// Act
var result = Plugin.Greet("Partner");
// Assert
result.Should().Be("Howdy, Partner!");
}
[Fact]
public void Greet_WhenLoggingDisabled_DoesNotLog()
{
// Arrange
Logger.Clear();
// Act
Plugin.Greet("Test");
// Assert - should not have logged the greeting
Logger.HasLoggedContaining("Generated greeting").Should().BeFalse();
}
}
/// <summary>
/// Tests with deterministic time.
/// </summary>
[Trait("Category", "Unit")]
public sealed class HelloWorldPluginTimeTests : PluginTestBase<HelloWorldPlugin>
{
private static readonly DateTimeOffset FixedStartTime = new(2025, 1, 15, 10, 30, 0, TimeSpan.Zero);
protected override void ConfigureHost(PluginTestHostOptions options)
{
// Set a fixed start time for deterministic testing
options.StartTime = FixedStartTime;
}
protected override Dictionary<string, object> GetConfiguration() => new()
{
["Greeting"] = "Hello"
};
[Fact]
public async Task HealthCheckAsync_ReturnsInitializedTime()
{
// Act
var health = await Plugin.HealthCheckAsync(CancellationToken.None);
// Assert
health.Status.Should().Be(HealthStatus.Healthy);
health.Details.Should().ContainKey("initialized_at");
// The initialized time should contain our fixed date
health.Details!["initialized_at"].ToString().Should().Contain("2025-01-15");
}
[Fact]
public void FakeTimeProvider_CanAdvanceTime()
{
// Arrange
var initialTime = FakeTimeProvider?.GetUtcNow();
// Act
FakeTimeProvider?.AdvanceMinutes(5);
// Assert
var newTime = FakeTimeProvider?.GetUtcNow();
newTime.Should().Be(initialTime?.AddMinutes(5));
}
}
/// <summary>
/// Tests for uninitialized plugin behavior.
/// </summary>
[Trait("Category", "Unit")]
public sealed class HelloWorldPluginUninitializedTests
{
[Fact]
public async Task HealthCheckAsync_WhenNotInitialized_ReturnsUnhealthy()
{
// Arrange - create plugin without initializing
var plugin = new HelloWorldPlugin();
// Act
var health = await plugin.HealthCheckAsync(CancellationToken.None);
// Assert
health.Status.Should().Be(HealthStatus.Unhealthy);
health.Message.Should().Contain("not initialized");
}
}

View File

@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<!-- Opt out of Concelier test infrastructure -->
<UseConcelierTestInfra>false</UseConcelierTestInfra>
</PropertyGroup>
<!-- Test packages are added by Directory.Build.props for *.Tests projects -->
<ItemGroup>
<PackageReference Include="coverlet.collector">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\StellaOps.Plugin.Samples.HelloWorld\StellaOps.Plugin.Samples.HelloWorld.csproj" />
<ProjectReference Include="..\..\StellaOps.Plugin.Testing\StellaOps.Plugin.Testing.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,122 @@
namespace StellaOps.Plugin.Samples.HelloWorld;
using StellaOps.Plugin.Abstractions;
using StellaOps.Plugin.Abstractions.Context;
using StellaOps.Plugin.Abstractions.Health;
using StellaOps.Plugin.Sdk;
/// <summary>
/// Sample HelloWorld plugin demonstrating SDK usage patterns.
/// This plugin shows how to:
/// - Use PluginBase for simplified development
/// - Access configuration values
/// - Use structured logging
/// - Handle lifecycle events
/// - Report health status
/// </summary>
public sealed class HelloWorldPlugin : PluginBase
{
private HelloWorldOptions? _options;
private DateTimeOffset _initializedAt;
/// <inheritdoc />
public override PluginInfo Info { get; } = new PluginInfoBuilder()
.WithId("org.stellaops.samples.helloworld")
.WithName("Hello World Sample")
.WithVersion("1.0.0")
.WithVendor("Stella Ops")
.WithDescription("A sample plugin demonstrating SDK usage patterns")
.WithLicense("AGPL-3.0-or-later")
.Build();
/// <inheritdoc />
public override PluginCapabilities Capabilities => PluginCapabilities.None;
/// <inheritdoc />
protected override async Task OnInitializeAsync(IPluginContext context, CancellationToken ct)
{
// Load and validate configuration
_options = Configuration.Bind<HelloWorldOptions>();
Logger.Info("HelloWorld plugin initializing with greeting: {Greeting}", _options.Greeting);
// Demonstrate async initialization (e.g., loading resources)
await Task.Delay(10, ct);
_initializedAt = TimeProvider.GetUtcNow();
Logger.Debug("HelloWorld plugin initialized successfully at {Time}", _initializedAt);
}
/// <inheritdoc />
protected override ValueTask OnDisposeAsync()
{
Logger.Debug("HelloWorld plugin disposed");
return ValueTask.CompletedTask;
}
/// <inheritdoc />
public override Task<HealthCheckResult> HealthCheckAsync(CancellationToken ct)
{
// Example health check logic
if (_options is null)
{
return Task.FromResult(HealthCheckResult.Unhealthy("Plugin not initialized"));
}
return Task.FromResult(
HealthCheckResult.Healthy()
.WithDetails(new Dictionary<string, object>
{
["greeting"] = _options.Greeting,
["initialized_at"] = _initializedAt.ToString("O")
}));
}
/// <summary>
/// Demonstrates the greeting functionality.
/// </summary>
/// <param name="name">Name to greet.</param>
/// <returns>The greeting message.</returns>
public string Greet(string name)
{
var greeting = _options?.Greeting ?? "Hello";
var message = $"{greeting}, {name}!";
if (_options?.LogGreetings == true)
{
Logger.Info("Generated greeting for {Name}: {Message}", name, message);
}
return message;
}
}
/// <summary>
/// Configuration options for HelloWorld plugin.
/// </summary>
public sealed class HelloWorldOptions : PluginOptionsBase
{
/// <summary>
/// Gets or sets the greeting phrase.
/// </summary>
[PluginConfig(Key = "Greeting", DefaultValue = "Hello", Description = "The greeting phrase to use")]
public string Greeting { get; set; } = "Hello";
/// <summary>
/// Gets or sets whether to log greetings.
/// </summary>
[PluginConfig(Key = "LogGreetings", DefaultValue = true, Description = "Whether to log each greeting")]
public bool LogGreetings { get; set; } = true;
/// <inheritdoc />
public override IEnumerable<System.ComponentModel.DataAnnotations.ValidationResult> Validate(
System.ComponentModel.DataAnnotations.ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(Greeting))
{
yield return new System.ComponentModel.DataAnnotations.ValidationResult(
"Greeting cannot be empty",
new[] { nameof(Greeting) });
}
}
}

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Description>Sample HelloWorld plugin demonstrating SDK usage</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\StellaOps.Plugin.Sdk\StellaOps.Plugin.Sdk.csproj" />
</ItemGroup>
</Project>