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,26 +1,26 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Time.Testing;
using StellaOps.Concelier.Bson;
using StellaOps.Concelier.Documents;
using StellaOps.Concelier.Models;
using StellaOps.Concelier.Connector.Common;
using StellaOps.Concelier.Connector.Common.Http;
using StellaOps.Concelier.Connector.Common.Testing;
using StellaOps.Concelier.Connector.Vndr.Vmware;
using StellaOps.Concelier.Connector.Vndr.Vmware.Configuration;
using StellaOps.Concelier.Connector.Vndr.Vmware.Internal;
using StellaOps.Concelier.Connector.Vndr.Vmware;
using StellaOps.Concelier.Connector.Vndr.Vmware.Configuration;
using StellaOps.Concelier.Connector.Vndr.Vmware.Internal;
using StellaOps.Concelier.Storage;
using StellaOps.Concelier.Storage.Advisories;
using StellaOps.Concelier.Storage;
@@ -29,59 +29,59 @@ using StellaOps.Concelier.Storage.Postgres;
using StellaOps.Concelier.Storage.PsirtFlags;
using StellaOps.Concelier.Testing;
using Xunit.Abstractions;
namespace StellaOps.Concelier.Connector.Vndr.Vmware.Tests.Vmware;
namespace StellaOps.Concelier.Connector.Vndr.Vmware.Tests.Vmware;
[Collection(ConcelierFixtureCollection.Name)]
public sealed class VmwareConnectorTests : IAsyncLifetime
{
private readonly ConcelierPostgresFixture _fixture;
private readonly FakeTimeProvider _timeProvider;
private readonly CannedHttpMessageHandler _handler;
private readonly ITestOutputHelper _output;
private static readonly Uri IndexUri = new("https://vmware.example/api/vmsa/index.json");
private static readonly Uri DetailOne = new("https://vmware.example/api/vmsa/VMSA-2024-0001.json");
private static readonly Uri DetailTwo = new("https://vmware.example/api/vmsa/VMSA-2024-0002.json");
private static readonly Uri DetailThree = new("https://vmware.example/api/vmsa/VMSA-2024-0003.json");
public VmwareConnectorTests(ConcelierPostgresFixture fixture, ITestOutputHelper output)
{
_fixture = fixture;
_timeProvider = new FakeTimeProvider(new DateTimeOffset(2024, 4, 5, 0, 0, 0, TimeSpan.Zero));
_handler = new CannedHttpMessageHandler();
_output = output;
}
[Fact]
public async Task FetchParseMap_ProducesSnapshotAndCoversResume()
{
await using var provider = await BuildServiceProviderAsync();
SeedInitialResponses();
using var metrics = new VmwareMetricCollector();
var connector = provider.GetRequiredService<VmwareConnector>();
await connector.FetchAsync(provider, CancellationToken.None);
await connector.ParseAsync(provider, CancellationToken.None);
await connector.MapAsync(provider, CancellationToken.None);
var advisoryStore = provider.GetRequiredService<IAdvisoryStore>();
var advisories = await advisoryStore.GetRecentAsync(10, CancellationToken.None);
var ordered = advisories.OrderBy(static a => a.AdvisoryKey, StringComparer.Ordinal).ToArray();
var snapshot = Normalize(SnapshotSerializer.ToSnapshot(ordered));
var expected = Normalize(ReadFixture("vmware-advisories.snapshot.json"));
if (!string.Equals(expected, snapshot, StringComparison.Ordinal))
{
var actualPath = Path.Combine(AppContext.BaseDirectory, "Vmware", "Fixtures", "vmware-advisories.actual.json");
Directory.CreateDirectory(Path.GetDirectoryName(actualPath)!);
File.WriteAllText(actualPath, snapshot);
}
Assert.Equal(expected, snapshot);
public sealed class VmwareConnectorTests : IAsyncLifetime
{
private readonly ConcelierPostgresFixture _fixture;
private readonly FakeTimeProvider _timeProvider;
private readonly CannedHttpMessageHandler _handler;
private readonly ITestOutputHelper _output;
private static readonly Uri IndexUri = new("https://vmware.example/api/vmsa/index.json");
private static readonly Uri DetailOne = new("https://vmware.example/api/vmsa/VMSA-2024-0001.json");
private static readonly Uri DetailTwo = new("https://vmware.example/api/vmsa/VMSA-2024-0002.json");
private static readonly Uri DetailThree = new("https://vmware.example/api/vmsa/VMSA-2024-0003.json");
public VmwareConnectorTests(ConcelierPostgresFixture fixture, ITestOutputHelper output)
{
_fixture = fixture;
_timeProvider = new FakeTimeProvider(new DateTimeOffset(2024, 4, 5, 0, 0, 0, TimeSpan.Zero));
_handler = new CannedHttpMessageHandler();
_output = output;
}
[Fact]
public async Task FetchParseMap_ProducesSnapshotAndCoversResume()
{
await using var provider = await BuildServiceProviderAsync();
SeedInitialResponses();
using var metrics = new VmwareMetricCollector();
var connector = provider.GetRequiredService<VmwareConnector>();
await connector.FetchAsync(provider, CancellationToken.None);
await connector.ParseAsync(provider, CancellationToken.None);
await connector.MapAsync(provider, CancellationToken.None);
var advisoryStore = provider.GetRequiredService<IAdvisoryStore>();
var advisories = await advisoryStore.GetRecentAsync(10, CancellationToken.None);
var ordered = advisories.OrderBy(static a => a.AdvisoryKey, StringComparer.Ordinal).ToArray();
var snapshot = Normalize(SnapshotSerializer.ToSnapshot(ordered));
var expected = Normalize(ReadFixture("vmware-advisories.snapshot.json"));
if (!string.Equals(expected, snapshot, StringComparison.Ordinal))
{
var actualPath = Path.Combine(AppContext.BaseDirectory, "Vmware", "Fixtures", "vmware-advisories.actual.json");
Directory.CreateDirectory(Path.GetDirectoryName(actualPath)!);
File.WriteAllText(actualPath, snapshot);
}
Assert.Equal(expected, snapshot);
var psirtStore = provider.GetRequiredService<IPsirtFlagStore>();
var psirtFlags = new List<PsirtFlagRecord>();
foreach (var advisory in ordered)
@@ -95,178 +95,178 @@ public sealed class VmwareConnectorTests : IAsyncLifetime
Assert.Equal(2, psirtFlags.Count);
Assert.All(psirtFlags, flag => Assert.Equal("VMware", flag.Vendor));
var stateRepository = provider.GetRequiredService<ISourceStateRepository>();
var state = await stateRepository.TryGetAsync(VmwareConnectorPlugin.SourceName, CancellationToken.None);
Assert.NotNull(state);
Assert.Empty(state!.Cursor.TryGetValue("pendingDocuments", out var pendingDocs) ? pendingDocs.AsBsonArray : new BsonArray());
Assert.Empty(state.Cursor.TryGetValue("pendingMappings", out var pendingMaps) ? pendingMaps.AsBsonArray : new BsonArray());
var cursorSnapshot = VmwareCursor.FromBson(state.Cursor);
_output.WriteLine($"Initial fetch cache entries: {cursorSnapshot.FetchCache.Count}");
foreach (var entry in cursorSnapshot.FetchCache)
{
_output.WriteLine($"Cache seed: {entry.Key} -> {entry.Value.Sha256}");
}
// Second run with unchanged advisories and one new advisory.
SeedUpdateResponses();
_timeProvider.Advance(TimeSpan.FromHours(1));
await connector.FetchAsync(provider, CancellationToken.None);
var documentStore = provider.GetRequiredService<IDocumentStore>();
var resumeDocOne = await documentStore.FindBySourceAndUriAsync(VmwareConnectorPlugin.SourceName, DetailOne.ToString(), CancellationToken.None);
var resumeDocTwo = await documentStore.FindBySourceAndUriAsync(VmwareConnectorPlugin.SourceName, DetailTwo.ToString(), CancellationToken.None);
_output.WriteLine($"After resume fetch status: {resumeDocOne?.Status} ({resumeDocOne?.Sha256}), {resumeDocTwo?.Status} ({resumeDocTwo?.Sha256})");
Assert.Equal(DocumentStatuses.Mapped, resumeDocOne?.Status);
Assert.Equal(DocumentStatuses.Mapped, resumeDocTwo?.Status);
await connector.ParseAsync(provider, CancellationToken.None);
await connector.MapAsync(provider, CancellationToken.None);
advisories = await advisoryStore.GetRecentAsync(10, CancellationToken.None);
Assert.Equal(3, advisories.Count);
Assert.Contains(advisories, advisory => advisory.AdvisoryKey == "VMSA-2024-0003");
psirtFlags = await psirtCollection.Find(Builders<BsonDocument>.Filter.Empty).ToListAsync();
_output.WriteLine("PSIRT flags after resume: " + string.Join(", ", psirtFlags.Select(flag => flag.GetValue("_id", BsonValue.Create("<missing>")).ToString())));
Assert.Equal(3, psirtFlags.Count);
Assert.Contains(psirtFlags, doc => doc["_id"] == "VMSA-2024-0003");
var measurements = metrics.Measurements;
_output.WriteLine("Captured metrics:");
foreach (var measurement in measurements)
{
_output.WriteLine($"{measurement.Name} -> {measurement.Value}");
}
Assert.Equal(0, Sum(measurements, "vmware.fetch.failures"));
Assert.Equal(0, Sum(measurements, "vmware.parse.fail"));
Assert.Equal(3, Sum(measurements, "vmware.fetch.items")); // two initial, one new
var affectedCounts = measurements
.Where(m => m.Name == "vmware.map.affected_count")
.Select(m => (int)m.Value)
.OrderBy(v => v)
.ToArray();
Assert.Equal(new[] { 1, 1, 2 }, affectedCounts);
}
public Task InitializeAsync() => Task.CompletedTask;
public Task DisposeAsync()
{
_handler.Clear();
return Task.CompletedTask;
}
var stateRepository = provider.GetRequiredService<ISourceStateRepository>();
var state = await stateRepository.TryGetAsync(VmwareConnectorPlugin.SourceName, CancellationToken.None);
Assert.NotNull(state);
Assert.Empty(state!.Cursor.TryGetValue("pendingDocuments", out var pendingDocs) ? pendingDocs.AsDocumentArray : new DocumentArray());
Assert.Empty(state.Cursor.TryGetValue("pendingMappings", out var pendingMaps) ? pendingMaps.AsDocumentArray : new DocumentArray());
var cursorSnapshot = VmwareCursor.FromBson(state.Cursor);
_output.WriteLine($"Initial fetch cache entries: {cursorSnapshot.FetchCache.Count}");
foreach (var entry in cursorSnapshot.FetchCache)
{
_output.WriteLine($"Cache seed: {entry.Key} -> {entry.Value.Sha256}");
}
// Second run with unchanged advisories and one new advisory.
SeedUpdateResponses();
_timeProvider.Advance(TimeSpan.FromHours(1));
await connector.FetchAsync(provider, CancellationToken.None);
var documentStore = provider.GetRequiredService<IDocumentStore>();
var resumeDocOne = await documentStore.FindBySourceAndUriAsync(VmwareConnectorPlugin.SourceName, DetailOne.ToString(), CancellationToken.None);
var resumeDocTwo = await documentStore.FindBySourceAndUriAsync(VmwareConnectorPlugin.SourceName, DetailTwo.ToString(), CancellationToken.None);
_output.WriteLine($"After resume fetch status: {resumeDocOne?.Status} ({resumeDocOne?.Sha256}), {resumeDocTwo?.Status} ({resumeDocTwo?.Sha256})");
Assert.Equal(DocumentStatuses.Mapped, resumeDocOne?.Status);
Assert.Equal(DocumentStatuses.Mapped, resumeDocTwo?.Status);
await connector.ParseAsync(provider, CancellationToken.None);
await connector.MapAsync(provider, CancellationToken.None);
advisories = await advisoryStore.GetRecentAsync(10, CancellationToken.None);
Assert.Equal(3, advisories.Count);
Assert.Contains(advisories, advisory => advisory.AdvisoryKey == "VMSA-2024-0003");
psirtFlags = await psirtCollection.Find(Builders<DocumentObject>.Filter.Empty).ToListAsync();
_output.WriteLine("PSIRT flags after resume: " + string.Join(", ", psirtFlags.Select(flag => flag.GetValue("_id", DocumentValue.Create("<missing>")).ToString())));
Assert.Equal(3, psirtFlags.Count);
Assert.Contains(psirtFlags, doc => doc["_id"] == "VMSA-2024-0003");
var measurements = metrics.Measurements;
_output.WriteLine("Captured metrics:");
foreach (var measurement in measurements)
{
_output.WriteLine($"{measurement.Name} -> {measurement.Value}");
}
Assert.Equal(0, Sum(measurements, "vmware.fetch.failures"));
Assert.Equal(0, Sum(measurements, "vmware.parse.fail"));
Assert.Equal(3, Sum(measurements, "vmware.fetch.items")); // two initial, one new
var affectedCounts = measurements
.Where(m => m.Name == "vmware.map.affected_count")
.Select(m => (int)m.Value)
.OrderBy(v => v)
.ToArray();
Assert.Equal(new[] { 1, 1, 2 }, affectedCounts);
}
public Task InitializeAsync() => Task.CompletedTask;
public Task DisposeAsync()
{
_handler.Clear();
return Task.CompletedTask;
}
private async Task<ServiceProvider> BuildServiceProviderAsync()
{
await _fixture.TruncateAllTablesAsync();
_handler.Clear();
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance));
services.AddSingleton<TimeProvider>(_timeProvider);
services.AddSingleton(_handler);
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance));
services.AddSingleton<TimeProvider>(_timeProvider);
services.AddSingleton(_handler);
services.AddConcelierPostgresStorage(options =>
{
options.ConnectionString = _fixture.ConnectionString;
options.SchemaName = _fixture.SchemaName;
options.CommandTimeoutSeconds = 5;
});
services.AddSourceCommon();
services.AddVmwareConnector(opts =>
{
opts.IndexUri = IndexUri;
opts.InitialBackfill = TimeSpan.FromDays(30);
opts.ModifiedTolerance = TimeSpan.FromMinutes(5);
opts.MaxAdvisoriesPerFetch = 10;
opts.RequestDelay = TimeSpan.Zero;
});
services.Configure<HttpClientFactoryOptions>(VmwareOptions.HttpClientName, builderOptions =>
{
builderOptions.HttpMessageHandlerBuilderActions.Add(builder => builder.PrimaryHandler = _handler);
});
services.AddSourceCommon();
services.AddVmwareConnector(opts =>
{
opts.IndexUri = IndexUri;
opts.InitialBackfill = TimeSpan.FromDays(30);
opts.ModifiedTolerance = TimeSpan.FromMinutes(5);
opts.MaxAdvisoriesPerFetch = 10;
opts.RequestDelay = TimeSpan.Zero;
});
services.Configure<HttpClientFactoryOptions>(VmwareOptions.HttpClientName, builderOptions =>
{
builderOptions.HttpMessageHandlerBuilderActions.Add(builder => builder.PrimaryHandler = _handler);
});
return services.BuildServiceProvider();
}
private void SeedInitialResponses()
{
_handler.AddJsonResponse(IndexUri, ReadFixture("vmware-index-initial.json"));
_handler.AddJsonResponse(DetailOne, ReadFixture("vmware-detail-vmsa-2024-0001.json"));
_handler.AddJsonResponse(DetailTwo, ReadFixture("vmware-detail-vmsa-2024-0002.json"));
}
private void SeedUpdateResponses()
{
_handler.AddJsonResponse(IndexUri, ReadFixture("vmware-index-second.json"));
_handler.AddJsonResponse(DetailOne, ReadFixture("vmware-detail-vmsa-2024-0001.json"));
_handler.AddJsonResponse(DetailTwo, ReadFixture("vmware-detail-vmsa-2024-0002.json"));
_handler.AddJsonResponse(DetailThree, ReadFixture("vmware-detail-vmsa-2024-0003.json"));
}
private static string ReadFixture(string name)
{
var primary = Path.Combine(AppContext.BaseDirectory, "Vmware", "Fixtures", name);
if (File.Exists(primary))
{
return File.ReadAllText(primary);
}
var fallback = Path.Combine(AppContext.BaseDirectory, "Fixtures", name);
if (File.Exists(fallback))
{
return File.ReadAllText(fallback);
}
throw new FileNotFoundException($"Fixture '{name}' not found.", name);
}
private static string Normalize(string value)
=> value.Replace("\r\n", "\n", StringComparison.Ordinal).TrimEnd();
private static long Sum(IEnumerable<VmwareMetricCollector.MetricMeasurement> measurements, string name)
=> measurements.Where(m => m.Name == name).Sum(m => m.Value);
private sealed class VmwareMetricCollector : IDisposable
{
private readonly MeterListener _listener;
private readonly ConcurrentBag<MetricMeasurement> _measurements = new();
public VmwareMetricCollector()
{
_listener = new MeterListener
{
InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name == VmwareDiagnostics.MeterName)
{
listener.EnableMeasurementEvents(instrument);
}
}
};
_listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
{
var tagList = new List<KeyValuePair<string, object?>>(tags.Length);
foreach (var tag in tags)
{
tagList.Add(tag);
}
_measurements.Add(new MetricMeasurement(instrument.Name, measurement, tagList));
});
_listener.Start();
}
public IReadOnlyCollection<MetricMeasurement> Measurements => _measurements;
public void Dispose() => _listener.Dispose();
public sealed record MetricMeasurement(string Name, long Value, IReadOnlyList<KeyValuePair<string, object?>> Tags);
}
}
private void SeedInitialResponses()
{
_handler.AddJsonResponse(IndexUri, ReadFixture("vmware-index-initial.json"));
_handler.AddJsonResponse(DetailOne, ReadFixture("vmware-detail-vmsa-2024-0001.json"));
_handler.AddJsonResponse(DetailTwo, ReadFixture("vmware-detail-vmsa-2024-0002.json"));
}
private void SeedUpdateResponses()
{
_handler.AddJsonResponse(IndexUri, ReadFixture("vmware-index-second.json"));
_handler.AddJsonResponse(DetailOne, ReadFixture("vmware-detail-vmsa-2024-0001.json"));
_handler.AddJsonResponse(DetailTwo, ReadFixture("vmware-detail-vmsa-2024-0002.json"));
_handler.AddJsonResponse(DetailThree, ReadFixture("vmware-detail-vmsa-2024-0003.json"));
}
private static string ReadFixture(string name)
{
var primary = Path.Combine(AppContext.BaseDirectory, "Vmware", "Fixtures", name);
if (File.Exists(primary))
{
return File.ReadAllText(primary);
}
var fallback = Path.Combine(AppContext.BaseDirectory, "Fixtures", name);
if (File.Exists(fallback))
{
return File.ReadAllText(fallback);
}
throw new FileNotFoundException($"Fixture '{name}' not found.", name);
}
private static string Normalize(string value)
=> value.Replace("\r\n", "\n", StringComparison.Ordinal).TrimEnd();
private static long Sum(IEnumerable<VmwareMetricCollector.MetricMeasurement> measurements, string name)
=> measurements.Where(m => m.Name == name).Sum(m => m.Value);
private sealed class VmwareMetricCollector : IDisposable
{
private readonly MeterListener _listener;
private readonly ConcurrentBag<MetricMeasurement> _measurements = new();
public VmwareMetricCollector()
{
_listener = new MeterListener
{
InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name == VmwareDiagnostics.MeterName)
{
listener.EnableMeasurementEvents(instrument);
}
}
};
_listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
{
var tagList = new List<KeyValuePair<string, object?>>(tags.Length);
foreach (var tag in tags)
{
tagList.Add(tag);
}
_measurements.Add(new MetricMeasurement(instrument.Name, measurement, tagList));
});
_listener.Start();
}
public IReadOnlyCollection<MetricMeasurement> Measurements => _measurements;
public void Dispose() => _listener.Dispose();
public sealed record MetricMeasurement(string Name, long Value, IReadOnlyList<KeyValuePair<string, object?>> Tags);
}
}

