Add unit tests and implementations for MongoDB index models and OpenAPI metadata
- Implemented `MongoIndexModelTests` to verify index models for various stores. - Created `OpenApiMetadataFactory` with methods to generate OpenAPI metadata. - Added tests for `OpenApiMetadataFactory` to ensure expected defaults and URL overrides. - Introduced `ObserverSurfaceSecrets` and `WebhookSurfaceSecrets` for managing secrets. - Developed `RuntimeSurfaceFsClient` and `WebhookSurfaceFsClient` for manifest retrieval. - Added dependency injection tests for `SurfaceEnvironmentRegistration` in both Observer and Webhook contexts. - Implemented tests for secret resolution in `ObserverSurfaceSecretsTests` and `WebhookSurfaceSecretsTests`. - Created `EnsureLinkNotMergeCollectionsMigrationTests` to validate MongoDB migration logic. - Added project files for MongoDB tests and NuGet package mirroring.
This commit is contained in:
@@ -205,6 +205,104 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
Assert.Equal("tenant-a:nvd:alpha:1", secondObservations[0].GetProperty("observationId").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LinksetsEndpoint_ReturnsNormalizedLinksetsFromIngestion()
|
||||
{
|
||||
var tenant = "tenant-linkset-ingest";
|
||||
using var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.Add("X-Stella-Tenant", tenant);
|
||||
|
||||
var firstIngest = await client.PostAsJsonAsync("/ingest/advisory", BuildAdvisoryIngestRequest("sha256:linkset-1", "GHSA-LINK-001", purls: new[] { "pkg:npm/demo@1.0.0" }));
|
||||
firstIngest.EnsureSuccessStatusCode();
|
||||
|
||||
var secondIngest = await client.PostAsJsonAsync("/ingest/advisory", BuildAdvisoryIngestRequest("sha256:linkset-2", "GHSA-LINK-002", purls: new[] { "pkg:npm/demo@2.0.0" }));
|
||||
secondIngest.EnsureSuccessStatusCode();
|
||||
|
||||
var response = await client.GetAsync("/linksets?tenant=tenant-linkset-ingest&limit=10");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var payload = await response.Content.ReadFromJsonAsync<AdvisoryLinksetQueryResponse>();
|
||||
Assert.NotNull(payload);
|
||||
Assert.Equal(2, payload!.Linksets.Length);
|
||||
|
||||
var linksetAdvisoryIds = payload.Linksets.Select(ls => ls.AdvisoryId).OrderBy(id => id, StringComparer.Ordinal).ToArray();
|
||||
Assert.Equal(new[] { "GHSA-LINK-001", "GHSA-LINK-002" }, linksetAdvisoryIds);
|
||||
|
||||
var allPurls = payload.Linksets.SelectMany(ls => ls.Purls).OrderBy(p => p, StringComparer.Ordinal).ToArray();
|
||||
Assert.Contains("pkg:npm/demo@1.0.0", allPurls);
|
||||
Assert.Contains("pkg:npm/demo@2.0.0", allPurls);
|
||||
|
||||
var versions = payload.Linksets
|
||||
.SelectMany(ls => ls.Versions)
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.OrderBy(v => v, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
Assert.Contains("1.0.0", versions);
|
||||
Assert.Contains("2.0.0", versions);
|
||||
|
||||
Assert.False(payload.HasMore);
|
||||
Assert.True(string.IsNullOrEmpty(payload.NextCursor));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LinksetsEndpoint_SupportsCursorPagination()
|
||||
{
|
||||
var tenant = "tenant-linkset-page";
|
||||
var documents = new[]
|
||||
{
|
||||
CreateLinksetDocument(
|
||||
tenant,
|
||||
"nvd",
|
||||
"ADV-002",
|
||||
new[] { "obs-2" },
|
||||
new[] { "pkg:npm/demo@2.0.0" },
|
||||
new[] { "2.0.0" },
|
||||
new DateTime(2025, 1, 6, 0, 0, 0, DateTimeKind.Utc)),
|
||||
CreateLinksetDocument(
|
||||
tenant,
|
||||
"osv",
|
||||
"ADV-001",
|
||||
new[] { "obs-1" },
|
||||
new[] { "pkg:npm/demo@1.0.0" },
|
||||
new[] { "1.0.0" },
|
||||
new DateTime(2025, 1, 5, 0, 0, 0, DateTimeKind.Utc)),
|
||||
CreateLinksetDocument(
|
||||
"tenant-other",
|
||||
"osv",
|
||||
"ADV-999",
|
||||
new[] { "obs-x" },
|
||||
new[] { "pkg:npm/other@1.0.0" },
|
||||
new[] { "1.0.0" },
|
||||
new DateTime(2025, 1, 4, 0, 0, 0, DateTimeKind.Utc))
|
||||
};
|
||||
|
||||
await SeedLinksetDocumentsAsync(documents);
|
||||
|
||||
using var client = _factory.CreateClient();
|
||||
|
||||
var firstResponse = await client.GetAsync($"/linksets?tenant={tenant}&limit=1");
|
||||
firstResponse.EnsureSuccessStatusCode();
|
||||
var firstPayload = await firstResponse.Content.ReadFromJsonAsync<AdvisoryLinksetQueryResponse>();
|
||||
Assert.NotNull(firstPayload);
|
||||
var first = Assert.Single(firstPayload!.Linksets);
|
||||
Assert.Equal("ADV-002", first.AdvisoryId);
|
||||
Assert.Equal(new[] { "pkg:npm/demo@2.0.0" }, first.Purls.ToArray());
|
||||
Assert.Equal(new[] { "2.0.0" }, first.Versions.ToArray());
|
||||
Assert.True(firstPayload.HasMore);
|
||||
Assert.False(string.IsNullOrWhiteSpace(firstPayload.NextCursor));
|
||||
|
||||
var secondResponse = await client.GetAsync($"/linksets?tenant={tenant}&limit=1&cursor={Uri.EscapeDataString(firstPayload.NextCursor!)}");
|
||||
secondResponse.EnsureSuccessStatusCode();
|
||||
var secondPayload = await secondResponse.Content.ReadFromJsonAsync<AdvisoryLinksetQueryResponse>();
|
||||
Assert.NotNull(secondPayload);
|
||||
var second = Assert.Single(secondPayload!.Linksets);
|
||||
Assert.Equal("ADV-001", second.AdvisoryId);
|
||||
Assert.Equal(new[] { "pkg:npm/demo@1.0.0" }, second.Purls.ToArray());
|
||||
Assert.Equal(new[] { "1.0.0" }, second.Versions.ToArray());
|
||||
Assert.False(secondPayload.HasMore);
|
||||
Assert.True(string.IsNullOrEmpty(secondPayload.NextCursor));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ObservationsEndpoint_ReturnsBadRequestWhenTenantMissing()
|
||||
{
|
||||
@@ -1505,6 +1603,52 @@ public sealed class WebServiceEndpointsTests : IAsyncLifetime
|
||||
await SeedAdvisoryRawDocumentsAsync(rawDocuments);
|
||||
}
|
||||
|
||||
private async Task SeedLinksetDocumentsAsync(IEnumerable<AdvisoryLinksetDocument> documents)
|
||||
{
|
||||
var client = new MongoClient(_runner.ConnectionString);
|
||||
var database = client.GetDatabase(MongoStorageDefaults.DefaultDatabaseName);
|
||||
var collection = database.GetCollection<AdvisoryLinksetDocument>(MongoStorageDefaults.Collections.AdvisoryLinksets);
|
||||
|
||||
try
|
||||
{
|
||||
await database.DropCollectionAsync(MongoStorageDefaults.Collections.AdvisoryLinksets);
|
||||
}
|
||||
catch (MongoCommandException ex) when (ex.CodeName == "NamespaceNotFound" || ex.Message.Contains("ns not found", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Collection not created yet; safe to ignore.
|
||||
}
|
||||
|
||||
var snapshot = documents?.ToArray() ?? Array.Empty<AdvisoryLinksetDocument>();
|
||||
if (snapshot.Length > 0)
|
||||
{
|
||||
await collection.InsertManyAsync(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
private static AdvisoryLinksetDocument CreateLinksetDocument(
|
||||
string tenant,
|
||||
string source,
|
||||
string advisoryId,
|
||||
IEnumerable<string> observationIds,
|
||||
IEnumerable<string> purls,
|
||||
IEnumerable<string> versions,
|
||||
DateTime createdAtUtc)
|
||||
{
|
||||
return new AdvisoryLinksetDocument
|
||||
{
|
||||
TenantId = tenant,
|
||||
Source = source,
|
||||
AdvisoryId = advisoryId,
|
||||
Observations = observationIds.ToList(),
|
||||
CreatedAt = DateTime.SpecifyKind(createdAtUtc, DateTimeKind.Utc),
|
||||
Normalized = new AdvisoryLinksetNormalizedDocument
|
||||
{
|
||||
Purls = purls.ToList(),
|
||||
Versions = versions.ToList()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static AdvisoryObservationDocument[] BuildSampleObservationDocuments()
|
||||
{
|
||||
return new[]
|
||||
|
||||
Reference in New Issue
Block a user