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,204 +1,204 @@
using System.Net;
using System.Net.Http;
using System.Text;
using StellaOps.Concelier.Bson;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Concelier.Models;
using StellaOps.Concelier.Connector.Common.Testing;
using StellaOps.Concelier.Connector.Ghsa.Configuration;
using StellaOps.Concelier.Connector.Ghsa.Internal;
using StellaOps.Concelier.Testing;
using StellaOps.Concelier.Storage;
using StellaOps.Concelier.Storage.Advisories;
namespace StellaOps.Concelier.Connector.Ghsa.Tests;
using System.Net;
using System.Net.Http;
using System.Text;
using StellaOps.Concelier.Documents;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Concelier.Models;
using StellaOps.Concelier.Connector.Common.Testing;
using StellaOps.Concelier.Connector.Ghsa.Configuration;
using StellaOps.Concelier.Connector.Ghsa.Internal;
using StellaOps.Concelier.Testing;
using StellaOps.Concelier.Storage;
using StellaOps.Concelier.Storage.Advisories;
namespace StellaOps.Concelier.Connector.Ghsa.Tests;
[Collection(ConcelierFixtureCollection.Name)]
public sealed class GhsaConnectorTests : IAsyncLifetime
{
private readonly ConcelierPostgresFixture _fixture;
private ConnectorTestHarness? _harness;
public GhsaConnectorTests(ConcelierPostgresFixture fixture)
{
_fixture = fixture;
}
[Fact]
public async Task FetchParseMap_EmitsCanonicalAdvisory()
{
var initialTime = new DateTimeOffset(2024, 10, 2, 0, 0, 0, TimeSpan.Zero);
await EnsureHarnessAsync(initialTime);
var harness = _harness!;
var since = initialTime - TimeSpan.FromDays(30);
var listUri = new Uri($"https://ghsa.test/security/advisories?updated_since={Uri.EscapeDataString(since.ToString("O"))}&updated_until={Uri.EscapeDataString(initialTime.ToString("O"))}&page=1&per_page=5");
harness.Handler.AddJsonResponse(listUri, ReadFixture("Fixtures/ghsa-list.json"));
harness.Handler.SetFallback(request =>
{
if (request.RequestUri is null)
{
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
if (request.RequestUri.AbsoluteUri.Equals("https://ghsa.test/security/advisories/GHSA-xxxx-yyyy-zzzz", StringComparison.OrdinalIgnoreCase))
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(ReadFixture("Fixtures/ghsa-GHSA-xxxx-yyyy-zzzz.json"), Encoding.UTF8, "application/json")
};
}
return new HttpResponseMessage(HttpStatusCode.NotFound);
});
var connector = new GhsaConnectorPlugin().Create(harness.ServiceProvider);
await connector.FetchAsync(harness.ServiceProvider, CancellationToken.None);
await connector.ParseAsync(harness.ServiceProvider, CancellationToken.None);
await connector.MapAsync(harness.ServiceProvider, CancellationToken.None);
var advisoryStore = harness.ServiceProvider.GetRequiredService<IAdvisoryStore>();
var advisory = await advisoryStore.FindAsync("GHSA-xxxx-yyyy-zzzz", CancellationToken.None);
Assert.NotNull(advisory);
Assert.Collection(advisory!.Credits,
credit =>
{
Assert.Equal("remediation_developer", credit.Role);
Assert.Equal("maintainer-team", credit.DisplayName);
Assert.Contains("https://github.com/maintainer-team", credit.Contacts);
},
credit =>
{
Assert.Equal("reporter", credit.Role);
Assert.Equal("security-reporter", credit.DisplayName);
Assert.Contains("https://github.com/security-reporter", credit.Contacts);
});
var weakness = Assert.Single(advisory.Cwes);
Assert.Equal("CWE-79", weakness.Identifier);
Assert.Equal("https://cwe.mitre.org/data/definitions/79.html", weakness.Uri);
var metric = Assert.Single(advisory.CvssMetrics);
Assert.Equal("3.1", metric.Version);
Assert.Equal("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", metric.Vector);
Assert.Equal("critical", metric.BaseSeverity);
Assert.Equal("3.1|CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", advisory.CanonicalMetricId);
var snapshot = SnapshotSerializer.ToSnapshot(advisory).Replace("\r\n", "\n").TrimEnd();
var expected = ReadFixture("Fixtures/expected-GHSA-xxxx-yyyy-zzzz.json").Replace("\r\n", "\n").TrimEnd();
if (!string.Equals(expected, snapshot, StringComparison.Ordinal))
{
var actualPath = Path.Combine(AppContext.BaseDirectory, "Fixtures", "expected-GHSA-xxxx-yyyy-zzzz.actual.json");
Directory.CreateDirectory(Path.GetDirectoryName(actualPath)!);
File.WriteAllText(actualPath, snapshot);
}
Assert.Equal(expected, snapshot);
harness.Handler.AssertNoPendingResponses();
}
[Fact]
public async Task FetchAsync_RateLimitDefersWindowAndRecordsSnapshot()
{
var initialTime = new DateTimeOffset(2024, 10, 5, 0, 0, 0, TimeSpan.Zero);
await EnsureHarnessAsync(initialTime);
var harness = _harness!;
var since = initialTime - TimeSpan.FromDays(30);
var until = initialTime;
var listUri = new Uri($"https://ghsa.test/security/advisories?updated_since={Uri.EscapeDataString(since.ToString("O"))}&updated_until={Uri.EscapeDataString(until.ToString("O"))}&page=1&per_page=5");
harness.Handler.AddResponse(HttpMethod.Get, listUri, _ =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(ReadFixture("Fixtures/ghsa-list.json"), Encoding.UTF8, "application/json")
};
response.Headers.TryAddWithoutValidation("X-RateLimit-Resource", "core");
response.Headers.TryAddWithoutValidation("X-RateLimit-Limit", "5000");
response.Headers.TryAddWithoutValidation("X-RateLimit-Remaining", "0");
return response;
});
harness.Handler.SetFallback(_ => new HttpResponseMessage(HttpStatusCode.NotFound));
var connector = new GhsaConnectorPlugin().Create(harness.ServiceProvider);
await connector.FetchAsync(harness.ServiceProvider, CancellationToken.None);
Assert.Single(harness.Handler.Requests);
var diagnostics = harness.ServiceProvider.GetRequiredService<GhsaDiagnostics>();
var snapshot = diagnostics.GetLastRateLimitSnapshot();
Assert.True(snapshot.HasValue);
Assert.Equal("list", snapshot!.Value.Phase);
Assert.Equal("core", snapshot.Value.Resource);
Assert.Equal(0, snapshot.Value.Remaining);
var stateRepository = harness.ServiceProvider.GetRequiredService<ISourceStateRepository>();
var state = await stateRepository.TryGetAsync(GhsaConnectorPlugin.SourceName, CancellationToken.None);
Assert.NotNull(state);
Assert.True(state!.Cursor.TryGetValue("currentWindowStart", out var startValue));
Assert.True(state.Cursor.TryGetValue("currentWindowEnd", out var endValue));
Assert.True(state.Cursor.TryGetValue("nextPage", out var nextPageValue));
Assert.Equal(since.UtcDateTime, startValue.ToUniversalTime());
Assert.Equal(until.UtcDateTime, endValue.ToUniversalTime());
Assert.Equal(1, nextPageValue.AsInt32);
Assert.True(state.Cursor.TryGetValue("pendingDocuments", out var pendingDocs));
Assert.Empty(pendingDocs.AsBsonArray);
Assert.True(state.Cursor.TryGetValue("pendingMappings", out var pendingMappings));
Assert.Empty(pendingMappings.AsBsonArray);
}
private async Task EnsureHarnessAsync(DateTimeOffset initialTime)
{
if (_harness is not null)
{
return;
}
var harness = new ConnectorTestHarness(_fixture, initialTime, GhsaOptions.HttpClientName);
await harness.EnsureServiceProviderAsync(services =>
{
services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance));
services.AddGhsaConnector(options =>
{
options.BaseEndpoint = new Uri("https://ghsa.test/", UriKind.Absolute);
options.ApiToken = "test-token";
options.PageSize = 5;
options.MaxPagesPerFetch = 2;
options.RequestDelay = TimeSpan.Zero;
options.InitialBackfill = TimeSpan.FromDays(30);
options.SecondaryRateLimitBackoff = TimeSpan.FromMilliseconds(10);
});
});
_harness = harness;
}
private static string ReadFixture(string relativePath)
{
var path = Path.Combine(AppContext.BaseDirectory, relativePath);
return File.ReadAllText(path);
}
public async Task InitializeAsync()
{
await Task.CompletedTask;
}
public async Task DisposeAsync()
{
if (_harness is not null)
{
await _harness.DisposeAsync();
}
}
}
public sealed class GhsaConnectorTests : IAsyncLifetime
{
private readonly ConcelierPostgresFixture _fixture;
private ConnectorTestHarness? _harness;
public GhsaConnectorTests(ConcelierPostgresFixture fixture)
{
_fixture = fixture;
}
[Fact]
public async Task FetchParseMap_EmitsCanonicalAdvisory()
{
var initialTime = new DateTimeOffset(2024, 10, 2, 0, 0, 0, TimeSpan.Zero);
await EnsureHarnessAsync(initialTime);
var harness = _harness!;
var since = initialTime - TimeSpan.FromDays(30);
var listUri = new Uri($"https://ghsa.test/security/advisories?updated_since={Uri.EscapeDataString(since.ToString("O"))}&updated_until={Uri.EscapeDataString(initialTime.ToString("O"))}&page=1&per_page=5");
harness.Handler.AddJsonResponse(listUri, ReadFixture("Fixtures/ghsa-list.json"));
harness.Handler.SetFallback(request =>
{
if (request.RequestUri is null)
{
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
if (request.RequestUri.AbsoluteUri.Equals("https://ghsa.test/security/advisories/GHSA-xxxx-yyyy-zzzz", StringComparison.OrdinalIgnoreCase))
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(ReadFixture("Fixtures/ghsa-GHSA-xxxx-yyyy-zzzz.json"), Encoding.UTF8, "application/json")
};
}
return new HttpResponseMessage(HttpStatusCode.NotFound);
});
var connector = new GhsaConnectorPlugin().Create(harness.ServiceProvider);
await connector.FetchAsync(harness.ServiceProvider, CancellationToken.None);
await connector.ParseAsync(harness.ServiceProvider, CancellationToken.None);
await connector.MapAsync(harness.ServiceProvider, CancellationToken.None);
var advisoryStore = harness.ServiceProvider.GetRequiredService<IAdvisoryStore>();
var advisory = await advisoryStore.FindAsync("GHSA-xxxx-yyyy-zzzz", CancellationToken.None);
Assert.NotNull(advisory);
Assert.Collection(advisory!.Credits,
credit =>
{
Assert.Equal("remediation_developer", credit.Role);
Assert.Equal("maintainer-team", credit.DisplayName);
Assert.Contains("https://github.com/maintainer-team", credit.Contacts);
},
credit =>
{
Assert.Equal("reporter", credit.Role);
Assert.Equal("security-reporter", credit.DisplayName);
Assert.Contains("https://github.com/security-reporter", credit.Contacts);
});
var weakness = Assert.Single(advisory.Cwes);
Assert.Equal("CWE-79", weakness.Identifier);
Assert.Equal("https://cwe.mitre.org/data/definitions/79.html", weakness.Uri);
var metric = Assert.Single(advisory.CvssMetrics);
Assert.Equal("3.1", metric.Version);
Assert.Equal("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", metric.Vector);
Assert.Equal("critical", metric.BaseSeverity);
Assert.Equal("3.1|CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", advisory.CanonicalMetricId);
var snapshot = SnapshotSerializer.ToSnapshot(advisory).Replace("\r\n", "\n").TrimEnd();
var expected = ReadFixture("Fixtures/expected-GHSA-xxxx-yyyy-zzzz.json").Replace("\r\n", "\n").TrimEnd();
if (!string.Equals(expected, snapshot, StringComparison.Ordinal))
{
var actualPath = Path.Combine(AppContext.BaseDirectory, "Fixtures", "expected-GHSA-xxxx-yyyy-zzzz.actual.json");
Directory.CreateDirectory(Path.GetDirectoryName(actualPath)!);
File.WriteAllText(actualPath, snapshot);
}
Assert.Equal(expected, snapshot);
harness.Handler.AssertNoPendingResponses();
}
[Fact]
public async Task FetchAsync_RateLimitDefersWindowAndRecordsSnapshot()
{
var initialTime = new DateTimeOffset(2024, 10, 5, 0, 0, 0, TimeSpan.Zero);
await EnsureHarnessAsync(initialTime);
var harness = _harness!;
var since = initialTime - TimeSpan.FromDays(30);
var until = initialTime;
var listUri = new Uri($"https://ghsa.test/security/advisories?updated_since={Uri.EscapeDataString(since.ToString("O"))}&updated_until={Uri.EscapeDataString(until.ToString("O"))}&page=1&per_page=5");
harness.Handler.AddResponse(HttpMethod.Get, listUri, _ =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(ReadFixture("Fixtures/ghsa-list.json"), Encoding.UTF8, "application/json")
};
response.Headers.TryAddWithoutValidation("X-RateLimit-Resource", "core");
response.Headers.TryAddWithoutValidation("X-RateLimit-Limit", "5000");
response.Headers.TryAddWithoutValidation("X-RateLimit-Remaining", "0");
return response;
});
harness.Handler.SetFallback(_ => new HttpResponseMessage(HttpStatusCode.NotFound));
var connector = new GhsaConnectorPlugin().Create(harness.ServiceProvider);
await connector.FetchAsync(harness.ServiceProvider, CancellationToken.None);
Assert.Single(harness.Handler.Requests);
var diagnostics = harness.ServiceProvider.GetRequiredService<GhsaDiagnostics>();
var snapshot = diagnostics.GetLastRateLimitSnapshot();
Assert.True(snapshot.HasValue);
Assert.Equal("list", snapshot!.Value.Phase);
Assert.Equal("core", snapshot.Value.Resource);
Assert.Equal(0, snapshot.Value.Remaining);
var stateRepository = harness.ServiceProvider.GetRequiredService<ISourceStateRepository>();
var state = await stateRepository.TryGetAsync(GhsaConnectorPlugin.SourceName, CancellationToken.None);
Assert.NotNull(state);
Assert.True(state!.Cursor.TryGetValue("currentWindowStart", out var startValue));
Assert.True(state.Cursor.TryGetValue("currentWindowEnd", out var endValue));
Assert.True(state.Cursor.TryGetValue("nextPage", out var nextPageValue));
Assert.Equal(since.UtcDateTime, startValue.ToUniversalTime());
Assert.Equal(until.UtcDateTime, endValue.ToUniversalTime());
Assert.Equal(1, nextPageValue.AsInt32);
Assert.True(state.Cursor.TryGetValue("pendingDocuments", out var pendingDocs));
Assert.Empty(pendingDocs.AsDocumentArray);
Assert.True(state.Cursor.TryGetValue("pendingMappings", out var pendingMappings));
Assert.Empty(pendingMappings.AsDocumentArray);
}
private async Task EnsureHarnessAsync(DateTimeOffset initialTime)
{
if (_harness is not null)
{
return;
}
var harness = new ConnectorTestHarness(_fixture, initialTime, GhsaOptions.HttpClientName);
await harness.EnsureServiceProviderAsync(services =>
{
services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance));
services.AddGhsaConnector(options =>
{
options.BaseEndpoint = new Uri("https://ghsa.test/", UriKind.Absolute);
options.ApiToken = "test-token";
options.PageSize = 5;
options.MaxPagesPerFetch = 2;
options.RequestDelay = TimeSpan.Zero;
options.InitialBackfill = TimeSpan.FromDays(30);
options.SecondaryRateLimitBackoff = TimeSpan.FromMilliseconds(10);
});
});
_harness = harness;
}
private static string ReadFixture(string relativePath)
{
var path = Path.Combine(AppContext.BaseDirectory, relativePath);
return File.ReadAllText(path);
}
public async Task InitializeAsync()
{
await Task.CompletedTask;
}
public async Task DisposeAsync()
{
if (_harness is not null)
{
await _harness.DisposeAsync();
}
}
}