View File

@@ -1,86 +1,86 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using StellaOps.Concelier.Bson;
using StellaOps.Concelier.Models;
using StellaOps.Concelier.Connector.Common;
using StellaOps.Concelier.Connector.Vndr.Vmware;
using StellaOps.Concelier.Connector.Vndr.Vmware.Internal;
using StellaOps.Concelier.Storage;
using StellaOps.Concelier.Storage;
using Xunit;
namespace StellaOps.Concelier.Connector.Vndr.Vmware.Tests;
public sealed class VmwareMapperTests
{
[Fact]
public void Map_CreatesCanonicalAdvisory()
{
var modified = DateTimeOffset.UtcNow;
var dto = new VmwareDetailDto
{
AdvisoryId = "VMSA-2025-0001",
Title = "Sample VMware Advisory",
Summary = "Summary text",
Published = modified.AddDays(-1),
Modified = modified,
CveIds = new[] { "CVE-2025-0001", "CVE-2025-0002" },
References = new[]
{
new VmwareReferenceDto { Url = "https://kb.vmware.com/some-kb", Type = "KB" },
new VmwareReferenceDto { Url = "https://vmsa.vmware.com/vmsa/KB", Type = "Advisory" },
},
Affected = new[]
{
new VmwareAffectedProductDto
{
Product = "VMware vCenter",
Version = "7.0",
FixedVersion = "7.0u3"
}
}
};
var document = new DocumentRecord(
Guid.NewGuid(),
VmwareConnectorPlugin.SourceName,
"https://vmsa.vmware.com/vmsa/VMSA-2025-0001",
DateTimeOffset.UtcNow,
"sha256",
DocumentStatuses.PendingParse,
"application/json",
null,
new Dictionary<string, string>(StringComparer.Ordinal)
{
["vmware.id"] = dto.AdvisoryId,
},
null,
modified,
null,
null);
var payload = BsonDocument.Parse(JsonSerializer.Serialize(dto, new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
}));
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, VmwareConnectorPlugin.SourceName, "vmware.v1", payload, DateTimeOffset.UtcNow);
var (advisory, flag) = VmwareMapper.Map(dto, document, dtoRecord);
Assert.Equal(dto.AdvisoryId, advisory.AdvisoryKey);
Assert.Contains("CVE-2025-0001", advisory.Aliases);
Assert.Contains("CVE-2025-0002", advisory.Aliases);
Assert.Single(advisory.AffectedPackages);
Assert.Equal("VMware vCenter", advisory.AffectedPackages[0].Identifier);
Assert.Single(advisory.AffectedPackages[0].VersionRanges);
Assert.Equal("7.0", advisory.AffectedPackages[0].VersionRanges[0].IntroducedVersion);
Assert.Equal("7.0u3", advisory.AffectedPackages[0].VersionRanges[0].FixedVersion);
Assert.Equal(2, advisory.References.Length);
Assert.Equal("https://kb.vmware.com/some-kb", advisory.References[0].Url);
Assert.Equal(dto.AdvisoryId, flag.AdvisoryKey);
Assert.Equal("VMware", flag.Vendor);
Assert.Equal(VmwareConnectorPlugin.SourceName, flag.SourceName);
}
}
using System;
using System.Collections.Generic;
using System.Text.Json;
using StellaOps.Concelier.Documents;
using StellaOps.Concelier.Models;
using StellaOps.Concelier.Connector.Common;
using StellaOps.Concelier.Connector.Vndr.Vmware;
using StellaOps.Concelier.Connector.Vndr.Vmware.Internal;
using StellaOps.Concelier.Storage;
using StellaOps.Concelier.Storage;
using Xunit;
namespace StellaOps.Concelier.Connector.Vndr.Vmware.Tests;
public sealed class VmwareMapperTests
{
[Fact]
public void Map_CreatesCanonicalAdvisory()
{
var modified = DateTimeOffset.UtcNow;
var dto = new VmwareDetailDto
{
AdvisoryId = "VMSA-2025-0001",
Title = "Sample VMware Advisory",
Summary = "Summary text",
Published = modified.AddDays(-1),
Modified = modified,
CveIds = new[] { "CVE-2025-0001", "CVE-2025-0002" },
References = new[]
{
new VmwareReferenceDto { Url = "https://kb.vmware.com/some-kb", Type = "KB" },
new VmwareReferenceDto { Url = "https://vmsa.vmware.com/vmsa/KB", Type = "Advisory" },
},
Affected = new[]
{
new VmwareAffectedProductDto
{
Product = "VMware vCenter",
Version = "7.0",
FixedVersion = "7.0u3"
}
}
};
var document = new DocumentRecord(
Guid.NewGuid(),
VmwareConnectorPlugin.SourceName,
"https://vmsa.vmware.com/vmsa/VMSA-2025-0001",
DateTimeOffset.UtcNow,
"sha256",
DocumentStatuses.PendingParse,
"application/json",
null,
new Dictionary<string, string>(StringComparer.Ordinal)
{
["vmware.id"] = dto.AdvisoryId,
},
null,
modified,
null,
null);
var payload = DocumentObject.Parse(JsonSerializer.Serialize(dto, new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
}));
var dtoRecord = new DtoRecord(Guid.NewGuid(), document.Id, VmwareConnectorPlugin.SourceName, "vmware.v1", payload, DateTimeOffset.UtcNow);
var (advisory, flag) = VmwareMapper.Map(dto, document, dtoRecord);
Assert.Equal(dto.AdvisoryId, advisory.AdvisoryKey);
Assert.Contains("CVE-2025-0001", advisory.Aliases);
Assert.Contains("CVE-2025-0002", advisory.Aliases);
Assert.Single(advisory.AffectedPackages);
Assert.Equal("VMware vCenter", advisory.AffectedPackages[0].Identifier);
Assert.Single(advisory.AffectedPackages[0].VersionRanges);
Assert.Equal("7.0", advisory.AffectedPackages[0].VersionRanges[0].IntroducedVersion);
Assert.Equal("7.0u3", advisory.AffectedPackages[0].VersionRanges[0].FixedVersion);
Assert.Equal(2, advisory.References.Length);
Assert.Equal("https://kb.vmware.com/some-kb", advisory.References[0].Url);
Assert.Equal(dto.AdvisoryId, flag.AdvisoryKey);
Assert.Equal("VMware", flag.Vendor);
Assert.Equal(VmwareConnectorPlugin.SourceName, flag.SourceName);
}
}