more audit work

This commit is contained in:
master
2026-01-08 10:21:51 +02:00
parent 43c02081ef
commit 51cf4bc16c
546 changed files with 36721 additions and 4003 deletions

View File

@@ -15,8 +15,8 @@ public sealed class CannedHttpMessageHandlerTests
handler.SetFallback(_ => new HttpResponseMessage(HttpStatusCode.NotFound));
using var client = handler.CreateClient();
var firstResponse = await client.GetAsync(requestUri);
var secondResponse = await client.GetAsync(new Uri("https://example.test/other"));
var firstResponse = await client.GetAsync(requestUri, TestContext.Current.CancellationToken);
var secondResponse = await client.GetAsync(new Uri("https://example.test/other"), TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, firstResponse.StatusCode);
Assert.Equal(HttpStatusCode.NotFound, secondResponse.StatusCode);
@@ -32,6 +32,6 @@ public sealed class CannedHttpMessageHandlerTests
handler.AddException(HttpMethod.Get, requestUri, new InvalidOperationException("boom"));
using var client = handler.CreateClient();
await Assert.ThrowsAsync<InvalidOperationException>(() => client.GetAsync(requestUri));
await Assert.ThrowsAsync<InvalidOperationException>(() => client.GetAsync(requestUri, TestContext.Current.CancellationToken));
}
}

View File