View File

@@ -1,51 +1,51 @@
using System.Collections.Immutable;
using System.Linq;
using System.Text.Json;
using StellaOps.Concelier.Models;
using Xunit;
namespace StellaOps.Concelier.Connector.Ghsa.Tests.Ghsa;
public sealed class GhsaCreditParityRegressionTests
{
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web);
[Fact]
public void CreditParity_FixturesRemainInSyncAcrossSources()
{
var ghsa = LoadFixture("credit-parity.ghsa.json");
var osv = LoadFixture("credit-parity.osv.json");
var nvd = LoadFixture("credit-parity.nvd.json");
var ghsaCredits = NormalizeCredits(ghsa);
var osvCredits = NormalizeCredits(osv);
var nvdCredits = NormalizeCredits(nvd);
Assert.NotEmpty(ghsaCredits);
Assert.Equal(ghsaCredits, osvCredits);
Assert.Equal(ghsaCredits, nvdCredits);
}
private static Advisory LoadFixture(string fileName)
{
var path = Path.Combine(AppContext.BaseDirectory, "Fixtures", fileName);
return JsonSerializer.Deserialize<Advisory>(File.ReadAllText(path), SerializerOptions)
?? throw new InvalidOperationException($"Failed to deserialize fixture '{fileName}'.");
}
private static HashSet<string> NormalizeCredits(Advisory advisory)
{
var set = new HashSet<string>(StringComparer.Ordinal);
foreach (var credit in advisory.Credits)
{
var contactList = credit.Contacts.IsDefaultOrEmpty
? Array.Empty<string>()
: credit.Contacts.ToArray();
var contacts = string.Join("|", contactList.OrderBy(static contact => contact, StringComparer.Ordinal));
var key = string.Join("||", credit.Role ?? string.Empty, credit.DisplayName ?? string.Empty, contacts);
set.Add(key);
}
return set;
}
}
using System.Collections.Immutable;
using System.Linq;
using System.Text.Json;
using StellaOps.Concelier.Models;
using Xunit;
namespace StellaOps.Concelier.Connector.Ghsa.Tests.Ghsa;
public sealed class GhsaCreditParityRegressionTests
{
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web);
[Fact]
public void CreditParity_FixturesRemainInSyncAcrossSources()
{
var ghsa = LoadFixture("credit-parity.ghsa.json");
var osv = LoadFixture("credit-parity.osv.json");
var nvd = LoadFixture("credit-parity.nvd.json");
var ghsaCredits = NormalizeCredits(ghsa);
var osvCredits = NormalizeCredits(osv);
var nvdCredits = NormalizeCredits(nvd);
Assert.NotEmpty(ghsaCredits);
Assert.Equal(ghsaCredits, osvCredits);
Assert.Equal(ghsaCredits, nvdCredits);
}
private static Advisory LoadFixture(string fileName)
{
var path = Path.Combine(AppContext.BaseDirectory, "Fixtures", fileName);
return JsonSerializer.Deserialize<Advisory>(File.ReadAllText(path), SerializerOptions)
?? throw new InvalidOperationException($"Failed to deserialize fixture '{fileName}'.");
}
private static HashSet<string> NormalizeCredits(Advisory advisory)
{
var set = new HashSet<string>(StringComparer.Ordinal);
foreach (var credit in advisory.Credits)
{
var contactList = credit.Contacts.IsDefaultOrEmpty
? Array.Empty<string>()
: credit.Contacts.ToArray();
var contacts = string.Join("|", contactList.OrderBy(static contact => contact, StringComparer.Ordinal));
var key = string.Join("||", credit.Role ?? string.Empty, credit.DisplayName ?? string.Empty, contacts);
set.Add(key);
}
return set;
}
}

