up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-13 00:20:26 +02:00
parent e1f1bef4c1
commit 564df71bfb
2376 changed files with 334389 additions and 328032 deletions

View File

@@ -1,39 +1,39 @@
using System;
using System.IO;
using StellaOps.Concelier.WebService.Options;
using Xunit;
namespace StellaOps.Concelier.WebService.Tests;
public sealed class ConcelierOptionsPostConfigureTests
{
[Fact]
using System;
using System.IO;
using StellaOps.Concelier.WebService.Options;
using Xunit;
namespace StellaOps.Concelier.WebService.Tests;
public sealed class ConcelierOptionsPostConfigureTests
{
[Fact]
public void Apply_LoadsClientSecretFromRelativeFile()
{
var tempDirectory = Directory.CreateTempSubdirectory();
try
{
var secretPath = Path.Combine(tempDirectory.FullName, "authority.secret");
File.WriteAllText(secretPath, " concelier-secret ");
var options = new ConcelierOptions
{
Authority = new ConcelierOptions.AuthorityOptions
{
ClientSecretFile = "authority.secret"
}
};
ConcelierOptionsPostConfigure.Apply(options, tempDirectory.FullName);
Assert.Equal("concelier-secret", options.Authority.ClientSecret);
}
finally
{
if (Directory.Exists(tempDirectory.FullName))
{
Directory.Delete(tempDirectory.FullName, recursive: true);
}
var secretPath = Path.Combine(tempDirectory.FullName, "authority.secret");
File.WriteAllText(secretPath, " concelier-secret ");
var options = new ConcelierOptions
{
Authority = new ConcelierOptions.AuthorityOptions
{
ClientSecretFile = "authority.secret"
}
};
ConcelierOptionsPostConfigure.Apply(options, tempDirectory.FullName);
Assert.Equal("concelier-secret", options.Authority.ClientSecret);
}
finally
{
if (Directory.Exists(tempDirectory.FullName))
{
Directory.Delete(tempDirectory.FullName, recursive: true);
}
}
}
@@ -50,15 +50,15 @@ public sealed class ConcelierOptionsPostConfigureTests
{
var options = new ConcelierOptions
{
Authority = new ConcelierOptions.AuthorityOptions
{
ClientSecretFile = "missing.secret"
}
};
var exception = Assert.Throws<InvalidOperationException>(() =>
ConcelierOptionsPostConfigure.Apply(options, AppContext.BaseDirectory));
Assert.Contains("Authority client secret file", exception.Message);
}
}
Authority = new ConcelierOptions.AuthorityOptions
{
ClientSecretFile = "missing.secret"
}
};
var exception = Assert.Throws<InvalidOperationException>(() =>
ConcelierOptionsPostConfigure.Apply(options, AppContext.BaseDirectory));
Assert.Contains("Authority client secret file", exception.Message);
}
}

View File