@@ -1,35 +1,29 @@
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Concelier.InMemoryRunner;
using StellaOps.Concelier.Documents;
using StellaOps.Concelier.InMemoryDriver;
using StellaOps.Aoc;
using StellaOps.Concelier.Connector.Common.Fetch;
using StellaOps.Concelier.Connector.Common.Http;
using StellaOps.Concelier.Core.Aoc;
using StellaOps.Concelier.Core.Linksets;
using StellaOps.Concelier.RawModels;
using StellaOps.Concelier.Storage;
using StellaOps.Cryptography;
using LegacyContracts = StellaOps.Concelier.Storage;
using StorageContracts = StellaOps.Concelier.Storage.Contracts;
namespace StellaOps.Concelier.Connector.Common.Tests;
public sealed class SourceFetchServiceGuardTests : IAsyncLifetime
{
private readonly InMemoryDbRunner _runner;
private readonly IStorageDatabase _database;
private readonly RawDocumentStorage _rawStorage;
private readonly ICryptoHash _hash;
public SourceFetchServiceGuardTests()
{
_runner = InMemoryDbRunner.Start(singleNodeReplSet: true);
var client = new InMemoryClient(_runner.ConnectionString);
_database = client.GetDatabase($"source-fetch-guard-{Guid.NewGuid():N}");
_rawStorage = new RawDocumentStorage();
_hash = CryptoHashFactory.CreateDefault();
}
@@ -41,15 +35,15 @@ public sealed class SourceFetchServiceGuardTests : IAsyncLifetime
var handler = new StaticHttpMessageHandler(() => CreateSuccessResponse(responsePayload));
var client = new HttpClient(handler, disposeHandler: false);
var httpClientFactory = new StaticHttpClientFactory(client);
var documentStore = new RecordingDocumentStore();
var documentStore = new RecordingStorageDocumentStore();
var legacyStore = new NoopDocumentStore();
var guard = new RecordingAdvisoryRawWriteGuard();
var jitter = new NoJitterSource();
var httpOptions = new TestOptionsMonitor<StellaOps.Concelier.Connector.Common.Http.SourceHttpClientOptions>(new StellaOps.Concelier.Connector.Common.Http.SourceHttpClientOptions());
var storageOptions = Options.Create(new StorageOptions
var storageOptions = Options.Create(new LegacyContracts.StorageOptions
{
ConnectionString = _runner.ConnectionString,
DatabaseName = _database.DatabaseNamespace.DatabaseName,
DefaultTenant = "tenant-default",
});
var linksetMapper = new NoopAdvisoryLinksetMapper();
@@ -57,6 +51,7 @@ public sealed class SourceFetchServiceGuardTests : IAsyncLifetime
var service = new SourceFetchService(
httpClientFactory,
_rawStorage,
legacyStore,
documentStore,
NullLogger<SourceFetchService>.Instance,
jitter,
@@ -90,11 +85,11 @@ public sealed class SourceFetchServiceGuardTests : IAsyncLifetime
Assert.True(documentStore.UpsertCount > 0);
Assert.Equal("msrc", documentStore.LastRecord!.Metadata!["source.vendor"]);
Assert.Equal("tenant-default", documentStore.LastRecord.Metadata!["tenant"]);
Assert.NotNull(documentStore.LastRecord.PayloadId);
Assert.NotNull(documentStore.LastRecord.Payload);
// verify raw payload stored
var filesCollection = _database.GetCollection<DocumentObject>("documents.files");
var count = await filesCollection.CountDocumentsAsync(FilterDefinition<DocumentObject>.Empty);
Assert.Equal(1, count);
var rawPayload = await _rawStorage.DownloadAsync(documentStore.LastRecord.PayloadId!.Value, CancellationToken.None);
Assert.Equal(responsePayload, Encoding.UTF8.GetString(rawPayload));
}
[Fact]
@@ -103,15 +98,15 @@ public sealed class SourceFetchServiceGuardTests : IAsyncLifetime
var handler = new StaticHttpMessageHandler(() => CreateSuccessResponse("{\"id\":\"CVE-2025-2222\"}"));
var client = new HttpClient(handler, disposeHandler: false);
var httpClientFactory = new StaticHttpClientFactory(client);
var documentStore = new RecordingDocumentStore();
var documentStore = new RecordingStorageDocumentStore();
var legacyStore = new NoopDocumentStore();
var guard = new RecordingAdvisoryRawWriteGuard { ShouldThrow = true };
var jitter = new NoJitterSource();
var httpOptions = new TestOptionsMonitor<StellaOps.Concelier.Connector.Common.Http.SourceHttpClientOptions>(new StellaOps.Concelier.Connector.Common.Http.SourceHttpClientOptions());
var storageOptions = Options.Create(new StorageOptions
var storageOptions = Options.Create(new LegacyContracts.StorageOptions
{
ConnectionString = _runner.ConnectionString,
DatabaseName = _database.DatabaseNamespace.DatabaseName,
DefaultTenant = "tenant-default",
});
var linksetMapper = new NoopAdvisoryLinksetMapper();
@@ -119,6 +114,7 @@ public sealed class SourceFetchServiceGuardTests : IAsyncLifetime
var service = new SourceFetchService(
httpClientFactory,
_rawStorage,
legacyStore,
documentStore,
NullLogger<SourceFetchService>.Instance,
jitter,
@@ -140,16 +136,14 @@ public sealed class SourceFetchServiceGuardTests : IAsyncLifetime
await Assert.ThrowsAsync<ConcelierAocGuardException>(() => service.FetchAsync(request, CancellationToken.None));
Assert.Equal(0, documentStore.UpsertCount);
var filesCollection = _database.GetCollection<DocumentObject>("documents.files");
var count = await filesCollection.CountDocumentsAsync(FilterDefinition<DocumentObject>.Empty);
Assert.Equal(0, count);
var recordId = CreateDeterministicGuid($"{request.SourceName}:{request.RequestUri}");
await Assert.ThrowsAsync<FileNotFoundException>(() => _rawStorage.DownloadAsync(recordId, CancellationToken.None));
}
public ValueTask InitializeAsync() => ValueTask.CompletedTask;
public ValueTask DisposeAsync()
{
_runner.Dispose();
return ValueTask.CompletedTask;
}
@@ -184,24 +178,59 @@ public sealed class SourceFetchServiceGuardTests : IAsyncLifetime
=> Task.FromResult(_responseFactory());
}
private sealed class RecordingDocumentStore : IDocumentStore
private sealed class RecordingStorageDocumentStore : StorageContracts.IStorageDocumentStore
{
public DocumentRecord? LastRecord { get; private set; }
private readonly Dictionary<Guid, StorageContracts.StorageDocument> _byId = new();
private readonly Dictionary<(string Source, string Uri), StorageContracts.StorageDocument> _bySourceUri = new();
public StorageContracts.StorageDocument? LastRecord { get; private set; }
public int UpsertCount { get; private set; }
public Task<DocumentRecord> UpsertAsync(DocumentRecord record, CancellationToken cancellationToken)
public Task<StorageContracts.StorageDocument?> FindBySourceAndUriAsync(string sourceName, string uri, CancellationToken cancellationToken)
{
_bySourceUri.TryGetValue((sourceName, uri), out var record);
return Task.FromResult<StorageContracts.StorageDocument?>(record);
}
public Task<StorageContracts.StorageDocument?> FindAsync(Guid id, CancellationToken cancellationToken)
{
_byId.TryGetValue(id, out var record);
return Task.FromResult<StorageContracts.StorageDocument?>(record);
}
public Task<StorageContracts.StorageDocument> UpsertAsync(StorageContracts.StorageDocument record, CancellationToken cancellationToken)
{
UpsertCount++;
LastRecord = record;
_byId[record.Id] = record;
_bySourceUri[(record.SourceName, record.Uri)] = record;
return Task.FromResult(record);
}
public Task<DocumentRecord?> FindBySourceAndUriAsync(string sourceName, string uri, CancellationToken cancellationToken)
=> Task.FromResult<DocumentRecord?>(null);
public Task UpdateStatusAsync(Guid id, string status, CancellationToken cancellationToken)
{
if (_byId.TryGetValue(id, out var existing))
{
var updated = existing with { Status = status };
_byId[id] = updated;
_bySourceUri[(updated.SourceName, updated.Uri)] = updated;
LastRecord = updated;
}
public Task<DocumentRecord?> FindAsync(Guid id, CancellationToken cancellationToken)
=> Task.FromResult<DocumentRecord?>(null);
return Task.CompletedTask;
}
}
private sealed class NoopDocumentStore : LegacyContracts.IDocumentStore
{
public Task<LegacyContracts.DocumentRecord?> FindBySourceAndUriAsync(string sourceName, string uri, CancellationToken cancellationToken)
=> Task.FromResult<LegacyContracts.DocumentRecord?>(null);
public Task<LegacyContracts.DocumentRecord?> FindAsync(Guid id, CancellationToken cancellationToken)
=> Task.FromResult<LegacyContracts.DocumentRecord?>(null);
public Task<LegacyContracts.DocumentRecord> UpsertAsync(LegacyContracts.DocumentRecord record, CancellationToken cancellationToken)
=> Task.FromResult(record);
public Task UpdateStatusAsync(Guid id, string status, CancellationToken cancellationToken)
=> Task.CompletedTask;
@@ -254,6 +283,14 @@ public sealed class SourceFetchServiceGuardTests : IAsyncLifetime
{
public RawLinkset Map(AdvisoryRawDocument document) => new();
}
private static Guid CreateDeterministicGuid(string value)
{
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(value ?? string.Empty));
bytes[6] = (byte)((bytes[6] & 0x0F) | 0x50);
bytes[8] = (byte)((bytes[8] & 0x3F) | 0x80);
return new Guid(bytes.AsSpan(0, 16));
}
}

View File

@@ -1,9 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using StellaOps.Concelier.InMemoryRunner;
using StellaOps.Concelier.Documents;
using StellaOps.Concelier.InMemoryDriver;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Time.Testing;
using StellaOps.Concelier.Connector.Common;
@@ -16,9 +13,6 @@ namespace StellaOps.Concelier.Connector.Common.Tests;
public sealed class SourceStateSeedProcessorTests : IAsyncLifetime
{
private readonly InMemoryDbRunner _runner;
private readonly InMemoryClient _client;
private readonly IStorageDatabase _database;
private readonly DocumentStore _documentStore;
private readonly RawDocumentStorage _rawStorage;
private readonly InMemorySourceStateRepository _stateRepository;
@@ -27,10 +21,7 @@ public sealed class SourceStateSeedProcessorTests : IAsyncLifetime
public SourceStateSeedProcessorTests()
{
_runner = InMemoryDbRunner.Start(singleNodeReplSet: true);
_client = new InMemoryClient(_runner.ConnectionString);
_database = _client.GetDatabase($"source-state-seed-{Guid.NewGuid():N}");
_documentStore = new DocumentStore(_database, NullLogger<DocumentStore>.Instance);
_documentStore = new DocumentStore();
_rawStorage = new RawDocumentStorage();
_stateRepository = new InMemorySourceStateRepository();
_timeProvider = new FakeTimeProvider(new DateTimeOffset(2025, 10, 28, 12, 0, 0, TimeSpan.Zero));
@@ -98,16 +89,16 @@ public sealed class SourceStateSeedProcessorTests : IAsyncLifetime
Assert.NotNull(storedDocument.Metadata);
Assert.Equal("value", storedDocument.Metadata!["test.meta"]);
var filesCollection = _database.GetCollection<DocumentObject>("documents.files");
var fileCount = await filesCollection.CountDocumentsAsync(FilterDefinition<DocumentObject>.Empty);
Assert.Equal(1, fileCount);
var payload = await _rawStorage.DownloadAsync(storedDocument.PayloadId!.Value, CancellationToken.None);
Assert.Equal("{\"id\":\"ADV-1\"}", Encoding.UTF8.GetString(payload));
var state = await _stateRepository.TryGetAsync("vndr.test", CancellationToken.None);
Assert.NotNull(state);
var stateValue = state!;
Assert.Equal(_timeProvider.GetUtcNow().UtcDateTime, stateValue.LastSuccess);
var cursor = stateValue.Cursor;
Assert.NotNull(stateValue.Cursor);
var cursor = stateValue.Cursor!;
var pendingDocs = cursor["pendingDocuments"].AsDocumentArray.Select(v => Guid.Parse(v.AsString)).ToList();
Assert.Contains(documentId, pendingDocs);
@@ -156,9 +147,8 @@ public sealed class SourceStateSeedProcessorTests : IAsyncLifetime
var previousGridId = existingRecord!.PayloadId;
Assert.NotNull(previousGridId);
var filesCollection = _database.GetCollection<DocumentObject>("documents.files");
var initialFiles = await filesCollection.Find(FilterDefinition<DocumentObject>.Empty).ToListAsync();
Assert.Single(initialFiles);
var initialPayload = await _rawStorage.DownloadAsync(previousGridId!.Value, CancellationToken.None);
Assert.Equal("{\"id\":\"ADV-2\",\"rev\":1}", Encoding.UTF8.GetString(initialPayload));
var updatedSpecification = new SourceStateSeedSpecification
{
@@ -190,11 +180,9 @@ public sealed class SourceStateSeedProcessorTests : IAsyncLifetime
Assert.NotNull(refreshedRecord);
Assert.Equal(documentId, refreshedRecord!.Id);
Assert.NotNull(refreshedRecord.PayloadId);
Assert.NotEqual(previousGridId?.ToString(), refreshedRecord.PayloadId?.ToString());
var files = await filesCollection.Find(FilterDefinition<DocumentObject>.Empty).ToListAsync();
Assert.Single(files);
Assert.NotEqual(previousGridId?.ToString(), files[0]["_id"].AsObjectId.ToString());
var updatedPayload = await _rawStorage.DownloadAsync(refreshedRecord.PayloadId!.Value, CancellationToken.None);
Assert.Equal("{\"id\":\"ADV-2\",\"rev\":2}", Encoding.UTF8.GetString(updatedPayload));
}
private SourceStateSeedProcessor CreateProcessor()
@@ -210,8 +198,7 @@ public sealed class SourceStateSeedProcessorTests : IAsyncLifetime
public async ValueTask DisposeAsync()
{
await _client.DropDatabaseAsync(_database.DatabaseNamespace.DatabaseName);
_runner.Dispose();
await Task.CompletedTask;
}
}

View File

@@ -8,3 +8,5 @@ Source of truth: `docs/implplan/SPRINT_20251229_049_BE_csproj_audit_maint_tests.
| AUDIT-0160-M | DONE | Revalidated 2026-01-06; findings recorded in audit report. |
| AUDIT-0160-T | DONE | Revalidated 2026-01-06; findings recorded in audit report. |
| AUDIT-0160-A | DONE | Waived (test project; revalidated 2026-01-06). |
| AUDIT-0374-T | DONE | Revalidated 2026-01-08 (storage store + raw payload checks). |
| AUDIT-0374-A | DONE | Revalidated 2026-01-08 (storage store + raw payload checks). |