View File

@@ -1,71 +1,71 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Concelier.Core.Jobs;
using StellaOps.Concelier.Connector.Common.Http;
using StellaOps.Concelier.Connector.Ghsa;
using StellaOps.Concelier.Connector.Ghsa.Configuration;
using Xunit;
namespace StellaOps.Concelier.Connector.Ghsa.Tests.Ghsa;
public sealed class GhsaDependencyInjectionRoutineTests
{
[Fact]
public void Register_ConfiguresConnectorAndScheduler()
{
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance));
services.AddOptions();
services.AddSourceCommon();
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["concelier:sources:ghsa:apiToken"] = "test-token",
["concelier:sources:ghsa:pageSize"] = "25",
["concelier:sources:ghsa:maxPagesPerFetch"] = "3",
["concelier:sources:ghsa:initialBackfill"] = "1.00:00:00",
})
.Build();
var routine = new GhsaDependencyInjectionRoutine();
routine.Register(services, configuration);
services.Configure<JobSchedulerOptions>(_ => { });
var provider = services.BuildServiceProvider(validateScopes: true);
var ghsaOptions = provider.GetRequiredService<IOptions<GhsaOptions>>().Value;
Assert.Equal("test-token", ghsaOptions.ApiToken);
Assert.Equal(25, ghsaOptions.PageSize);
Assert.Equal(TimeSpan.FromDays(1), ghsaOptions.InitialBackfill);
var schedulerOptions = provider.GetRequiredService<IOptions<JobSchedulerOptions>>().Value;
Assert.True(schedulerOptions.Definitions.TryGetValue(GhsaJobKinds.Fetch, out var fetchDefinition));
Assert.True(schedulerOptions.Definitions.TryGetValue(GhsaJobKinds.Parse, out var parseDefinition));
Assert.True(schedulerOptions.Definitions.TryGetValue(GhsaJobKinds.Map, out var mapDefinition));
Assert.Equal(typeof(GhsaFetchJob), fetchDefinition.JobType);
Assert.Equal(TimeSpan.FromMinutes(6), fetchDefinition.Timeout);
Assert.Equal(TimeSpan.FromMinutes(4), fetchDefinition.LeaseDuration);
Assert.Equal("1,11,21,31,41,51 * * * *", fetchDefinition.CronExpression);
Assert.True(fetchDefinition.Enabled);
Assert.Equal(typeof(GhsaParseJob), parseDefinition.JobType);
Assert.Equal(TimeSpan.FromMinutes(5), parseDefinition.Timeout);
Assert.Equal(TimeSpan.FromMinutes(4), parseDefinition.LeaseDuration);
Assert.Equal("3,13,23,33,43,53 * * * *", parseDefinition.CronExpression);
Assert.True(parseDefinition.Enabled);
Assert.Equal(typeof(GhsaMapJob), mapDefinition.JobType);
Assert.Equal(TimeSpan.FromMinutes(5), mapDefinition.Timeout);
Assert.Equal(TimeSpan.FromMinutes(4), mapDefinition.LeaseDuration);
Assert.Equal("5,15,25,35,45,55 * * * *", mapDefinition.CronExpression);
Assert.True(mapDefinition.Enabled);
}
}
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Concelier.Core.Jobs;
using StellaOps.Concelier.Connector.Common.Http;
using StellaOps.Concelier.Connector.Ghsa;
using StellaOps.Concelier.Connector.Ghsa.Configuration;
using Xunit;
namespace StellaOps.Concelier.Connector.Ghsa.Tests.Ghsa;
public sealed class GhsaDependencyInjectionRoutineTests
{
[Fact]
public void Register_ConfiguresConnectorAndScheduler()
{
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddProvider(NullLoggerProvider.Instance));
services.AddOptions();
services.AddSourceCommon();
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["concelier:sources:ghsa:apiToken"] = "test-token",
["concelier:sources:ghsa:pageSize"] = "25",
["concelier:sources:ghsa:maxPagesPerFetch"] = "3",
["concelier:sources:ghsa:initialBackfill"] = "1.00:00:00",
})
.Build();
var routine = new GhsaDependencyInjectionRoutine();
routine.Register(services, configuration);
services.Configure<JobSchedulerOptions>(_ => { });
var provider = services.BuildServiceProvider(validateScopes: true);
var ghsaOptions = provider.GetRequiredService<IOptions<GhsaOptions>>().Value;
Assert.Equal("test-token", ghsaOptions.ApiToken);
Assert.Equal(25, ghsaOptions.PageSize);
Assert.Equal(TimeSpan.FromDays(1), ghsaOptions.InitialBackfill);
var schedulerOptions = provider.GetRequiredService<IOptions<JobSchedulerOptions>>().Value;
Assert.True(schedulerOptions.Definitions.TryGetValue(GhsaJobKinds.Fetch, out var fetchDefinition));
Assert.True(schedulerOptions.Definitions.TryGetValue(GhsaJobKinds.Parse, out var parseDefinition));
Assert.True(schedulerOptions.Definitions.TryGetValue(GhsaJobKinds.Map, out var mapDefinition));
Assert.Equal(typeof(GhsaFetchJob), fetchDefinition.JobType);
Assert.Equal(TimeSpan.FromMinutes(6), fetchDefinition.Timeout);
Assert.Equal(TimeSpan.FromMinutes(4), fetchDefinition.LeaseDuration);
Assert.Equal("1,11,21,31,41,51 * * * *", fetchDefinition.CronExpression);
Assert.True(fetchDefinition.Enabled);
Assert.Equal(typeof(GhsaParseJob), parseDefinition.JobType);
Assert.Equal(TimeSpan.FromMinutes(5), parseDefinition.Timeout);
Assert.Equal(TimeSpan.FromMinutes(4), parseDefinition.LeaseDuration);
Assert.Equal("3,13,23,33,43,53 * * * *", parseDefinition.CronExpression);
Assert.True(parseDefinition.Enabled);
Assert.Equal(typeof(GhsaMapJob), mapDefinition.JobType);
Assert.Equal(TimeSpan.FromMinutes(5), mapDefinition.Timeout);
Assert.Equal(TimeSpan.FromMinutes(4), mapDefinition.LeaseDuration);
Assert.Equal("5,15,25,35,45,55 * * * *", mapDefinition.CronExpression);
Assert.True(mapDefinition.Enabled);
}
}

