Tests fixes, audit progress, UI completions
This commit is contained in:
10
src/AirGap/StellaOps.AirGap.Importer/TASKS.md
Normal file
10
src/AirGap/StellaOps.AirGap.Importer/TASKS.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# AirGap Importer Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0026-M | DONE | Maintainability audit for StellaOps.AirGap.Importer. |
|
||||
| AUDIT-0026-T | DONE | Test coverage audit for StellaOps.AirGap.Importer. |
|
||||
| AUDIT-0026-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,19 @@
|
||||
# AirGap Policy Analyzers Tests Charter
|
||||
|
||||
## Working Directory
|
||||
- `src/AirGap/StellaOps.AirGap.Policy/StellaOps.AirGap.Policy.Analyzers.Tests`
|
||||
|
||||
## Scope
|
||||
- Analyzer and code-fix tests for air-gap egress enforcement.
|
||||
|
||||
## Required Reading
|
||||
- `docs/airgap/airgap-mode.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `src/AirGap/StellaOps.AirGap.Policy/AGENTS.md`
|
||||
|
||||
## Working Agreements
|
||||
- Update task status in the sprint tracker and local `TASKS.md`.
|
||||
- Keep tests deterministic; avoid environment-dependent references.
|
||||
|
||||
## Testing Rules
|
||||
- Cover diagnostics, suppression rules, and deterministic code-fix output.
|
||||
@@ -0,0 +1,10 @@
|
||||
# AirGap Policy Analyzers Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0032-M | DONE | Maintainability audit for StellaOps.AirGap.Policy.Analyzers.Tests. |
|
||||
| AUDIT-0032-T | DONE | Test coverage audit for StellaOps.AirGap.Policy.Analyzers.Tests. |
|
||||
| AUDIT-0032-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,19 @@
|
||||
# AirGap Policy Analyzers Charter
|
||||
|
||||
## Working Directory
|
||||
- `src/AirGap/StellaOps.AirGap.Policy/StellaOps.AirGap.Policy.Analyzers`
|
||||
|
||||
## Scope
|
||||
- Roslyn analyzer + code fix enforcing air-gap egress policy usage.
|
||||
|
||||
## Required Reading
|
||||
- `docs/airgap/airgap-mode.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `src/AirGap/StellaOps.AirGap.Policy/AGENTS.md`
|
||||
|
||||
## Working Agreements
|
||||
- Update task status in the sprint tracker and local `TASKS.md`.
|
||||
- Keep diagnostics deterministic and stable across builds.
|
||||
|
||||
## Testing Rules
|
||||
- Analyzer and code-fix tests must cover expected diagnostics and fix output determinism.
|
||||
@@ -0,0 +1,10 @@
|
||||
# AirGap Policy Analyzers Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0031-M | DONE | Maintainability audit for StellaOps.AirGap.Policy.Analyzers. |
|
||||
| AUDIT-0031-T | DONE | Test coverage audit for StellaOps.AirGap.Policy.Analyzers. |
|
||||
| AUDIT-0031-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,19 @@
|
||||
# AirGap Policy Tests Charter
|
||||
|
||||
## Working Directory
|
||||
- `src/AirGap/StellaOps.AirGap.Policy/StellaOps.AirGap.Policy.Tests`
|
||||
|
||||
## Scope
|
||||
- Unit tests for egress policy evaluation, configuration binding, and HttpClient enforcement.
|
||||
|
||||
## Required Reading
|
||||
- `docs/airgap/airgap-mode.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `src/AirGap/StellaOps.AirGap.Policy/AGENTS.md`
|
||||
|
||||
## Working Agreements
|
||||
- Update task status in the sprint tracker and local `TASKS.md`.
|
||||
- Keep tests deterministic; avoid wall-clock dependencies.
|
||||
|
||||
## Testing Rules
|
||||
- Cover allowlist parsing, rule matching, and sealed/unsealed behavior.
|
||||
@@ -0,0 +1,10 @@
|
||||
# AirGap Policy Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0033-M | DONE | Maintainability audit for StellaOps.AirGap.Policy.Tests. |
|
||||
| AUDIT-0033-T | DONE | Test coverage audit for StellaOps.AirGap.Policy.Tests. |
|
||||
| AUDIT-0033-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,21 @@
|
||||
# AirGap Policy Library Charter
|
||||
|
||||
## Working Directory
|
||||
- `src/AirGap/StellaOps.AirGap.Policy/StellaOps.AirGap.Policy`
|
||||
|
||||
## Scope
|
||||
- Egress policy evaluation, rules, and configuration helpers.
|
||||
- Air-gap aware HttpClient creation helpers.
|
||||
|
||||
## Required Reading
|
||||
- `docs/airgap/airgap-mode.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `src/AirGap/StellaOps.AirGap.Policy/AGENTS.md`
|
||||
|
||||
## Working Agreements
|
||||
- Update task status in the sprint tracker and local `TASKS.md`.
|
||||
- Keep outputs deterministic and sealed-mode safe.
|
||||
- Avoid direct network egress without policy checks.
|
||||
|
||||
## Testing Rules
|
||||
- Cover allow/deny logic, rule matching, and configuration precedence.
|
||||
@@ -0,0 +1,10 @@
|
||||
# AirGap Policy Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0030-M | DONE | Maintainability audit for StellaOps.AirGap.Policy. |
|
||||
| AUDIT-0030-T | DONE | Test coverage audit for StellaOps.AirGap.Policy. |
|
||||
| AUDIT-0030-A | TODO | Pending approval for changes. |
|
||||
10
src/AirGap/StellaOps.AirGap.Time/TASKS.md
Normal file
10
src/AirGap/StellaOps.AirGap.Time/TASKS.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# AirGap Time Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0034-M | DONE | Maintainability audit for StellaOps.AirGap.Time. |
|
||||
| AUDIT-0034-T | DONE | Test coverage audit for StellaOps.AirGap.Time. |
|
||||
| AUDIT-0034-A | TODO | Pending approval for changes. |
|
||||
@@ -88,9 +88,11 @@ public sealed class BundleBuilder : IBundleBuilder
|
||||
var targetPath = Path.Combine(outputPath, source.RelativePath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath) ?? outputPath);
|
||||
|
||||
await using var input = File.OpenRead(source.SourcePath);
|
||||
await using var output = File.Create(targetPath);
|
||||
await input.CopyToAsync(output, ct).ConfigureAwait(false);
|
||||
await using (var input = File.OpenRead(source.SourcePath))
|
||||
await using (var output = File.Create(targetPath))
|
||||
{
|
||||
await input.CopyToAsync(output, ct).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await using var digestStream = File.OpenRead(targetPath);
|
||||
var hash = await SHA256.HashDataAsync(digestStream, ct).ConfigureAwait(false);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# AirGap Persistence Guild Charter
|
||||
|
||||
## Working Directory
|
||||
- `src/AirGap/__Libraries/StellaOps.AirGap.Persistence`
|
||||
|
||||
## Scope
|
||||
- PostgreSQL persistence for AirGap state and bundle version history.
|
||||
- Data source configuration, schema management, and repository wiring.
|
||||
- EF Core context scaffolding for AirGap data models.
|
||||
|
||||
## Required Reading
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/airgap/bundle-repositories.md`
|
||||
- `docs/airgap/airgap-mode.md`
|
||||
|
||||
## Working Agreements
|
||||
- Update task status in the sprint tracker and local `TASKS.md`.
|
||||
- Keep schema changes deterministic and migration-driven.
|
||||
- Use configured schema names consistently (no hard-coded schema drift).
|
||||
- Avoid cross-module edits unless the sprint explicitly permits them.
|
||||
|
||||
## Testing Rules
|
||||
- Use Postgres test fixtures or Testcontainers; no network.
|
||||
- Mark integration tests as Integration, not Unit.
|
||||
- Keep data ordering deterministic with explicit ORDER BY clauses.
|
||||
@@ -0,0 +1,61 @@
|
||||
-- AirGap Schema Migration 001: Initial Schema
|
||||
-- Creates AirGap state and bundle version tracking tables.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS state (
|
||||
id TEXT NOT NULL,
|
||||
tenant_id TEXT NOT NULL PRIMARY KEY,
|
||||
sealed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
policy_hash TEXT,
|
||||
time_anchor JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
last_transition_at TIMESTAMPTZ NOT NULL DEFAULT '0001-01-01T00:00:00Z',
|
||||
staleness_budget JSONB NOT NULL DEFAULT '{"warningSeconds":3600,"breachSeconds":7200}'::jsonb,
|
||||
drift_baseline_seconds BIGINT NOT NULL DEFAULT 0,
|
||||
content_budgets JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_airgap_state_tenant ON state(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_airgap_state_sealed ON state(sealed) WHERE sealed = TRUE;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bundle_versions (
|
||||
tenant_id TEXT NOT NULL,
|
||||
bundle_type TEXT NOT NULL,
|
||||
version_string TEXT NOT NULL,
|
||||
major INTEGER NOT NULL,
|
||||
minor INTEGER NOT NULL,
|
||||
patch INTEGER NOT NULL,
|
||||
prerelease TEXT,
|
||||
bundle_created_at TIMESTAMPTZ NOT NULL,
|
||||
bundle_digest TEXT NOT NULL,
|
||||
activated_at TIMESTAMPTZ NOT NULL,
|
||||
was_force_activated BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
force_activate_reason TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (tenant_id, bundle_type)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_airgap_bundle_versions_tenant
|
||||
ON bundle_versions(tenant_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bundle_version_history (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL,
|
||||
bundle_type TEXT NOT NULL,
|
||||
version_string TEXT NOT NULL,
|
||||
major INTEGER NOT NULL,
|
||||
minor INTEGER NOT NULL,
|
||||
patch INTEGER NOT NULL,
|
||||
prerelease TEXT,
|
||||
bundle_created_at TIMESTAMPTZ NOT NULL,
|
||||
bundle_digest TEXT NOT NULL,
|
||||
activated_at TIMESTAMPTZ NOT NULL,
|
||||
deactivated_at TIMESTAMPTZ,
|
||||
was_force_activated BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
force_activate_reason TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_airgap_bundle_version_history_tenant
|
||||
ON bundle_version_history(tenant_id, bundle_type, activated_at DESC);
|
||||
@@ -30,7 +30,7 @@ public sealed class PostgresAirGapStateStore : RepositoryBase<AirGapDataSource>,
|
||||
const string sql = """
|
||||
SELECT id, tenant_id, sealed, policy_hash, time_anchor, last_transition_at,
|
||||
staleness_budget, drift_baseline_seconds, content_budgets
|
||||
FROM airgap.state
|
||||
FROM state
|
||||
WHERE LOWER(tenant_id) = LOWER(@tenant_id);
|
||||
""";
|
||||
|
||||
@@ -54,7 +54,7 @@ public sealed class PostgresAirGapStateStore : RepositoryBase<AirGapDataSource>,
|
||||
|
||||
await using var connection = await DataSource.OpenConnectionAsync("public", "writer", cancellationToken).ConfigureAwait(false);
|
||||
const string sql = """
|
||||
INSERT INTO airgap.state (
|
||||
INSERT INTO state (
|
||||
id, tenant_id, sealed, policy_hash, time_anchor, last_transition_at,
|
||||
staleness_budget, drift_baseline_seconds, content_budgets
|
||||
)
|
||||
@@ -245,22 +245,25 @@ public sealed class PostgresAirGapStateStore : RepositoryBase<AirGapDataSource>,
|
||||
}
|
||||
|
||||
await using var connection = await DataSource.OpenSystemConnectionAsync(cancellationToken).ConfigureAwait(false);
|
||||
const string sql = """
|
||||
CREATE SCHEMA IF NOT EXISTS airgap;
|
||||
CREATE TABLE IF NOT EXISTS airgap.state (
|
||||
var schemaName = DataSource.SchemaName ?? "public";
|
||||
var quotedSchema = QuoteIdentifier(schemaName);
|
||||
var sql = $$"""
|
||||
CREATE SCHEMA IF NOT EXISTS {{quotedSchema}};
|
||||
CREATE TABLE IF NOT EXISTS {{quotedSchema}}.state (
|
||||
id TEXT NOT NULL,
|
||||
tenant_id TEXT NOT NULL PRIMARY KEY,
|
||||
sealed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
policy_hash TEXT,
|
||||
time_anchor JSONB NOT NULL DEFAULT '{}',
|
||||
time_anchor JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
last_transition_at TIMESTAMPTZ NOT NULL DEFAULT '0001-01-01T00:00:00Z',
|
||||
staleness_budget JSONB NOT NULL DEFAULT '{"warningSeconds":3600,"breachSeconds":7200}',
|
||||
staleness_budget JSONB NOT NULL DEFAULT '{"warningSeconds":3600,"breachSeconds":7200}'::jsonb,
|
||||
drift_baseline_seconds BIGINT NOT NULL DEFAULT 0,
|
||||
content_budgets JSONB NOT NULL DEFAULT '{}',
|
||||
content_budgets JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_airgap_state_sealed ON airgap.state(sealed) WHERE sealed = TRUE;
|
||||
CREATE INDEX IF NOT EXISTS idx_airgap_state_tenant ON {{quotedSchema}}.state(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_airgap_state_sealed ON {{quotedSchema}}.state(sealed) WHERE sealed = TRUE;
|
||||
""";
|
||||
|
||||
await using var command = CreateCommand(sql, connection);
|
||||
@@ -272,4 +275,10 @@ public sealed class PostgresAirGapStateStore : RepositoryBase<AirGapDataSource>,
|
||||
_initLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private static string QuoteIdentifier(string identifier)
|
||||
{
|
||||
var escaped = identifier.Replace("\"", "\"\"", StringComparison.Ordinal);
|
||||
return $"\"{escaped}\"";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public sealed class PostgresBundleVersionStore : RepositoryBase<AirGapDataSource
|
||||
const string sql = """
|
||||
SELECT tenant_id, bundle_type, version_string, major, minor, patch, prerelease,
|
||||
bundle_created_at, bundle_digest, activated_at, was_force_activated, force_activate_reason
|
||||
FROM airgap.bundle_versions
|
||||
FROM bundle_versions
|
||||
WHERE tenant_id = @tenant_id AND bundle_type = @bundle_type;
|
||||
""";
|
||||
|
||||
@@ -59,7 +59,7 @@ public sealed class PostgresBundleVersionStore : RepositoryBase<AirGapDataSource
|
||||
await using var tx = await connection.BeginTransactionAsync(ct).ConfigureAwait(false);
|
||||
|
||||
const string closeHistorySql = """
|
||||
UPDATE airgap.bundle_version_history
|
||||
UPDATE bundle_version_history
|
||||
SET deactivated_at = @activated_at
|
||||
WHERE tenant_id = @tenant_id AND bundle_type = @bundle_type AND deactivated_at IS NULL;
|
||||
""";
|
||||
@@ -74,7 +74,7 @@ public sealed class PostgresBundleVersionStore : RepositoryBase<AirGapDataSource
|
||||
}
|
||||
|
||||
const string historySql = """
|
||||
INSERT INTO airgap.bundle_version_history (
|
||||
INSERT INTO bundle_version_history (
|
||||
tenant_id, bundle_type, version_string, major, minor, patch, prerelease,
|
||||
bundle_created_at, bundle_digest, activated_at, deactivated_at, was_force_activated, force_activate_reason
|
||||
)
|
||||
@@ -103,7 +103,7 @@ public sealed class PostgresBundleVersionStore : RepositoryBase<AirGapDataSource
|
||||
}
|
||||
|
||||
const string upsertSql = """
|
||||
INSERT INTO airgap.bundle_versions (
|
||||
INSERT INTO bundle_versions (
|
||||
tenant_id, bundle_type, version_string, major, minor, patch, prerelease,
|
||||
bundle_created_at, bundle_digest, activated_at, was_force_activated, force_activate_reason
|
||||
)
|
||||
@@ -169,7 +169,7 @@ public sealed class PostgresBundleVersionStore : RepositoryBase<AirGapDataSource
|
||||
const string sql = """
|
||||
SELECT tenant_id, bundle_type, version_string, major, minor, patch, prerelease,
|
||||
bundle_created_at, bundle_digest, activated_at, was_force_activated, force_activate_reason
|
||||
FROM airgap.bundle_version_history
|
||||
FROM bundle_version_history
|
||||
WHERE tenant_id = @tenant_id AND bundle_type = @bundle_type
|
||||
ORDER BY activated_at DESC
|
||||
LIMIT @limit;
|
||||
@@ -236,10 +236,12 @@ public sealed class PostgresBundleVersionStore : RepositoryBase<AirGapDataSource
|
||||
}
|
||||
|
||||
await using var connection = await DataSource.OpenSystemConnectionAsync(ct).ConfigureAwait(false);
|
||||
const string sql = """
|
||||
CREATE SCHEMA IF NOT EXISTS airgap;
|
||||
var schemaName = DataSource.SchemaName ?? "public";
|
||||
var quotedSchema = QuoteIdentifier(schemaName);
|
||||
var sql = $$"""
|
||||
CREATE SCHEMA IF NOT EXISTS {{quotedSchema}};
|
||||
|
||||
CREATE TABLE IF NOT EXISTS airgap.bundle_versions (
|
||||
CREATE TABLE IF NOT EXISTS {{quotedSchema}}.bundle_versions (
|
||||
tenant_id TEXT NOT NULL,
|
||||
bundle_type TEXT NOT NULL,
|
||||
version_string TEXT NOT NULL,
|
||||
@@ -258,9 +260,9 @@ public sealed class PostgresBundleVersionStore : RepositoryBase<AirGapDataSource
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_airgap_bundle_versions_tenant
|
||||
ON airgap.bundle_versions(tenant_id);
|
||||
ON {{quotedSchema}}.bundle_versions(tenant_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS airgap.bundle_version_history (
|
||||
CREATE TABLE IF NOT EXISTS {{quotedSchema}}.bundle_version_history (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL,
|
||||
bundle_type TEXT NOT NULL,
|
||||
@@ -279,7 +281,7 @@ public sealed class PostgresBundleVersionStore : RepositoryBase<AirGapDataSource
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_airgap_bundle_version_history_tenant
|
||||
ON airgap.bundle_version_history(tenant_id, bundle_type, activated_at DESC);
|
||||
ON {{quotedSchema}}.bundle_version_history(tenant_id, bundle_type, activated_at DESC);
|
||||
""";
|
||||
|
||||
await using var command = CreateCommand(sql, connection);
|
||||
@@ -293,4 +295,10 @@ public sealed class PostgresBundleVersionStore : RepositoryBase<AirGapDataSource
|
||||
}
|
||||
|
||||
private static string NormalizeKey(string value) => value.Trim().ToLowerInvariant();
|
||||
|
||||
private static string QuoteIdentifier(string identifier)
|
||||
{
|
||||
var escaped = identifier.Replace("\"", "\"\"", StringComparison.Ordinal);
|
||||
return $"\"{escaped}\"";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
<Description>Consolidated persistence layer for StellaOps AirGap module</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Migrations\**\*.sql" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" PrivateAssets="all" />
|
||||
|
||||
10
src/AirGap/__Libraries/StellaOps.AirGap.Persistence/TASKS.md
Normal file
10
src/AirGap/__Libraries/StellaOps.AirGap.Persistence/TASKS.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# AirGap Persistence Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0028-M | DONE | Maintainability audit for StellaOps.AirGap.Persistence. |
|
||||
| AUDIT-0028-T | DONE | Test coverage audit for StellaOps.AirGap.Persistence. |
|
||||
| AUDIT-0028-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,3 @@
|
||||
using Xunit;
|
||||
|
||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||
@@ -3,6 +3,7 @@
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseConcelierTestInfra>false</UseConcelierTestInfra>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -14,4 +15,4 @@
|
||||
<ProjectReference Include="../../StellaOps.AirGap.Bundle/StellaOps.AirGap.Bundle.csproj" />
|
||||
<ProjectReference Include="../../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
27
src/AirGap/__Tests/StellaOps.AirGap.Importer.Tests/AGENTS.md
Normal file
27
src/AirGap/__Tests/StellaOps.AirGap.Importer.Tests/AGENTS.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# AirGap Importer Tests Guild Charter
|
||||
|
||||
## Working Directory
|
||||
- `src/AirGap/__Tests/StellaOps.AirGap.Importer.Tests`
|
||||
|
||||
## Scope
|
||||
- Unit and integration tests for AirGap Importer validation, quarantine, versioning, and reconciliation flows.
|
||||
- Deterministic fixtures for DSSE, TUF, SBOM parsing, and evidence graph outputs.
|
||||
- Offline-only inputs (no network, no external services).
|
||||
|
||||
## Required Reading
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/airgap/importer-scaffold.md`
|
||||
- `docs/airgap/airgap-mode.md`
|
||||
|
||||
## Working Agreements
|
||||
- Update task status in the sprint tracker and local `TASKS.md` for this directory.
|
||||
- Keep tests deterministic (fixed time, fixed IDs, stable ordering).
|
||||
- Prefer shared temp directory helpers and ensure cleanup.
|
||||
- Do not silently skip fixture-based tests; mark explicit skip when fixtures are missing.
|
||||
|
||||
## Testing Rules
|
||||
- Use `Unit` vs `Integration` trait categories consistently.
|
||||
- Use WebApplicationFactory only when exercising HTTP endpoints.
|
||||
- Keep fixtures and golden files under this directory; no downloads.
|
||||
10
src/AirGap/__Tests/StellaOps.AirGap.Importer.Tests/TASKS.md
Normal file
10
src/AirGap/__Tests/StellaOps.AirGap.Importer.Tests/TASKS.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# AirGap Importer Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0027-M | DONE | Maintainability audit for StellaOps.AirGap.Importer.Tests. |
|
||||
| AUDIT-0027-T | DONE | Test coverage audit for StellaOps.AirGap.Importer.Tests. |
|
||||
| AUDIT-0027-A | TODO | Pending approval for changes. |
|
||||
@@ -0,0 +1,25 @@
|
||||
# AirGap Persistence Tests Guild Charter
|
||||
|
||||
## Working Directory
|
||||
- `src/AirGap/__Tests/StellaOps.AirGap.Persistence.Tests`
|
||||
|
||||
## Scope
|
||||
- Integration and unit tests for AirGap persistence stores and schema behavior.
|
||||
- Deterministic validation of state and bundle version storage.
|
||||
|
||||
## Required Reading
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/airgap/bundle-repositories.md`
|
||||
- `docs/airgap/airgap-mode.md`
|
||||
|
||||
## Working Agreements
|
||||
- Update task status in the sprint tracker and local `TASKS.md`.
|
||||
- Keep tests deterministic (fixed time, fixed IDs, stable ordering).
|
||||
- Prefer shared temp directory helpers and ensure cleanup.
|
||||
- Categorize integration tests correctly; avoid "Unit" for Postgres-backed tests.
|
||||
|
||||
## Testing Rules
|
||||
- Use the AirGap Postgres fixture; no network.
|
||||
- Validate schema names, indexes, and ordering explicitly in assertions.
|
||||
@@ -17,7 +17,7 @@ public sealed class AirGapPostgresFixture : PostgresIntegrationFixture, ICollect
|
||||
|
||||
protected override string GetModuleName() => "AirGap";
|
||||
|
||||
protected override string? GetResourcePrefix() => "Migrations";
|
||||
protected override string? GetResourcePrefix() => null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all table names in the test schema.
|
||||
|
||||
@@ -37,7 +37,7 @@ public sealed class AirGapStorageIntegrationTests : IAsyncLifetime
|
||||
var options = Options.Create(new PostgresOptions
|
||||
{
|
||||
ConnectionString = fixture.ConnectionString,
|
||||
SchemaName = AirGapDataSource.DefaultSchemaName,
|
||||
SchemaName = fixture.SchemaName,
|
||||
AutoMigrate = false
|
||||
});
|
||||
|
||||
@@ -64,9 +64,9 @@ public sealed class AirGapStorageIntegrationTests : IAsyncLifetime
|
||||
// Arrange
|
||||
var expectedTables = new[]
|
||||
{
|
||||
"airgap_state",
|
||||
"airgap_bundles",
|
||||
"airgap_import_log"
|
||||
"state",
|
||||
"bundle_versions",
|
||||
"bundle_version_history"
|
||||
};
|
||||
|
||||
// Act
|
||||
@@ -88,7 +88,7 @@ public sealed class AirGapStorageIntegrationTests : IAsyncLifetime
|
||||
var expectedColumns = new[] { "tenant_id", "sealed", "policy_hash", "time_anchor", "created_at", "updated_at" };
|
||||
|
||||
// Act
|
||||
var columns = await _fixture.GetColumnNamesAsync("airgap_state");
|
||||
var columns = await _fixture.GetColumnNamesAsync("state");
|
||||
|
||||
// Assert
|
||||
foreach (var expectedColumn in expectedColumns)
|
||||
@@ -117,7 +117,7 @@ public sealed class AirGapStorageIntegrationTests : IAsyncLifetime
|
||||
public async Task Migration_HasTenantIndex()
|
||||
{
|
||||
// Act
|
||||
var indexes = await _fixture.GetIndexNamesAsync("airgap_state");
|
||||
var indexes = await _fixture.GetIndexNamesAsync("state");
|
||||
|
||||
// Assert
|
||||
indexes.Should().Contain(i => i.Contains("tenant", StringComparison.OrdinalIgnoreCase),
|
||||
|
||||
@@ -25,7 +25,7 @@ public sealed class PostgresAirGapStateStoreTests : IAsyncLifetime
|
||||
var options = Options.Create(new PostgresOptions
|
||||
{
|
||||
ConnectionString = fixture.ConnectionString,
|
||||
SchemaName = AirGapDataSource.DefaultSchemaName,
|
||||
SchemaName = fixture.SchemaName,
|
||||
AutoMigrate = false
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# AirGap Persistence Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0029-M | DONE | Maintainability audit for StellaOps.AirGap.Persistence.Tests. |
|
||||
| AUDIT-0029-T | DONE | Test coverage audit for StellaOps.AirGap.Persistence.Tests. |
|
||||
| AUDIT-0029-A | TODO | Pending approval for changes. |
|
||||
22
src/AirGap/__Tests/StellaOps.AirGap.Time.Tests/AGENTS.md
Normal file
22
src/AirGap/__Tests/StellaOps.AirGap.Time.Tests/AGENTS.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# AirGap Time Tests Charter
|
||||
|
||||
## Working Directory
|
||||
- `src/AirGap/__Tests/StellaOps.AirGap.Time.Tests`
|
||||
|
||||
## Scope
|
||||
- Unit and integration tests for time anchors, staleness evaluation, and verification services.
|
||||
|
||||
## Required Reading
|
||||
- `docs/airgap/staleness-and-time.md`
|
||||
- `docs/airgap/airgap-mode.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `src/AirGap/StellaOps.AirGap.Time/AGENTS.md`
|
||||
|
||||
## Working Agreements
|
||||
- Update task status in the sprint tracker and local `TASKS.md`.
|
||||
- Keep tests deterministic (fixed time and IDs).
|
||||
- Clean up temp artifacts created during tests.
|
||||
|
||||
## Testing Rules
|
||||
- Include happy-path verification tests with deterministic fixtures.
|
||||
- Exercise health checks and controller endpoints where applicable.
|
||||
10
src/AirGap/__Tests/StellaOps.AirGap.Time.Tests/TASKS.md
Normal file
10
src/AirGap/__Tests/StellaOps.AirGap.Time.Tests/TASKS.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# AirGap Time Tests Task Board
|
||||
|
||||
This board mirrors active sprint tasks for this module.
|
||||
Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.md`.
|
||||
|
||||
| Task ID | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| AUDIT-0035-M | DONE | Maintainability audit for StellaOps.AirGap.Time.Tests. |
|
||||
| AUDIT-0035-T | DONE | Test coverage audit for StellaOps.AirGap.Time.Tests. |
|
||||
| AUDIT-0035-A | TODO | Pending approval for changes. |
|
||||
@@ -23,12 +23,12 @@ public class TimeAnchorLoaderTests
|
||||
[Fact]
|
||||
public void LoadsHexToken()
|
||||
{
|
||||
var loader = Build();
|
||||
var loader = Build(allowUntrusted: true);
|
||||
var hex = "01020304";
|
||||
var trust = new[] { new TimeTrustRoot("k1", new byte[32], "ed25519") };
|
||||
var result = loader.TryLoadHex(hex, TimeTokenFormat.Roughtime, trust, out var anchor);
|
||||
var result = loader.TryLoadHex(hex, TimeTokenFormat.Roughtime, Array.Empty<TimeTrustRoot>(), out var anchor);
|
||||
|
||||
Assert.True(result.IsValid);
|
||||
Assert.Equal("untrusted-no-trust-roots", result.Reason);
|
||||
Assert.Equal("Roughtime", anchor.Format);
|
||||
}
|
||||
|
||||
@@ -58,9 +58,9 @@ public class TimeAnchorLoaderTests
|
||||
Assert.Equal("trust-roots-required", result.Reason);
|
||||
}
|
||||
|
||||
private static TimeAnchorLoader Build()
|
||||
private static TimeAnchorLoader Build(bool allowUntrusted = false)
|
||||
{
|
||||
var options = Options.Create(new AirGapOptions { AllowUntrustedAnchors = false });
|
||||
var options = Options.Create(new AirGapOptions { AllowUntrustedAnchors = allowUntrusted });
|
||||
return new TimeAnchorLoader(new TimeVerificationService(), new TimeTokenParser(), options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,18 +14,18 @@ public class TimeVerificationServiceTests
|
||||
var svc = new TimeVerificationService();
|
||||
var result = svc.Verify(new byte[] { 0x01 }, TimeTokenFormat.Roughtime, Array.Empty<TimeTrustRoot>(), out _);
|
||||
Assert.False(result.IsValid);
|
||||
Assert.Equal("trust-roots-required", result.Reason);
|
||||
Assert.Equal("roughtime-trust-roots-required", result.Reason);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void SucceedsForRoughtimeWithTrustRoot()
|
||||
public void FailsForRoughtimeWithInvalidToken()
|
||||
{
|
||||
var svc = new TimeVerificationService();
|
||||
var trust = new[] { new TimeTrustRoot("k1", new byte[] { 0x01 }, "rsassa-pss-sha256") };
|
||||
var result = svc.Verify(new byte[] { 0x01, 0x02 }, TimeTokenFormat.Roughtime, trust, out var anchor);
|
||||
Assert.True(result.IsValid);
|
||||
Assert.Equal("Roughtime", anchor.Format);
|
||||
Assert.Equal("k1", anchor.SignatureFingerprint);
|
||||
Assert.False(result.IsValid);
|
||||
Assert.Equal("roughtime-message-too-short", result.Reason);
|
||||
Assert.Equal("unknown", anchor.Format);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user