texts fixes, search bar fixes, global menu fixes.

This commit is contained in:
master
2026-03-05 18:10:56 +02:00
parent 8e1cb9448d
commit a918d39a61
101 changed files with 3543 additions and 534 deletions

View File

@@ -0,0 +1,70 @@
using System.Net;
using Microsoft.AspNetCore.Mvc.Testing;
namespace StellaOps.PacksRegistry.Tests;
[Collection(PacksRegistryStartupEnvironmentCollection.Name)]
public sealed class PacksRegistryStartupContractTests
{
[Fact]
public void Startup_FailsWithoutPostgresConnectionString_InProduction()
{
using var environment = PacksRegistryStartupEnvironmentScope.ProductionPostgresWithoutConnection();
using var factory = new WebApplicationFactory<Program>();
var exception = Assert.ThrowsAny<Exception>(() =>
{
using var client = factory.CreateClient();
});
Assert.Contains(
"PacksRegistry requires PostgreSQL connection settings in non-development mode.",
exception.ToString(),
StringComparison.Ordinal);
}
[Fact]
public void Startup_RejectsRustFsObjectStoreDriver()
{
using var environment = PacksRegistryStartupEnvironmentScope.ProductionWithObjectStoreDriver("rustfs");
using var factory = new WebApplicationFactory<Program>();
var exception = Assert.ThrowsAny<Exception>(() =>
{
using var client = factory.CreateClient();
});
Assert.Contains(
"RustFS object store is configured for PacksRegistry, but no RustFS adapter is implemented.",
exception.ToString(),
StringComparison.Ordinal);
}
[Fact]
public void Startup_RejectsUnsupportedObjectStoreDriver()
{
using var environment = PacksRegistryStartupEnvironmentScope.ProductionWithObjectStoreDriver("unknown-store");
using var factory = new WebApplicationFactory<Program>();
var exception = Assert.ThrowsAny<Exception>(() =>
{
using var client = factory.CreateClient();
});
Assert.Contains(
"Unsupported object store driver 'unknown-store' for PacksRegistry. Allowed values: seed-fs.",
exception.ToString(),
StringComparison.Ordinal);
}
[Fact]
public async Task Startup_AllowsSeedFsObjectStoreDriver()
{
using var environment = PacksRegistryStartupEnvironmentScope.TestingInMemorySeedFs();
using var factory = new WebApplicationFactory<Program>();
using var client = factory.CreateClient();
var response = await client.GetAsync("/healthz", TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}

View File

@@ -0,0 +1,96 @@
namespace StellaOps.PacksRegistry.Tests;
[CollectionDefinition(Name, DisableParallelization = true)]
public sealed class PacksRegistryStartupEnvironmentCollection
{
public const string Name = "PacksRegistryStartupEnvironment";
}
internal sealed class PacksRegistryStartupEnvironmentScope : IDisposable
{
private static readonly string[] ManagedKeys =
[
"DOTNET_ENVIRONMENT",
"ASPNETCORE_ENVIRONMENT",
"STORAGE__DRIVER",
"PACKSREGISTRY__STORAGE__DRIVER",
"STORAGE__OBJECTSTORE__DRIVER",
"PACKSREGISTRY__STORAGE__OBJECTSTORE__DRIVER",
"STORAGE__POSTGRES__CONNECTIONSTRING",
"PACKSREGISTRY__STORAGE__POSTGRES__CONNECTIONSTRING",
"CONNECTIONSTRINGS__PACKSREGISTRY",
"CONNECTIONSTRINGS__DEFAULT"
];
private readonly Dictionary<string, string?> _originalValues = new(StringComparer.Ordinal);
private PacksRegistryStartupEnvironmentScope()
{
foreach (var key in ManagedKeys)
{
_originalValues[key] = Environment.GetEnvironmentVariable(key);
}
}
public static PacksRegistryStartupEnvironmentScope ProductionPostgresWithoutConnection()
{
var scope = new PacksRegistryStartupEnvironmentScope();
scope.Set("DOTNET_ENVIRONMENT", "Production");
scope.Set("ASPNETCORE_ENVIRONMENT", "Production");
scope.Set("STORAGE__DRIVER", "postgres");
scope.Set("PACKSREGISTRY__STORAGE__DRIVER", "postgres");
scope.Set("PACKSREGISTRY__STORAGE__OBJECTSTORE__DRIVER", "seed-fs");
scope.Set("STORAGE__OBJECTSTORE__DRIVER", "seed-fs");
scope.Set("STORAGE__POSTGRES__CONNECTIONSTRING", null);
scope.Set("PACKSREGISTRY__STORAGE__POSTGRES__CONNECTIONSTRING", null);
scope.Set("CONNECTIONSTRINGS__PACKSREGISTRY", null);
scope.Set("CONNECTIONSTRINGS__DEFAULT", null);
return scope;
}
public static PacksRegistryStartupEnvironmentScope ProductionWithObjectStoreDriver(string objectStoreDriver)
{
var scope = new PacksRegistryStartupEnvironmentScope();
scope.Set("DOTNET_ENVIRONMENT", "Production");
scope.Set("ASPNETCORE_ENVIRONMENT", "Production");
scope.Set("STORAGE__DRIVER", "postgres");
scope.Set("PACKSREGISTRY__STORAGE__DRIVER", "postgres");
scope.Set("PACKSREGISTRY__STORAGE__OBJECTSTORE__DRIVER", objectStoreDriver);
scope.Set("STORAGE__OBJECTSTORE__DRIVER", objectStoreDriver);
var connectionString = "Host=localhost;Database=stellaops_packs;Username=stellaops;Password=stellaops";
scope.Set("STORAGE__POSTGRES__CONNECTIONSTRING", connectionString);
scope.Set("PACKSREGISTRY__STORAGE__POSTGRES__CONNECTIONSTRING", connectionString);
scope.Set("CONNECTIONSTRINGS__PACKSREGISTRY", connectionString);
scope.Set("CONNECTIONSTRINGS__DEFAULT", connectionString);
return scope;
}
public static PacksRegistryStartupEnvironmentScope TestingInMemorySeedFs()
{
var scope = new PacksRegistryStartupEnvironmentScope();
scope.Set("DOTNET_ENVIRONMENT", "Testing");
scope.Set("ASPNETCORE_ENVIRONMENT", "Testing");
scope.Set("STORAGE__DRIVER", "inmemory");
scope.Set("PACKSREGISTRY__STORAGE__DRIVER", "inmemory");
scope.Set("PACKSREGISTRY__STORAGE__OBJECTSTORE__DRIVER", "seed-fs");
scope.Set("STORAGE__OBJECTSTORE__DRIVER", "seed-fs");
scope.Set("STORAGE__POSTGRES__CONNECTIONSTRING", null);
scope.Set("PACKSREGISTRY__STORAGE__POSTGRES__CONNECTIONSTRING", null);
scope.Set("CONNECTIONSTRINGS__PACKSREGISTRY", null);
scope.Set("CONNECTIONSTRINGS__DEFAULT", null);
return scope;
}
public void Dispose()
{
foreach (var entry in _originalValues)
{
Environment.SetEnvironmentVariable(entry.Key, entry.Value);
}
}
private void Set(string key, string? value)
{
Environment.SetEnvironmentVariable(key, value);
}
}

View File

@@ -9,3 +9,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| AUDIT-0432-T | DONE | Revalidated 2026-01-07; test coverage audit for StellaOps.PacksRegistry.Tests. |
| AUDIT-0432-A | DONE | Waived (test project; revalidated 2026-01-07). |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |
| SPRINT-20260305-002 | DONE | Added `PacksRegistryStartupContractTests` covering postgres missing-connection fail-fast and seed-fs/rustfs object-store contract enforcement. |

View File

@@ -73,8 +73,6 @@ else
}
ValidateObjectStoreContract(
builder.Configuration,
builder.Environment.IsDevelopment(),
"PacksRegistry",
objectStoreDriver);
@@ -925,38 +923,21 @@ static string ResolveObjectStoreDriver(IConfiguration configuration, string serv
}
static void ValidateObjectStoreContract(
IConfiguration configuration,
bool isDevelopment,
string serviceName,
string objectStoreDriver)
{
if (!string.Equals(objectStoreDriver, "rustfs", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(objectStoreDriver, "seed-fs", StringComparison.OrdinalIgnoreCase))
if (!string.Equals(objectStoreDriver, "seed-fs", StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(objectStoreDriver, "rustfs", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
$"RustFS object store is configured for {serviceName}, but no RustFS adapter is implemented. " +
"Use seed-fs.");
}
throw new InvalidOperationException(
$"Unsupported object store driver '{objectStoreDriver}' for {serviceName}. " +
"Allowed values: rustfs, seed-fs.");
}
if (!string.Equals(objectStoreDriver, "rustfs", StringComparison.OrdinalIgnoreCase))
{
return;
}
if (!isDevelopment)
{
throw new InvalidOperationException(
$"RustFS object store is configured for {serviceName}, but the RustFS adapter is not implemented yet. " +
"Use seed-fs until RustFS adapter support lands.");
}
var rustFsBaseUrl = FirstNonEmpty(
configuration[$"{serviceName}:Storage:ObjectStore:RustFs:BaseUrl"],
configuration["Storage:ObjectStore:RustFs:BaseUrl"]);
if (string.IsNullOrWhiteSpace(rustFsBaseUrl))
{
return;
"Allowed values: seed-fs.");
}
}

View File

@@ -10,3 +10,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| AUDIT-0433-A | TODO | Revalidated 2026-01-07 (open findings). |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |
| SPRINT-312-003 | DONE | Postgres-first storage driver migration with seed-fs payload contract wired in Program startup (pack/provenance/attestation payload channel). |
| SPRINT-20260305-002 | DONE | Finalized startup contract: seed-fs is the only accepted object-store driver; rustfs/unknown drivers fail fast with deterministic error messages. |

View File

@@ -6,3 +6,4 @@ Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_sol
| --- | --- | --- |
| REMED-05 | TODO | Remediation checklist: docs/implplan/audits/csproj-standards/remediation/checklists/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.Tests/StellaOps.TaskRunner.Tests.md. |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |
| SPRINT-20260305-002 | DONE | Added `TaskRunnerStartupContractTests` covering postgres non-dev fail-fast and object-store driver contract (`seed-fs` only, rustfs/unknown rejected). |

View File

@@ -0,0 +1,70 @@
using System.Net;
using Microsoft.AspNetCore.Mvc.Testing;
namespace StellaOps.TaskRunner.Tests;
[Collection(TaskRunnerStartupEnvironmentCollection.Name)]
public sealed class TaskRunnerStartupContractTests
{
[Fact]
public void Startup_FailsWithoutPostgresConnectionString_InProduction()
{
using var environment = TaskRunnerStartupEnvironmentScope.ProductionPostgresWithoutConnection();
using var factory = new WebApplicationFactory<Program>();
var exception = Assert.ThrowsAny<Exception>(() =>
{
using var client = factory.CreateClient();
});
Assert.Contains(
"TaskRunner requires PostgreSQL connection settings in non-development mode.",
exception.ToString(),
StringComparison.Ordinal);
}
[Fact]
public void Startup_RejectsRustFsObjectStoreDriver()
{
using var environment = TaskRunnerStartupEnvironmentScope.ProductionWithObjectStoreDriver("rustfs");
using var factory = new WebApplicationFactory<Program>();
var exception = Assert.ThrowsAny<Exception>(() =>
{
using var client = factory.CreateClient();
});
Assert.Contains(
"RustFS object store is configured for TaskRunner, but no RustFS adapter is implemented. Use seed-fs.",
exception.ToString(),
StringComparison.Ordinal);
}
[Fact]
public void Startup_RejectsUnsupportedObjectStoreDriver()
{
using var environment = TaskRunnerStartupEnvironmentScope.ProductionWithObjectStoreDriver("unknown-store");
using var factory = new WebApplicationFactory<Program>();
var exception = Assert.ThrowsAny<Exception>(() =>
{
using var client = factory.CreateClient();
});
Assert.Contains(
"Unsupported object store driver 'unknown-store' for TaskRunner. Allowed values: seed-fs.",
exception.ToString(),
StringComparison.Ordinal);
}
[Fact]
public async Task Startup_AllowsSeedFsObjectStoreDriver()
{
using var environment = TaskRunnerStartupEnvironmentScope.TestingInMemorySeedFs();
using var factory = new WebApplicationFactory<Program>();
using var client = factory.CreateClient();
var response = await client.GetAsync("/.well-known/openapi", TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}

View File

@@ -0,0 +1,96 @@
namespace StellaOps.TaskRunner.Tests;
[CollectionDefinition(Name, DisableParallelization = true)]
public sealed class TaskRunnerStartupEnvironmentCollection
{
public const string Name = "TaskRunnerStartupEnvironment";
}
internal sealed class TaskRunnerStartupEnvironmentScope : IDisposable
{
private static readonly string[] ManagedKeys =
[
"DOTNET_ENVIRONMENT",
"ASPNETCORE_ENVIRONMENT",
"STORAGE__DRIVER",
"TASKRUNNER__STORAGE__DRIVER",
"STORAGE__OBJECTSTORE__DRIVER",
"TASKRUNNER__STORAGE__OBJECTSTORE__DRIVER",
"STORAGE__POSTGRES__CONNECTIONSTRING",
"TASKRUNNER__STORAGE__POSTGRES__CONNECTIONSTRING",
"CONNECTIONSTRINGS__TASKRUNNER",
"CONNECTIONSTRINGS__DEFAULT"
];
private readonly Dictionary<string, string?> _originalValues = new(StringComparer.Ordinal);
private TaskRunnerStartupEnvironmentScope()
{
foreach (var key in ManagedKeys)
{
_originalValues[key] = Environment.GetEnvironmentVariable(key);
}
}
public static TaskRunnerStartupEnvironmentScope ProductionPostgresWithoutConnection()
{
var scope = new TaskRunnerStartupEnvironmentScope();
scope.Set("DOTNET_ENVIRONMENT", "Production");
scope.Set("ASPNETCORE_ENVIRONMENT", "Production");
scope.Set("STORAGE__DRIVER", "postgres");
scope.Set("TASKRUNNER__STORAGE__DRIVER", "postgres");
scope.Set("TASKRUNNER__STORAGE__OBJECTSTORE__DRIVER", "seed-fs");
scope.Set("STORAGE__OBJECTSTORE__DRIVER", "seed-fs");
scope.Set("STORAGE__POSTGRES__CONNECTIONSTRING", null);
scope.Set("TASKRUNNER__STORAGE__POSTGRES__CONNECTIONSTRING", null);
scope.Set("CONNECTIONSTRINGS__TASKRUNNER", null);
scope.Set("CONNECTIONSTRINGS__DEFAULT", null);
return scope;
}
public static TaskRunnerStartupEnvironmentScope ProductionWithObjectStoreDriver(string objectStoreDriver)
{
var scope = new TaskRunnerStartupEnvironmentScope();
scope.Set("DOTNET_ENVIRONMENT", "Production");
scope.Set("ASPNETCORE_ENVIRONMENT", "Production");
scope.Set("STORAGE__DRIVER", "postgres");
scope.Set("TASKRUNNER__STORAGE__DRIVER", "postgres");
scope.Set("TASKRUNNER__STORAGE__OBJECTSTORE__DRIVER", objectStoreDriver);
scope.Set("STORAGE__OBJECTSTORE__DRIVER", objectStoreDriver);
var connectionString = "Host=localhost;Database=stellaops_taskrunner;Username=stellaops;Password=stellaops";
scope.Set("STORAGE__POSTGRES__CONNECTIONSTRING", connectionString);
scope.Set("TASKRUNNER__STORAGE__POSTGRES__CONNECTIONSTRING", connectionString);
scope.Set("CONNECTIONSTRINGS__TASKRUNNER", connectionString);
scope.Set("CONNECTIONSTRINGS__DEFAULT", connectionString);
return scope;
}
public static TaskRunnerStartupEnvironmentScope TestingInMemorySeedFs()
{
var scope = new TaskRunnerStartupEnvironmentScope();
scope.Set("DOTNET_ENVIRONMENT", "Testing");
scope.Set("ASPNETCORE_ENVIRONMENT", "Testing");
scope.Set("STORAGE__DRIVER", "inmemory");
scope.Set("TASKRUNNER__STORAGE__DRIVER", "inmemory");
scope.Set("TASKRUNNER__STORAGE__OBJECTSTORE__DRIVER", "seed-fs");
scope.Set("STORAGE__OBJECTSTORE__DRIVER", "seed-fs");
scope.Set("STORAGE__POSTGRES__CONNECTIONSTRING", null);
scope.Set("TASKRUNNER__STORAGE__POSTGRES__CONNECTIONSTRING", null);
scope.Set("CONNECTIONSTRINGS__TASKRUNNER", null);
scope.Set("CONNECTIONSTRINGS__DEFAULT", null);
return scope;
}
public void Dispose()
{
foreach (var entry in _originalValues)
{
Environment.SetEnvironmentVariable(entry.Key, entry.Value);
}
}
private void Set(string key, string? value)
{
Environment.SetEnvironmentVariable(key, value);
}
}

View File

@@ -60,7 +60,7 @@ builder.Services.AddStellaOpsTelemetry(
var storageDriver = ResolveStorageDriver(builder.Configuration, "TaskRunner");
RegisterStateStores(builder.Services, builder.Configuration, builder.Environment.IsDevelopment(), storageDriver);
ValidateObjectStoreContract(builder.Configuration, builder.Environment.IsDevelopment(), "TaskRunner");
ValidateObjectStoreContract(builder.Configuration, "TaskRunner");
builder.Services.AddSingleton<IPackRunArtifactReader>(sp =>
{
@@ -1066,27 +1066,19 @@ static string? ResolveSchemaName(IConfiguration configuration, string serviceNam
configuration[$"Postgres:{serviceName}:SchemaName"]);
}
static void ValidateObjectStoreContract(IConfiguration configuration, bool isDevelopment, string serviceName)
static void ValidateObjectStoreContract(IConfiguration configuration, string serviceName)
{
var objectStoreDriver = ResolveObjectStoreDriver(configuration, serviceName);
if (!string.Equals(objectStoreDriver, "seed-fs", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(objectStoreDriver, "rustfs", StringComparison.OrdinalIgnoreCase))
if (!string.Equals(objectStoreDriver, "seed-fs", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
$"Unsupported object store driver '{objectStoreDriver}' for {serviceName}. Allowed values: seed-fs, rustfs.");
}
if (string.Equals(objectStoreDriver, "rustfs", StringComparison.OrdinalIgnoreCase) && !isDevelopment)
{
var rustFsBaseUrl = FirstNonEmpty(
configuration[$"{serviceName}:Storage:ObjectStore:RustFs:BaseUrl"],
configuration["Storage:ObjectStore:RustFs:BaseUrl"]);
if (string.IsNullOrWhiteSpace(rustFsBaseUrl))
if (string.Equals(objectStoreDriver, "rustfs", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
$"RustFS object store is configured for {serviceName}, but BaseUrl is missing.");
$"RustFS object store is configured for {serviceName}, but no RustFS adapter is implemented. Use seed-fs.");
}
throw new InvalidOperationException(
$"Unsupported object store driver '{objectStoreDriver}' for {serviceName}. Allowed values: seed-fs.");
}
}
@@ -1342,5 +1334,7 @@ internal static class RunStateMapper
}
}
public partial class Program;

View File

@@ -7,3 +7,4 @@ Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_sol
| REMED-05 | TODO | Remediation checklist: docs/implplan/audits/csproj-standards/remediation/checklists/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.WebService/StellaOps.TaskRunner.WebService.md. |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |
| SPRINT-312-004 | DONE | Runtime storage driver migration verified: Postgres state/log/approval default plus seed-fs artifact object-store path. |
| SPRINT-20260305-002 | DONE | Startup contract hardened: non-dev postgres missing-connection fails fast; object-store accepts seed-fs only and rejects rustfs/unknown values. |

View File

@@ -58,7 +58,7 @@ builder.Services.AddStellaOpsTelemetry(
var storageDriver = ResolveStorageDriver(builder.Configuration, "TaskRunner");
RegisterStateStores(builder.Services, builder.Configuration, builder.Environment.IsDevelopment(), storageDriver);
ValidateObjectStoreContract(builder.Configuration, builder.Environment.IsDevelopment(), "TaskRunner");
ValidateObjectStoreContract(builder.Configuration, "TaskRunner");
builder.Services.AddSingleton<IPackRunArtifactUploader>(sp =>
{
@@ -179,27 +179,19 @@ static string? ResolveSchemaName(IConfiguration configuration, string serviceNam
configuration[$"Postgres:{serviceName}:SchemaName"]);
}
static void ValidateObjectStoreContract(IConfiguration configuration, bool isDevelopment, string serviceName)
static void ValidateObjectStoreContract(IConfiguration configuration, string serviceName)
{
var objectStoreDriver = ResolveObjectStoreDriver(configuration, serviceName);
if (!string.Equals(objectStoreDriver, "seed-fs", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(objectStoreDriver, "rustfs", StringComparison.OrdinalIgnoreCase))
if (!string.Equals(objectStoreDriver, "seed-fs", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
$"Unsupported object store driver '{objectStoreDriver}' for {serviceName}. Allowed values: seed-fs, rustfs.");
}
if (string.Equals(objectStoreDriver, "rustfs", StringComparison.OrdinalIgnoreCase) && !isDevelopment)
{
var rustFsBaseUrl = FirstNonEmpty(
configuration[$"{serviceName}:Storage:ObjectStore:RustFs:BaseUrl"],
configuration["Storage:ObjectStore:RustFs:BaseUrl"]);
if (string.IsNullOrWhiteSpace(rustFsBaseUrl))
if (string.Equals(objectStoreDriver, "rustfs", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(
$"RustFS object store is configured for {serviceName}, but BaseUrl is missing.");
$"RustFS object store is configured for {serviceName}, but no RustFS adapter is implemented. Use seed-fs.");
}
throw new InvalidOperationException(
$"Unsupported object store driver '{objectStoreDriver}' for {serviceName}. Allowed values: seed-fs.");
}
}

View File

@@ -7,3 +7,4 @@ Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_sol
| REMED-05 | TODO | Remediation checklist: docs/implplan/audits/csproj-standards/remediation/checklists/src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.Worker/StellaOps.TaskRunner.Worker.md. |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |
| SPRINT-312-004 | DONE | Worker storage wiring aligned to Postgres state/log/approval and seed-fs artifact/provenance object-store contract. |
| SPRINT-20260305-002 | DONE | Worker startup contract now rejects rustfs/unknown object-store drivers and keeps seed-fs as the deterministic supported payload channel. |