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

@@ -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. |