@@ -1,6 +1,6 @@
using System.Collections.Immutable;
using System.Text.Json;
using StellaOps.Concelier.Bson.Serialization.Attributes;
using StellaOps.Concelier.Documents.Serialization.Attributes;
using StellaOps.Concelier.RawModels;
namespace StellaOps.Concelier.WebService.Tests.Fixtures;
@@ -344,31 +344,31 @@ public sealed record AdvisoryChunkSeedSet(
/// </summary>
public sealed class AdvisorySeedDocument
{
[BsonElement("tenantId")]
[DocumentElement("tenantId")]
public string TenantId { get; init; } = string.Empty;
[BsonElement("advisoryKey")]
[DocumentElement("advisoryKey")]
public string AdvisoryKey { get; init; } = string.Empty;
[BsonElement("source")]
[DocumentElement("source")]
public string Source { get; init; } = string.Empty;
[BsonElement("severity")]
[DocumentElement("severity")]
public string Severity { get; init; } = string.Empty;
[BsonElement("title")]
[DocumentElement("title")]
public string Title { get; init; } = string.Empty;
[BsonElement("description")]
[DocumentElement("description")]
public string Description { get; init; } = string.Empty;
[BsonElement("published")]
[DocumentElement("published")]
public DateTime Published { get; init; }
[BsonElement("modified")]
[DocumentElement("modified")]
public DateTime Modified { get; init; }
[BsonElement("fingerprint")]
[DocumentElement("fingerprint")]
public string Fingerprint { get; init; } = string.Empty;
}
@@ -377,25 +377,25 @@ public sealed class AdvisorySeedDocument
/// </summary>
public sealed class ObservationSeedDocument
{
[BsonElement("tenantId")]
[DocumentElement("tenantId")]
public string TenantId { get; init; } = string.Empty;
[BsonElement("observationId")]
[DocumentElement("observationId")]
public string ObservationId { get; init; } = string.Empty;
[BsonElement("advisoryKey")]
[DocumentElement("advisoryKey")]
public string AdvisoryKey { get; init; } = string.Empty;
[BsonElement("source")]
[DocumentElement("source")]
public string Source { get; init; } = string.Empty;
[BsonElement("format")]
[DocumentElement("format")]
public string Format { get; init; } = string.Empty;
[BsonElement("rawContent")]
[DocumentElement("rawContent")]
public string RawContent { get; init; } = string.Empty;
[BsonElement("createdAt")]
[DocumentElement("createdAt")]
public DateTime CreatedAt { get; init; }
}
@@ -404,15 +404,15 @@ public sealed class ObservationSeedDocument
/// </summary>
public sealed class AliasSeedDocument
{
[BsonElement("tenantId")]
[DocumentElement("tenantId")]
public string TenantId { get; init; } = string.Empty;
[BsonElement("alias")]
[DocumentElement("alias")]
public string Alias { get; init; } = string.Empty;
[BsonElement("canonicalId")]
[DocumentElement("canonicalId")]
public string CanonicalId { get; init; } = string.Empty;
[BsonElement("aliases")]
[DocumentElement("aliases")]
public IReadOnlyList<string> Aliases { get; init; } = Array.Empty<string>();
}

View File

@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using StellaOps.Concelier.Bson.Serialization.Attributes;
using StellaOps.Concelier.Documents.Serialization.Attributes;
namespace StellaOps.Concelier.WebService.Tests;
@@ -10,31 +10,31 @@ namespace StellaOps.Concelier.WebService.Tests;
/// </summary>
internal sealed class AdvisoryLinksetDocument
{
[BsonElement("tenantId")]
[DocumentElement("tenantId")]
public string TenantId { get; init; } = string.Empty;
[BsonElement("source")]
[DocumentElement("source")]
public string Source { get; init; } = string.Empty;
[BsonElement("advisoryId")]
[DocumentElement("advisoryId")]
public string AdvisoryId { get; init; } = string.Empty;
[BsonElement("observations")]
[DocumentElement("observations")]
public IReadOnlyList<string> Observations { get; init; } = Array.Empty<string>();
[BsonElement("createdAt")]
[DocumentElement("createdAt")]
public DateTime CreatedAt { get; init; }
[BsonElement("normalized")]
[DocumentElement("normalized")]
public AdvisoryLinksetNormalizedDocument Normalized { get; init; } = new();
}
internal sealed class AdvisoryLinksetNormalizedDocument
{
[BsonElement("purls")]
[DocumentElement("purls")]
public IReadOnlyList<string> Purls { get; init; } = Array.Empty<string>();
[BsonElement("versions")]
[DocumentElement("versions")]
public IReadOnlyList<string> Versions { get; init; } = Array.Empty<string>();
}

View File

@@ -79,7 +79,7 @@ public sealed class OrchestratorTestWebAppFactory : WebApplicationFactory<Progra
services.AddSingleton<IOptions<ConcelierOptions>>(_ => Microsoft.Extensions.Options.Options.Create(forcedOptions));
// Force Mongo storage options to a deterministic in-memory test DSN.
services.PostConfigure<MongoStorageOptions>(opts =>
services.PostConfigure<StorageOptions>(opts =>
{
opts.ConnectionString = "mongodb://localhost:27017/orch-tests";
opts.DatabaseName = "orch-tests";

View File

@@ -1,29 +1,29 @@
using StellaOps.Plugin;
namespace StellaOps.Concelier.WebService.Tests;
public class PluginLoaderTests
{
private sealed class NullServices : IServiceProvider
{
public object? GetService(Type serviceType) => null;
}
[Fact]
public void ScansConnectorPluginsDirectory()
{
var services = new NullServices();
using StellaOps.Plugin;
namespace StellaOps.Concelier.WebService.Tests;
public class PluginLoaderTests
{
private sealed class NullServices : IServiceProvider
{
public object? GetService(Type serviceType) => null;
}
[Fact]
public void ScansConnectorPluginsDirectory()
{
var services = new NullServices();
var catalog = new PluginCatalog().AddFromDirectory(Path.Combine(AppContext.BaseDirectory, "StellaOps.Concelier.PluginBinaries"));
var plugins = catalog.GetAvailableConnectorPlugins(services);
Assert.NotNull(plugins);
}
[Fact]
public void ScansExporterPluginsDirectory()
{
var services = new NullServices();
var plugins = catalog.GetAvailableConnectorPlugins(services);
Assert.NotNull(plugins);
}
[Fact]
public void ScansExporterPluginsDirectory()
{
var services = new NullServices();
var catalog = new PluginCatalog().AddFromDirectory(Path.Combine(AppContext.BaseDirectory, "StellaOps.Concelier.PluginBinaries"));
var plugins = catalog.GetAvailableExporterPlugins(services);
Assert.NotNull(plugins);
}
}
var plugins = catalog.GetAvailableExporterPlugins(services);
Assert.NotNull(plugins);
}
}

View File

@@ -23,10 +23,10 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Mongo2Go;
using StellaOps.Concelier.Bson;
using StellaOps.Concelier.Bson.IO;
using MongoDB.Driver;
using StellaOps.Concelier.InMemoryRunner;
using StellaOps.Concelier.Documents;
using StellaOps.Concelier.Documents.IO;
using StellaOps.Concelier.InMemoryDriver;
using StellaOps.Concelier.Core.Attestation;
using static StellaOps.Concelier.WebService.Program;
using StellaOps.Concelier.Core.Events;
@@ -63,7 +63,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
private static readonly SymmetricSecurityKey TestSigningKey = new(Encoding.UTF8.GetBytes(TestSigningSecret));
private readonly ITestOutputHelper _output;
private MongoDbRunner _runner = null!;
private InMemoryDbRunner _runner = null!;
private Process? _externalMongo;
private string? _externalMongoDataPath;
private ConcelierApplicationFactory _factory = null!;
@@ -377,7 +377,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
[Fact]
public async Task AdvisoryChunksEndpoint_ReturnsParagraphAnchors()
{
var newestRaw = BsonDocument.Parse(
var newestRaw = DocumentObject.Parse(
"""
{
"summary": {
@@ -391,7 +391,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
]
}
""");
var olderRaw = BsonDocument.Parse(
var olderRaw = DocumentObject.Parse(
"""
{
"summary": {
@@ -423,8 +423,8 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
await SeedObservationDocumentsAsync(documents);
await SeedAdvisoryRawDocumentsAsync(
CreateAdvisoryRawDocument("tenant-a", "nvd", "tenant-a:chunk:newest", newerHash, newestRaw.DeepClone().AsBsonDocument),
CreateAdvisoryRawDocument("tenant-a", "nvd", "tenant-a:chunk:older", olderHash, olderRaw.DeepClone().AsBsonDocument));
CreateAdvisoryRawDocument("tenant-a", "nvd", "tenant-a:chunk:newest", newerHash, newestRaw.DeepClone().AsDocumentObject),
CreateAdvisoryRawDocument("tenant-a", "nvd", "tenant-a:chunk:older", olderHash, olderRaw.DeepClone().AsDocumentObject));
await SeedCanonicalAdvisoriesAsync(
CreateStructuredAdvisory("CVE-2025-0001", "GHSA-2025-0001", "tenant-a:chunk:newest", newerCreatedAt));
@@ -580,7 +580,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
vendor: "osv",
upstreamId: "GHSA-VERIFY-ERR",
contentHash: "sha256:verify-err",
raw: new BsonDocument
raw: new DocumentObject
{
{ "id", "GHSA-VERIFY-ERR" },
{ "severity", "critical" }
@@ -643,9 +643,9 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
public async Task AdvisoryEvidenceEndpoint_ReturnsDocumentsForCanonicalKey()
{
await SeedAdvisoryRawDocumentsAsync(
CreateAdvisoryRawDocument("tenant-a", "vendor-x", "GHSA-2025-0001", "sha256:001", new BsonDocument("id", "GHSA-2025-0001:1")),
CreateAdvisoryRawDocument("tenant-a", "vendor-y", "GHSA-2025-0001", "sha256:002", new BsonDocument("id", "GHSA-2025-0001:2")),
CreateAdvisoryRawDocument("tenant-b", "vendor-x", "GHSA-2025-0001", "sha256:003", new BsonDocument("id", "GHSA-2025-0001:3")));
CreateAdvisoryRawDocument("tenant-a", "vendor-x", "GHSA-2025-0001", "sha256:001", new DocumentObject("id", "GHSA-2025-0001:1")),
CreateAdvisoryRawDocument("tenant-a", "vendor-y", "GHSA-2025-0001", "sha256:002", new DocumentObject("id", "GHSA-2025-0001:2")),
CreateAdvisoryRawDocument("tenant-b", "vendor-x", "GHSA-2025-0001", "sha256:003", new DocumentObject("id", "GHSA-2025-0001:3")));
using var client = _factory.CreateClient();
var response = await client.GetAsync("/vuln/evidence/advisories/ghsa-2025-0001?tenant=tenant-a");
@@ -664,7 +664,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
public async Task AdvisoryEvidenceEndpoint_AttachesAttestationWhenBundleProvided()
{
await SeedAdvisoryRawDocumentsAsync(
CreateAdvisoryRawDocument("tenant-a", "vendor-x", "GHSA-2025-0003", "sha256:201", new BsonDocument("id", "GHSA-2025-0003:1")));
CreateAdvisoryRawDocument("tenant-a", "vendor-x", "GHSA-2025-0003", "sha256:201", new DocumentObject("id", "GHSA-2025-0003:1")));
var repoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..", "..", ".."));
var sampleDir = Path.Combine(repoRoot, "docs", "samples", "evidence-bundle");
@@ -750,8 +750,8 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
public async Task AdvisoryEvidenceEndpoint_FiltersByVendor()
{
await SeedAdvisoryRawDocumentsAsync(
CreateAdvisoryRawDocument("tenant-a", "vendor-x", "GHSA-2025-0002", "sha256:101", new BsonDocument("id", "GHSA-2025-0002:1")),
CreateAdvisoryRawDocument("tenant-a", "vendor-y", "GHSA-2025-0002", "sha256:102", new BsonDocument("id", "GHSA-2025-0002:2")));
CreateAdvisoryRawDocument("tenant-a", "vendor-x", "GHSA-2025-0002", "sha256:101", new DocumentObject("id", "GHSA-2025-0002:1")),
CreateAdvisoryRawDocument("tenant-a", "vendor-y", "GHSA-2025-0002", "sha256:102", new DocumentObject("id", "GHSA-2025-0002:2")));
using var client = _factory.CreateClient();
var response = await client.GetAsync("/vuln/evidence/advisories/GHSA-2025-0002?tenant=tenant-a&vendor=vendor-y");
@@ -852,7 +852,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
[Fact]
public async Task AdvisoryChunksEndpoint_EmitsGuardrailMetrics()
{
var raw = BsonDocument.Parse("{\"details\":\"tiny\"}");
var raw = DocumentObject.Parse("{\"details\":\"tiny\"}");
var document = CreateChunkObservationDocument(
"tenant-a:chunk:1",
"tenant-a",
@@ -1734,7 +1734,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
private async Task SeedObservationDocumentsAsync(IEnumerable<AdvisoryObservationDocument> documents)
{
var client = new MongoClient(_runner.ConnectionString);
var client = new InMemoryClient(_runner.ConnectionString);
var database = client.GetDatabase(MongoStorageDefaults.DefaultDatabaseName);
var collection = database.GetCollection<AdvisoryObservationDocument>(MongoStorageDefaults.Collections.AdvisoryObservations);
@@ -1742,7 +1742,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
{
await database.DropCollectionAsync(MongoStorageDefaults.Collections.AdvisoryObservations);
}
catch (MongoCommandException ex) when (ex.CodeName == "NamespaceNotFound" || ex.Message.Contains("ns not found", StringComparison.OrdinalIgnoreCase))
catch (StorageCommandException ex) when (ex.CodeName == "NamespaceNotFound" || ex.Message.Contains("ns not found", StringComparison.OrdinalIgnoreCase))
{
// Collection does not exist yet; ignore.
}
@@ -1762,7 +1762,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
doc.Source.Vendor,
doc.Id,
doc.Upstream.ContentHash,
doc.Content.Raw.DeepClone().AsBsonDocument))
doc.Content.Raw.DeepClone().AsDocumentObject))
.ToArray();
await SeedAdvisoryRawDocumentsAsync(rawDocuments);
@@ -1770,7 +1770,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
private async Task SeedLinksetDocumentsAsync(IEnumerable<AdvisoryLinksetDocument> documents)
{
var client = new MongoClient(_runner.ConnectionString);
var client = new InMemoryClient(_runner.ConnectionString);
var database = client.GetDatabase(MongoStorageDefaults.DefaultDatabaseName);
var collection = database.GetCollection<AdvisoryLinksetDocument>(MongoStorageDefaults.Collections.AdvisoryLinksets);
@@ -1778,7 +1778,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
{
await database.DropCollectionAsync(MongoStorageDefaults.Collections.AdvisoryLinksets);
}
catch (MongoCommandException ex) when (ex.CodeName == "NamespaceNotFound" || ex.Message.Contains("ns not found", StringComparison.OrdinalIgnoreCase))
catch (StorageCommandException ex) when (ex.CodeName == "NamespaceNotFound" || ex.Message.Contains("ns not found", StringComparison.OrdinalIgnoreCase))
{
// Collection not created yet; safe to ignore.
}
@@ -1882,7 +1882,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
{
Format = "csaf",
SpecVersion = "2.0",
Raw = BsonDocument.Parse("""{"observation":"%ID%"}""".Replace("%ID%", id)),
Raw = DocumentObject.Parse("""{"observation":"%ID%"}""".Replace("%ID%", id)),
Metadata = new Dictionary<string, string>(StringComparer.Ordinal)
},
Linkset = new AdvisoryObservationLinksetDocument
@@ -1909,14 +1909,14 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
string tenant,
DateTime createdAt,
string alias,
BsonDocument rawDocument)
DocumentObject rawDocument)
{
var document = CreateObservationDocument(
id,
tenant,
createdAt,
aliases: new[] { alias });
var clone = rawDocument.DeepClone().AsBsonDocument;
var clone = rawDocument.DeepClone().AsDocumentObject;
document.Content.Raw = clone;
document.Upstream.ContentHash = ComputeContentHash(clone);
return document;
@@ -1925,7 +1925,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
private static readonly DateTimeOffset DefaultIngestTimestamp = new(2025, 1, 1, 0, 0, 0, TimeSpan.Zero);
private static readonly ICryptoHash Hash = CryptoHashFactory.CreateDefault();
private static string ComputeContentHash(BsonDocument rawDocument)
private static string ComputeContentHash(DocumentObject rawDocument)
{
var canonical = rawDocument.ToJson(new JsonWriterSettings
{
@@ -2363,16 +2363,16 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
using var validationScope = _factory.Services.CreateScope();
var database = validationScope.ServiceProvider.GetRequiredService<IMongoDatabase>();
var statements = database.GetCollection<BsonDocument>(MongoStorageDefaults.Collections.AdvisoryStatements);
var statements = database.GetCollection<DocumentObject>(MongoStorageDefaults.Collections.AdvisoryStatements);
var stored = await statements
.Find(Builders<BsonDocument>.Filter.Eq("_id", statementId.ToString()))
.Find(Builders<DocumentObject>.Filter.Eq("_id", statementId.ToString()))
.FirstOrDefaultAsync();
Assert.NotNull(stored);
var dsse = stored!["provenance"].AsBsonDocument["dsse"].AsBsonDocument;
var dsse = stored!["provenance"].AsDocumentObject["dsse"].AsDocumentObject;
Assert.Equal("sha256:feedface", dsse["envelopeDigest"].AsString);
var trustDoc = stored["trust"].AsBsonDocument;
var trustDoc = stored["trust"].AsDocumentObject;
Assert.True(trustDoc["verified"].AsBoolean);
Assert.Equal("Authority@stella", trustDoc["verifier"].AsString);
}
@@ -2380,8 +2380,8 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
{
using var cleanupScope = _factory.Services.CreateScope();
var database = cleanupScope.ServiceProvider.GetRequiredService<IMongoDatabase>();
var statements = database.GetCollection<BsonDocument>(MongoStorageDefaults.Collections.AdvisoryStatements);
await statements.DeleteOneAsync(Builders<BsonDocument>.Filter.Eq("_id", statementId.ToString()));
var statements = database.GetCollection<DocumentObject>(MongoStorageDefaults.Collections.AdvisoryStatements);
await statements.DeleteOneAsync(Builders<DocumentObject>.Filter.Eq("_id", statementId.ToString()));
}
}
@@ -2484,7 +2484,7 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
{
await database.DropCollectionAsync(collectionName);
}
catch (MongoCommandException ex) when (ex.CodeName == "NamespaceNotFound" || ex.Message.Contains("ns not found", StringComparison.OrdinalIgnoreCase))
catch (StorageCommandException ex) when (ex.CodeName == "NamespaceNotFound" || ex.Message.Contains("ns not found", StringComparison.OrdinalIgnoreCase))
{
}
}
@@ -2573,34 +2573,34 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
return advisory;
}
private async Task SeedAdvisoryRawDocumentsAsync(params BsonDocument[] documents)
private async Task SeedAdvisoryRawDocumentsAsync(params DocumentObject[] documents)
{
var client = new MongoClient(_runner.ConnectionString);
var client = new InMemoryClient(_runner.ConnectionString);
var database = client.GetDatabase(MongoStorageDefaults.DefaultDatabaseName);
var collection = database.GetCollection<BsonDocument>(MongoStorageDefaults.Collections.AdvisoryRaw);
await collection.DeleteManyAsync(FilterDefinition<BsonDocument>.Empty);
var collection = database.GetCollection<DocumentObject>(MongoStorageDefaults.Collections.AdvisoryRaw);
await collection.DeleteManyAsync(FilterDefinition<DocumentObject>.Empty);
if (documents.Length > 0)
{
await collection.InsertManyAsync(documents);
}
}
private static BsonDocument CreateAdvisoryRawDocument(
private static DocumentObject CreateAdvisoryRawDocument(
string tenant,
string vendor,
string upstreamId,
string contentHash,
BsonDocument? raw = null,
DocumentObject? raw = null,
string? supersedes = null)
{
var now = DateTime.UtcNow;
return new BsonDocument
return new DocumentObject
{
{ "_id", BuildRawDocumentId(vendor, upstreamId, contentHash) },
{ "tenant", tenant },
{
"source",
new BsonDocument
new DocumentObject
{
{ "vendor", vendor },
{ "connector", "test-connector" },
@@ -2609,57 +2609,57 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
},
{
"upstream",
new BsonDocument
new DocumentObject
{
{ "upstream_id", upstreamId },
{ "document_version", "1" },
{ "retrieved_at", now },
{ "content_hash", contentHash },
{ "signature", new BsonDocument { { "present", false } } },
{ "provenance", new BsonDocument { { "api", "https://example.test" } } }
{ "signature", new DocumentObject { { "present", false } } },
{ "provenance", new DocumentObject { { "api", "https://example.test" } } }
}
},
{
"content",
new BsonDocument
new DocumentObject
{
{ "format", "osv" },
{ "raw", raw ?? new BsonDocument("id", upstreamId) }
{ "raw", raw ?? new DocumentObject("id", upstreamId) }
}
},
{
"identifiers",
new BsonDocument
new DocumentObject
{
{ "aliases", new BsonArray(new[] { upstreamId }) },
{ "aliases", new DocumentArray(new[] { upstreamId }) },
{ "primary", upstreamId }
}
},
{
"linkset",
new BsonDocument
new DocumentObject
{
{ "aliases", new BsonArray() },
{ "purls", new BsonArray() },
{ "cpes", new BsonArray() },
{ "references", new BsonArray() },
{ "reconciled_from", new BsonArray() },
{ "notes", new BsonDocument() }
{ "aliases", new DocumentArray() },
{ "purls", new DocumentArray() },
{ "cpes", new DocumentArray() },
{ "references", new DocumentArray() },
{ "reconciled_from", new DocumentArray() },
{ "notes", new DocumentObject() }
}
},
{ "advisory_key", upstreamId.ToUpperInvariant() },
{
"links",
new BsonArray
new DocumentArray
{
new BsonDocument
new DocumentObject
{
{ "scheme", "PRIMARY" },
{ "value", upstreamId.ToUpperInvariant() }
}
}
},
{ "supersedes", supersedes is null ? BsonNull.Value : supersedes },
{ "supersedes", supersedes is null ? DocumentNull.Value : supersedes },
{ "ingested_at", now },
{ "created_at", now }
};
@@ -2883,21 +2883,21 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
}
// Small ping loop to ensure mongod is ready
var client = new MongoClient($"mongodb://127.0.0.1:{port}");
var client = new InMemoryClient($"mongodb://127.0.0.1:{port}");
var sw = System.Diagnostics.Stopwatch.StartNew();
while (sw.Elapsed < TimeSpan.FromSeconds(5))
{
try
{
client.GetDatabase("admin").RunCommand<BsonDocument>("{ ping: 1 }");
client.GetDatabase("admin").RunCommand<DocumentObject>("{ ping: 1 }");
// Initiate single-node replica set so features expecting replset work.
client.GetDatabase("admin").RunCommand<BsonDocument>(BsonDocument.Parse("{ replSetInitiate: { _id: \"rs0\", members: [ { _id: 0, host: \"127.0.0.1:" + port + "\" } ] } }"));
client.GetDatabase("admin").RunCommand<DocumentObject>(DocumentObject.Parse("{ replSetInitiate: { _id: \"rs0\", members: [ { _id: 0, host: \"127.0.0.1:" + port + "\" } ] } }"));
// Wait for primary
var readySw = System.Diagnostics.Stopwatch.StartNew();
while (readySw.Elapsed < TimeSpan.FromSeconds(5))
{
var status = client.GetDatabase("admin").RunCommand<BsonDocument>(BsonDocument.Parse("{ replSetGetStatus: 1 }"));
var myState = status["members"].AsBsonArray.FirstOrDefault(x => x["self"].AsBoolean);
var status = client.GetDatabase("admin").RunCommand<DocumentObject>(DocumentObject.Parse("{ replSetGetStatus: 1 }"));
var myState = status["members"].AsDocumentArray.FirstOrDefault(x => x["self"].AsBoolean);
if (myState != null && myState["state"].ToInt32() == 1)
{
connectionString = $"mongodb://127.0.0.1:{port}/?replicaSet=rs0";