View File

@@ -1,35 +1,35 @@
using System;
using StellaOps.Concelier.Connector.Ghsa.Internal;
using Xunit;
namespace StellaOps.Concelier.Connector.Ghsa.Tests.Ghsa;
public class GhsaDiagnosticsTests : IDisposable
{
private readonly GhsaDiagnostics diagnostics = new();
[Fact]
public void RecordRateLimit_PersistsSnapshot()
{
var snapshot = new GhsaRateLimitSnapshot(
Phase: "list",
Resource: "core",
Limit: 5000,
Remaining: 100,
Used: 4900,
ResetAt: DateTimeOffset.UtcNow.AddMinutes(1),
ResetAfter: TimeSpan.FromMinutes(1),
RetryAfter: TimeSpan.FromSeconds(10));
diagnostics.RecordRateLimit(snapshot);
var stored = diagnostics.GetLastRateLimitSnapshot();
Assert.NotNull(stored);
Assert.Equal(snapshot, stored);
}
public void Dispose()
{
diagnostics.Dispose();
}
}
using System;
using StellaOps.Concelier.Connector.Ghsa.Internal;
using Xunit;
namespace StellaOps.Concelier.Connector.Ghsa.Tests.Ghsa;
public class GhsaDiagnosticsTests : IDisposable
{
private readonly GhsaDiagnostics diagnostics = new();
[Fact]
public void RecordRateLimit_PersistsSnapshot()
{
var snapshot = new GhsaRateLimitSnapshot(
Phase: "list",
Resource: "core",
Limit: 5000,
Remaining: 100,
Used: 4900,
ResetAt: DateTimeOffset.UtcNow.AddMinutes(1),
ResetAfter: TimeSpan.FromMinutes(1),
RetryAfter: TimeSpan.FromSeconds(10));
diagnostics.RecordRateLimit(snapshot);
var stored = diagnostics.GetLastRateLimitSnapshot();
Assert.NotNull(stored);
Assert.Equal(snapshot, stored);
}
public void Dispose()
{
diagnostics.Dispose();
}
}

