up
This commit is contained in:
		| @@ -62,7 +62,12 @@ public sealed class AdvisoryStorePerformanceTests : IClassFixture<MongoIntegrati | ||||
|             await bootstrapper.InitializeAsync(CancellationToken.None); | ||||
|  | ||||
|             var aliasStore = new AliasStore(database, NullLogger<AliasStore>.Instance); | ||||
|             var store = new AdvisoryStore(database, aliasStore, NullLogger<AdvisoryStore>.Instance, TimeProvider.System); | ||||
|             var store = new AdvisoryStore( | ||||
|                 database, | ||||
|                 aliasStore, | ||||
|                 NullLogger<AdvisoryStore>.Instance, | ||||
|                 Options.Create(new MongoStorageOptions()), | ||||
|                 TimeProvider.System); | ||||
|             using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(45)); | ||||
|  | ||||
|             // Warm up collections (indexes, serialization caches) so perf timings exclude one-time setup work. | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using Microsoft.Extensions.Logging.Abstractions; | ||||
| using MongoDB.Driver; | ||||
| using StellaOps.Feedser.Models; | ||||
| using Microsoft.Extensions.Logging.Abstractions; | ||||
| using Microsoft.Extensions.Options; | ||||
| using MongoDB.Driver; | ||||
| using StellaOps.Feedser.Models; | ||||
| using StellaOps.Feedser.Storage.Mongo.Advisories; | ||||
| using StellaOps.Feedser.Storage.Mongo.Aliases; | ||||
|  | ||||
| @@ -25,8 +26,13 @@ public sealed class AdvisoryStoreTests : IClassFixture<MongoIntegrationFixture> | ||||
|         await DropCollectionAsync(MongoStorageDefaults.Collections.Advisory); | ||||
|         await DropCollectionAsync(MongoStorageDefaults.Collections.Alias); | ||||
|  | ||||
|         var aliasStore = new AliasStore(_fixture.Database, NullLogger<AliasStore>.Instance); | ||||
|         var store = new AdvisoryStore(_fixture.Database, aliasStore, NullLogger<AdvisoryStore>.Instance, TimeProvider.System); | ||||
|         var aliasStore = new AliasStore(_fixture.Database, NullLogger<AliasStore>.Instance); | ||||
|         var store = new AdvisoryStore( | ||||
|             _fixture.Database, | ||||
|             aliasStore, | ||||
|             NullLogger<AdvisoryStore>.Instance, | ||||
|             Options.Create(new MongoStorageOptions()), | ||||
|             TimeProvider.System); | ||||
|         var advisory = new Advisory( | ||||
|             advisoryKey: "ADV-1", | ||||
|             title: "Sample Advisory", | ||||
| @@ -62,8 +68,13 @@ public sealed class AdvisoryStoreTests : IClassFixture<MongoIntegrationFixture> | ||||
|         await DropCollectionAsync(MongoStorageDefaults.Collections.Advisory); | ||||
|         await DropCollectionAsync(MongoStorageDefaults.Collections.Alias); | ||||
|  | ||||
|         var aliasStore = new AliasStore(_fixture.Database, NullLogger<AliasStore>.Instance); | ||||
|         var store = new AdvisoryStore(_fixture.Database, aliasStore, NullLogger<AdvisoryStore>.Instance, TimeProvider.System); | ||||
|         var aliasStore = new AliasStore(_fixture.Database, NullLogger<AliasStore>.Instance); | ||||
|         var store = new AdvisoryStore( | ||||
|             _fixture.Database, | ||||
|             aliasStore, | ||||
|             NullLogger<AdvisoryStore>.Instance, | ||||
|             Options.Create(new MongoStorageOptions()), | ||||
|             TimeProvider.System); | ||||
|  | ||||
|         var recordedAt = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero); | ||||
|         var provenance = new AdvisoryProvenance("source-x", "mapper", "payload-123", recordedAt); | ||||
| @@ -144,15 +155,147 @@ public sealed class AdvisoryStoreTests : IClassFixture<MongoIntegrationFixture> | ||||
|         Assert.NotNull(fetchedRange.Primitives); | ||||
|         Assert.Equal(rangePrimitives.SemVer, fetchedRange.Primitives!.SemVer); | ||||
|         Assert.Equal(rangePrimitives.Nevra, fetchedRange.Primitives.Nevra); | ||||
|         Assert.Equal(rangePrimitives.Evr, fetchedRange.Primitives.Evr); | ||||
|         Assert.Equal(rangePrimitives.VendorExtensions, fetchedRange.Primitives.VendorExtensions); | ||||
|     } | ||||
|  | ||||
|     private async Task DropCollectionAsync(string collectionName) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await _fixture.Database.DropCollectionAsync(collectionName); | ||||
|         Assert.Equal(rangePrimitives.Evr, fetchedRange.Primitives.Evr); | ||||
|         Assert.Equal(rangePrimitives.VendorExtensions, fetchedRange.Primitives.VendorExtensions); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public async Task UpsertAsync_SkipsNormalizedVersionsWhenFeatureDisabled() | ||||
|     { | ||||
|         await DropCollectionAsync(MongoStorageDefaults.Collections.Advisory); | ||||
|         await DropCollectionAsync(MongoStorageDefaults.Collections.Alias); | ||||
|  | ||||
|         var aliasStore = new AliasStore(_fixture.Database, NullLogger<AliasStore>.Instance); | ||||
|         var store = new AdvisoryStore( | ||||
|             _fixture.Database, | ||||
|             aliasStore, | ||||
|             NullLogger<AdvisoryStore>.Instance, | ||||
|             Options.Create(new MongoStorageOptions { EnableSemVerStyle = false }), | ||||
|             TimeProvider.System); | ||||
|  | ||||
|         var advisory = CreateNormalizedAdvisory("ADV-NORM-DISABLED"); | ||||
|         await store.UpsertAsync(advisory, CancellationToken.None); | ||||
|  | ||||
|         var document = await _fixture.Database | ||||
|             .GetCollection<AdvisoryDocument>(MongoStorageDefaults.Collections.Advisory) | ||||
|             .Find(x => x.AdvisoryKey == advisory.AdvisoryKey) | ||||
|             .FirstOrDefaultAsync(); | ||||
|  | ||||
|         Assert.NotNull(document); | ||||
|         Assert.True(document!.NormalizedVersions is null || document.NormalizedVersions.Count == 0); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public async Task UpsertAsync_PopulatesNormalizedVersionsWhenFeatureEnabled() | ||||
|     { | ||||
|         await DropCollectionAsync(MongoStorageDefaults.Collections.Advisory); | ||||
|         await DropCollectionAsync(MongoStorageDefaults.Collections.Alias); | ||||
|  | ||||
|         var aliasStore = new AliasStore(_fixture.Database, NullLogger<AliasStore>.Instance); | ||||
|         var store = new AdvisoryStore( | ||||
|             _fixture.Database, | ||||
|             aliasStore, | ||||
|             NullLogger<AdvisoryStore>.Instance, | ||||
|             Options.Create(new MongoStorageOptions { EnableSemVerStyle = true }), | ||||
|             TimeProvider.System); | ||||
|  | ||||
|         var advisory = CreateNormalizedAdvisory("ADV-NORM-ENABLED"); | ||||
|         await store.UpsertAsync(advisory, CancellationToken.None); | ||||
|  | ||||
|         var document = await _fixture.Database | ||||
|             .GetCollection<AdvisoryDocument>(MongoStorageDefaults.Collections.Advisory) | ||||
|             .Find(x => x.AdvisoryKey == advisory.AdvisoryKey) | ||||
|             .FirstOrDefaultAsync(); | ||||
|  | ||||
|         Assert.NotNull(document); | ||||
|         var normalizedCollection = document!.NormalizedVersions; | ||||
|         Assert.NotNull(normalizedCollection); | ||||
|         var normalized = Assert.Single(normalizedCollection!); | ||||
|         Assert.Equal("pkg:npm/example", normalized.PackageId); | ||||
|         Assert.Equal(AffectedPackageTypes.SemVer, normalized.PackageType); | ||||
|         Assert.Equal(NormalizedVersionSchemes.SemVer, normalized.Scheme); | ||||
|         Assert.Equal(NormalizedVersionRuleTypes.Range, normalized.Type); | ||||
|         Assert.Equal("range", normalized.Style); | ||||
|         Assert.Equal("1.0.0", normalized.Min); | ||||
|         Assert.True(normalized.MinInclusive); | ||||
|         Assert.Equal("2.0.0", normalized.Max); | ||||
|         Assert.False(normalized.MaxInclusive); | ||||
|         Assert.Null(normalized.Value); | ||||
|         Assert.Equal("ghsa:pkg:npm/example", normalized.Notes); | ||||
|         Assert.Equal("range-decision", normalized.DecisionReason); | ||||
|         Assert.Equal(">= 1.0.0 < 2.0.0", normalized.Constraint); | ||||
|         Assert.Equal("ghsa", normalized.Source); | ||||
|         Assert.Equal(new DateTime(2025, 10, 9, 0, 0, 0, DateTimeKind.Utc), normalized.RecordedAtUtc); | ||||
|     } | ||||
|  | ||||
|     private static Advisory CreateNormalizedAdvisory(string advisoryKey) | ||||
|     { | ||||
|         var recordedAt = new DateTimeOffset(2025, 10, 9, 0, 0, 0, TimeSpan.Zero); | ||||
|         var rangeProvenance = new AdvisoryProvenance( | ||||
|             source: "ghsa", | ||||
|             kind: "affected-range", | ||||
|             value: "pkg:npm/example", | ||||
|             recordedAt: recordedAt, | ||||
|             fieldMask: new[] { "affectedpackages[].versionranges[]" }, | ||||
|             decisionReason: "range-decision"); | ||||
|  | ||||
|         var semverPrimitive = new SemVerPrimitive( | ||||
|             Introduced: "1.0.0", | ||||
|             IntroducedInclusive: true, | ||||
|             Fixed: "2.0.0", | ||||
|             FixedInclusive: false, | ||||
|             LastAffected: null, | ||||
|             LastAffectedInclusive: false, | ||||
|             ConstraintExpression: ">= 1.0.0 < 2.0.0"); | ||||
|  | ||||
|         var normalizedRule = semverPrimitive.ToNormalizedVersionRule("ghsa:pkg:npm/example")!; | ||||
|         var versionRange = new AffectedVersionRange( | ||||
|             rangeKind: "semver", | ||||
|             introducedVersion: "1.0.0", | ||||
|             fixedVersion: "2.0.0", | ||||
|             lastAffectedVersion: null, | ||||
|             rangeExpression: ">= 1.0.0 < 2.0.0", | ||||
|             provenance: rangeProvenance, | ||||
|             primitives: new RangePrimitives(semverPrimitive, null, null, null)); | ||||
|  | ||||
|         var package = new AffectedPackage( | ||||
|             type: AffectedPackageTypes.SemVer, | ||||
|             identifier: "pkg:npm/example", | ||||
|             platform: "npm", | ||||
|             versionRanges: new[] { versionRange }, | ||||
|             statuses: Array.Empty<AffectedPackageStatus>(), | ||||
|             provenance: new[] { rangeProvenance }, | ||||
|             normalizedVersions: new[] { normalizedRule }); | ||||
|  | ||||
|         var advisoryProvenance = new AdvisoryProvenance( | ||||
|             source: "ghsa", | ||||
|             kind: "document", | ||||
|             value: advisoryKey, | ||||
|             recordedAt: recordedAt, | ||||
|             fieldMask: new[] { "advisory" }, | ||||
|             decisionReason: "document-decision"); | ||||
|  | ||||
|         return new Advisory( | ||||
|             advisoryKey: advisoryKey, | ||||
|             title: "Normalized advisory", | ||||
|             summary: "Contains normalized versions for storage testing.", | ||||
|             language: "en", | ||||
|             published: recordedAt, | ||||
|             modified: recordedAt, | ||||
|             severity: "medium", | ||||
|             exploitKnown: false, | ||||
|             aliases: new[] { $"{advisoryKey}-ALIAS" }, | ||||
|             references: Array.Empty<AdvisoryReference>(), | ||||
|             affectedPackages: new[] { package }, | ||||
|             cvssMetrics: Array.Empty<CvssMetric>(), | ||||
|             provenance: new[] { advisoryProvenance }); | ||||
|     } | ||||
|  | ||||
|     private async Task DropCollectionAsync(string collectionName) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await _fixture.Database.DropCollectionAsync(collectionName); | ||||
|         } | ||||
|         catch (MongoDB.Driver.MongoCommandException ex) when (ex.CodeName == "NamespaceNotFound" || ex.Message.Contains("ns not found", StringComparison.OrdinalIgnoreCase)) | ||||
|         { | ||||
|   | ||||
| @@ -0,0 +1,97 @@ | ||||
| using System; | ||||
| using System.Linq; | ||||
| using System.Threading; | ||||
| using Microsoft.Extensions.Logging.Abstractions; | ||||
| using Microsoft.Extensions.Options; | ||||
| using MongoDB.Bson; | ||||
| using MongoDB.Driver; | ||||
| using StellaOps.Feedser.Storage.Mongo; | ||||
| using StellaOps.Feedser.Storage.Mongo.Migrations; | ||||
| using Xunit; | ||||
|  | ||||
| namespace StellaOps.Feedser.Storage.Mongo.Tests; | ||||
|  | ||||
| [Collection("mongo-fixture")] | ||||
| public sealed class MongoBootstrapperTests : IClassFixture<MongoIntegrationFixture> | ||||
| { | ||||
|     private readonly MongoIntegrationFixture _fixture; | ||||
|  | ||||
|     public MongoBootstrapperTests(MongoIntegrationFixture fixture) | ||||
|     { | ||||
|         _fixture = fixture; | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public async Task InitializeAsync_CreatesNormalizedIndexesWhenSemVerStyleEnabled() | ||||
|     { | ||||
|         var databaseName = $"feedser-bootstrap-semver-{Guid.NewGuid():N}"; | ||||
|         var database = _fixture.Client.GetDatabase(databaseName); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             var runner = new MongoMigrationRunner( | ||||
|                 database, | ||||
|                 Array.Empty<IMongoMigration>(), | ||||
|                 NullLogger<MongoMigrationRunner>.Instance, | ||||
|                 TimeProvider.System); | ||||
|  | ||||
|             var bootstrapper = new MongoBootstrapper( | ||||
|                 database, | ||||
|                 Options.Create(new MongoStorageOptions { EnableSemVerStyle = true }), | ||||
|                 NullLogger<MongoBootstrapper>.Instance, | ||||
|                 runner); | ||||
|  | ||||
|             await bootstrapper.InitializeAsync(CancellationToken.None); | ||||
|  | ||||
|             var indexCursor = await database | ||||
|                 .GetCollection<BsonDocument>(MongoStorageDefaults.Collections.Advisory) | ||||
|                 .Indexes | ||||
|                 .ListAsync(); | ||||
|             var indexNames = (await indexCursor.ToListAsync()).Select(x => x["name"].AsString).ToArray(); | ||||
|  | ||||
|             Assert.Contains("advisory_normalizedVersions_pkg_scheme_type", indexNames); | ||||
|             Assert.Contains("advisory_normalizedVersions_value", indexNames); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             await _fixture.Client.DropDatabaseAsync(databaseName); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public async Task InitializeAsync_DoesNotCreateNormalizedIndexesWhenFeatureDisabled() | ||||
|     { | ||||
|         var databaseName = $"feedser-bootstrap-no-semver-{Guid.NewGuid():N}"; | ||||
|         var database = _fixture.Client.GetDatabase(databaseName); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             var runner = new MongoMigrationRunner( | ||||
|                 database, | ||||
|                 Array.Empty<IMongoMigration>(), | ||||
|                 NullLogger<MongoMigrationRunner>.Instance, | ||||
|                 TimeProvider.System); | ||||
|  | ||||
|             var bootstrapper = new MongoBootstrapper( | ||||
|                 database, | ||||
|                 Options.Create(new MongoStorageOptions { EnableSemVerStyle = false }), | ||||
|                 NullLogger<MongoBootstrapper>.Instance, | ||||
|                 runner); | ||||
|  | ||||
|             await bootstrapper.InitializeAsync(CancellationToken.None); | ||||
|  | ||||
|             var indexCursor = await database | ||||
|                 .GetCollection<BsonDocument>(MongoStorageDefaults.Collections.Advisory) | ||||
|                 .Indexes | ||||
|                 .ListAsync(); | ||||
|             var indexNames = (await indexCursor.ToListAsync()).Select(x => x["name"].AsString).ToArray(); | ||||
|  | ||||
|             Assert.DoesNotContain("advisory_normalizedVersions_pkg_scheme_type", indexNames); | ||||
|             Assert.DoesNotContain("advisory_normalizedVersions_value", indexNames); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             await _fixture.Client.DropDatabaseAsync(databaseName); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user