View File

@@ -1,60 +1,60 @@
using System.Collections.Generic;
using System.Globalization;
using StellaOps.Concelier.Connector.Ghsa.Internal;
using Xunit;
namespace StellaOps.Concelier.Connector.Ghsa.Tests.Ghsa;
public class GhsaRateLimitParserTests
{
[Fact]
public void TryParse_ReturnsSnapshot_WhenHeadersPresent()
{
var now = DateTimeOffset.UtcNow;
var reset = now.AddMinutes(5);
var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["X-RateLimit-Limit"] = "5000",
["X-RateLimit-Remaining"] = "42",
["X-RateLimit-Used"] = "4958",
["X-RateLimit-Reset"] = reset.ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture),
["X-RateLimit-Resource"] = "core"
};
var snapshot = GhsaRateLimitParser.TryParse(headers, now, "list");
Assert.True(snapshot.HasValue);
Assert.Equal("list", snapshot.Value.Phase);
Assert.Equal("core", snapshot.Value.Resource);
Assert.Equal(5000, snapshot.Value.Limit);
Assert.Equal(42, snapshot.Value.Remaining);
Assert.Equal(4958, snapshot.Value.Used);
Assert.NotNull(snapshot.Value.ResetAfter);
Assert.True(snapshot.Value.ResetAfter!.Value.TotalMinutes <= 5.1 && snapshot.Value.ResetAfter.Value.TotalMinutes >= 4.9);
}
[Fact]
public void TryParse_ReturnsNull_WhenHeadersMissing()
{
var snapshot = GhsaRateLimitParser.TryParse(null, DateTimeOffset.UtcNow, "list");
Assert.Null(snapshot);
}
[Fact]
public void TryParse_HandlesRetryAfter()
{
var now = DateTimeOffset.UtcNow;
var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["Retry-After"] = "60"
};
var snapshot = GhsaRateLimitParser.TryParse(headers, now, "detail");
Assert.True(snapshot.HasValue);
Assert.Equal("detail", snapshot.Value.Phase);
Assert.NotNull(snapshot.Value.RetryAfter);
Assert.Equal(60, Math.Round(snapshot.Value.RetryAfter!.Value.TotalSeconds));
}
}
using System.Collections.Generic;
using System.Globalization;
using StellaOps.Concelier.Connector.Ghsa.Internal;
using Xunit;
namespace StellaOps.Concelier.Connector.Ghsa.Tests.Ghsa;
public class GhsaRateLimitParserTests
{
[Fact]
public void TryParse_ReturnsSnapshot_WhenHeadersPresent()
{
var now = DateTimeOffset.UtcNow;
var reset = now.AddMinutes(5);
var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["X-RateLimit-Limit"] = "5000",
["X-RateLimit-Remaining"] = "42",
["X-RateLimit-Used"] = "4958",
["X-RateLimit-Reset"] = reset.ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture),
["X-RateLimit-Resource"] = "core"
};
var snapshot = GhsaRateLimitParser.TryParse(headers, now, "list");
Assert.True(snapshot.HasValue);
Assert.Equal("list", snapshot.Value.Phase);
Assert.Equal("core", snapshot.Value.Resource);
Assert.Equal(5000, snapshot.Value.Limit);
Assert.Equal(42, snapshot.Value.Remaining);
Assert.Equal(4958, snapshot.Value.Used);
Assert.NotNull(snapshot.Value.ResetAfter);
Assert.True(snapshot.Value.ResetAfter!.Value.TotalMinutes <= 5.1 && snapshot.Value.ResetAfter.Value.TotalMinutes >= 4.9);
}
[Fact]
public void TryParse_ReturnsNull_WhenHeadersMissing()
{
var snapshot = GhsaRateLimitParser.TryParse(null, DateTimeOffset.UtcNow, "list");
Assert.Null(snapshot);
}
[Fact]
public void TryParse_HandlesRetryAfter()
{
var now = DateTimeOffset.UtcNow;
var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["Retry-After"] = "60"
};
var snapshot = GhsaRateLimitParser.TryParse(headers, now, "detail");
Assert.True(snapshot.HasValue);
Assert.Equal("detail", snapshot.Value.Phase);
Assert.NotNull(snapshot.Value.RetryAfter);
Assert.Equal(60, Math.Round(snapshot.Value.RetryAfter!.Value.TotalSeconds));
}
}