Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
Lighthouse CI / Lighthouse Audit (push) Has been cancelled
Lighthouse CI / Axe Accessibility Audit (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Reachability Corpus Validation / validate-corpus (push) Has been cancelled
Reachability Corpus Validation / validate-ground-truths (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Reachability Corpus Validation / determinism-check (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
- Introduced `all-edge-reasons.json` to test edge resolution reasons in .NET. - Added `all-visibility-levels.json` to validate method visibility levels in .NET. - Created `dotnet-aspnetcore-minimal.json` for a minimal ASP.NET Core application. - Included `go-gin-api.json` for a Go Gin API application structure. - Added `java-spring-boot.json` for the Spring PetClinic application in Java. - Introduced `legacy-no-schema.json` for legacy application structure without schema. - Created `node-express-api.json` for an Express.js API application structure.
545 lines
19 KiB
C#
545 lines
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using MsOptions = Microsoft.Extensions.Options;
|
|
using StellaOps.Signals.Models;
|
|
using StellaOps.Signals.Options;
|
|
using StellaOps.Signals.Persistence;
|
|
using StellaOps.Signals.Services;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Signals.Tests;
|
|
|
|
public class UnknownsDecayServiceTests
|
|
{
|
|
private readonly MockTimeProvider _timeProvider;
|
|
private readonly InMemoryUnknownsRepository _unknownsRepo;
|
|
private readonly InMemoryDeploymentRefsRepository _deploymentRefs;
|
|
private readonly InMemoryGraphMetricsRepository _graphMetrics;
|
|
private readonly UnknownsScoringOptions _scoringOptions;
|
|
private readonly UnknownsDecayOptions _decayOptions;
|
|
|
|
public UnknownsDecayServiceTests()
|
|
{
|
|
_timeProvider = new MockTimeProvider(new DateTimeOffset(2025, 12, 15, 12, 0, 0, TimeSpan.Zero));
|
|
_unknownsRepo = new InMemoryUnknownsRepository();
|
|
_deploymentRefs = new InMemoryDeploymentRefsRepository();
|
|
_graphMetrics = new InMemoryGraphMetricsRepository();
|
|
_scoringOptions = new UnknownsScoringOptions();
|
|
_decayOptions = new UnknownsDecayOptions();
|
|
}
|
|
|
|
private (UnknownsDecayService DecayService, UnknownsScoringService ScoringService) CreateServices()
|
|
{
|
|
var scoringService = new UnknownsScoringService(
|
|
_unknownsRepo,
|
|
_deploymentRefs,
|
|
_graphMetrics,
|
|
MsOptions.Options.Create(_scoringOptions),
|
|
_timeProvider,
|
|
NullLogger<UnknownsScoringService>.Instance);
|
|
|
|
var decayService = new UnknownsDecayService(
|
|
_unknownsRepo,
|
|
scoringService,
|
|
MsOptions.Options.Create(_scoringOptions),
|
|
MsOptions.Options.Create(_decayOptions),
|
|
_timeProvider,
|
|
NullLogger<UnknownsDecayService>.Instance);
|
|
|
|
return (decayService, scoringService);
|
|
}
|
|
|
|
#region ApplyDecayAsync Tests
|
|
|
|
[Fact]
|
|
public async Task ApplyDecayAsync_EmptySubject_ReturnsZeroCounts()
|
|
{
|
|
var (decayService, _) = CreateServices();
|
|
|
|
var result = await decayService.ApplyDecayAsync("empty|1.0.0", CancellationToken.None);
|
|
|
|
Assert.Equal("empty|1.0.0", result.SubjectKey);
|
|
Assert.Equal(0, result.ProcessedCount);
|
|
Assert.Equal(0, result.HotCount);
|
|
Assert.Equal(0, result.WarmCount);
|
|
Assert.Equal(0, result.ColdCount);
|
|
Assert.Equal(0, result.BandChanges);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ApplyDecayAsync_SingleUnknown_UpdatesAndPersists()
|
|
{
|
|
var (decayService, _) = CreateServices();
|
|
var now = _timeProvider.GetUtcNow();
|
|
const string subjectKey = "test|1.0.0";
|
|
|
|
var unknown = new UnknownSymbolDocument
|
|
{
|
|
Id = "unknown-1",
|
|
SubjectKey = subjectKey,
|
|
LastAnalyzedAt = now.AddDays(-7),
|
|
Flags = new UnknownFlags(),
|
|
CreatedAt = now.AddDays(-10),
|
|
Band = UnknownsBand.Cold
|
|
};
|
|
|
|
await _unknownsRepo.UpsertAsync(subjectKey, new[] { unknown }, CancellationToken.None);
|
|
|
|
var result = await decayService.ApplyDecayAsync(subjectKey, CancellationToken.None);
|
|
|
|
Assert.Equal(1, result.ProcessedCount);
|
|
Assert.Equal(subjectKey, result.SubjectKey);
|
|
|
|
// Verify the unknown was updated in the repository
|
|
var updated = await _unknownsRepo.GetBySubjectAsync(subjectKey, CancellationToken.None);
|
|
Assert.Single(updated);
|
|
Assert.True(updated[0].UpdatedAt >= now);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ApplyDecayAsync_BandChangesTracked()
|
|
{
|
|
var (decayService, _) = CreateServices();
|
|
var now = _timeProvider.GetUtcNow();
|
|
const string subjectKey = "test|1.0.0";
|
|
|
|
// Create unknown that will change from COLD to HOT due to high staleness and flags
|
|
var unknown = new UnknownSymbolDocument
|
|
{
|
|
Id = "unknown-1",
|
|
SubjectKey = subjectKey,
|
|
LastAnalyzedAt = now.AddDays(-14),
|
|
Flags = new UnknownFlags
|
|
{
|
|
NoProvenanceAnchor = true,
|
|
VersionRange = true,
|
|
ConflictingFeeds = true,
|
|
MissingVector = true
|
|
},
|
|
CreatedAt = now.AddDays(-20),
|
|
Band = UnknownsBand.Cold // Initially cold
|
|
};
|
|
|
|
_deploymentRefs.SetDeploymentCount("pkg:npm/test@1.0.0", 100);
|
|
await _unknownsRepo.UpsertAsync(subjectKey, new[] { unknown }, CancellationToken.None);
|
|
|
|
var result = await decayService.ApplyDecayAsync(subjectKey, CancellationToken.None);
|
|
|
|
// Band should have changed from COLD to HOT
|
|
if (result.HotCount > 0)
|
|
{
|
|
Assert.Equal(1, result.BandChanges);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ApplyDecayAsync_MultipleUnknowns_ProcessesAll()
|
|
{
|
|
var (decayService, _) = CreateServices();
|
|
var now = _timeProvider.GetUtcNow();
|
|
const string subjectKey = "test|1.0.0";
|
|
|
|
var unknowns = new[]
|
|
{
|
|
new UnknownSymbolDocument
|
|
{
|
|
Id = "unknown-1",
|
|
SubjectKey = subjectKey,
|
|
LastAnalyzedAt = now,
|
|
Flags = new UnknownFlags(),
|
|
CreatedAt = now.AddDays(-1),
|
|
Band = UnknownsBand.Cold
|
|
},
|
|
new UnknownSymbolDocument
|
|
{
|
|
Id = "unknown-2",
|
|
SubjectKey = subjectKey,
|
|
LastAnalyzedAt = now.AddDays(-7),
|
|
Flags = new UnknownFlags { NoProvenanceAnchor = true },
|
|
CreatedAt = now.AddDays(-10),
|
|
Band = UnknownsBand.Warm
|
|
},
|
|
new UnknownSymbolDocument
|
|
{
|
|
Id = "unknown-3",
|
|
SubjectKey = subjectKey,
|
|
LastAnalyzedAt = now.AddDays(-14),
|
|
Flags = new UnknownFlags { NoProvenanceAnchor = true, VersionRange = true },
|
|
CreatedAt = now.AddDays(-20),
|
|
Band = UnknownsBand.Hot
|
|
}
|
|
};
|
|
|
|
await _unknownsRepo.UpsertAsync(subjectKey, unknowns, CancellationToken.None);
|
|
|
|
var result = await decayService.ApplyDecayAsync(subjectKey, CancellationToken.None);
|
|
|
|
Assert.Equal(3, result.ProcessedCount);
|
|
Assert.Equal(result.HotCount + result.WarmCount + result.ColdCount, result.ProcessedCount);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region RunNightlyDecayBatchAsync Tests
|
|
|
|
[Fact]
|
|
public async Task RunNightlyDecayBatchAsync_ProcessesAllSubjects()
|
|
{
|
|
var (decayService, _) = CreateServices();
|
|
var now = _timeProvider.GetUtcNow();
|
|
|
|
// Create unknowns in multiple subjects
|
|
await _unknownsRepo.UpsertAsync("subject-1|1.0.0", new[]
|
|
{
|
|
new UnknownSymbolDocument
|
|
{
|
|
Id = "u1",
|
|
SubjectKey = "subject-1|1.0.0",
|
|
LastAnalyzedAt = now.AddDays(-7),
|
|
Flags = new UnknownFlags(),
|
|
CreatedAt = now.AddDays(-10)
|
|
}
|
|
}, CancellationToken.None);
|
|
|
|
await _unknownsRepo.UpsertAsync("subject-2|1.0.0", new[]
|
|
{
|
|
new UnknownSymbolDocument
|
|
{
|
|
Id = "u2",
|
|
SubjectKey = "subject-2|1.0.0",
|
|
LastAnalyzedAt = now.AddDays(-3),
|
|
Flags = new UnknownFlags(),
|
|
CreatedAt = now.AddDays(-5)
|
|
}
|
|
}, CancellationToken.None);
|
|
|
|
var result = await decayService.RunNightlyDecayBatchAsync(CancellationToken.None);
|
|
|
|
Assert.Equal(2, result.TotalSubjects);
|
|
Assert.Equal(2, result.TotalUnknowns);
|
|
Assert.True(result.Duration >= TimeSpan.Zero);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RunNightlyDecayBatchAsync_RespectsMaxSubjectsLimit()
|
|
{
|
|
var decayOptions = new UnknownsDecayOptions { MaxSubjectsPerBatch = 1 };
|
|
var scoringService = new UnknownsScoringService(
|
|
_unknownsRepo,
|
|
_deploymentRefs,
|
|
_graphMetrics,
|
|
MsOptions.Options.Create(_scoringOptions),
|
|
_timeProvider,
|
|
NullLogger<UnknownsScoringService>.Instance);
|
|
|
|
var decayService = new UnknownsDecayService(
|
|
_unknownsRepo,
|
|
scoringService,
|
|
MsOptions.Options.Create(_scoringOptions),
|
|
MsOptions.Options.Create(decayOptions),
|
|
_timeProvider,
|
|
NullLogger<UnknownsDecayService>.Instance);
|
|
|
|
var now = _timeProvider.GetUtcNow();
|
|
|
|
// Create unknowns in multiple subjects
|
|
await _unknownsRepo.UpsertAsync("subject-1|1.0.0", new[]
|
|
{
|
|
new UnknownSymbolDocument
|
|
{
|
|
Id = "u1",
|
|
SubjectKey = "subject-1|1.0.0",
|
|
LastAnalyzedAt = now.AddDays(-7),
|
|
Flags = new UnknownFlags(),
|
|
CreatedAt = now.AddDays(-10)
|
|
}
|
|
}, CancellationToken.None);
|
|
|
|
await _unknownsRepo.UpsertAsync("subject-2|1.0.0", new[]
|
|
{
|
|
new UnknownSymbolDocument
|
|
{
|
|
Id = "u2",
|
|
SubjectKey = "subject-2|1.0.0",
|
|
LastAnalyzedAt = now.AddDays(-3),
|
|
Flags = new UnknownFlags(),
|
|
CreatedAt = now.AddDays(-5)
|
|
}
|
|
}, CancellationToken.None);
|
|
|
|
var result = await decayService.RunNightlyDecayBatchAsync(CancellationToken.None);
|
|
|
|
// Should only process 1 subject due to limit
|
|
Assert.Equal(1, result.TotalSubjects);
|
|
Assert.Equal(1, result.TotalUnknowns);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RunNightlyDecayBatchAsync_CancellationRespected()
|
|
{
|
|
var (decayService, _) = CreateServices();
|
|
var now = _timeProvider.GetUtcNow();
|
|
|
|
// Create unknowns in multiple subjects
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
await _unknownsRepo.UpsertAsync($"subject-{i}|1.0.0", new[]
|
|
{
|
|
new UnknownSymbolDocument
|
|
{
|
|
Id = $"u{i}",
|
|
SubjectKey = $"subject-{i}|1.0.0",
|
|
LastAnalyzedAt = now.AddDays(-7),
|
|
Flags = new UnknownFlags(),
|
|
CreatedAt = now.AddDays(-10)
|
|
}
|
|
}, CancellationToken.None);
|
|
}
|
|
|
|
using var cts = new CancellationTokenSource();
|
|
cts.Cancel();
|
|
|
|
await Assert.ThrowsAsync<OperationCanceledException>(() =>
|
|
decayService.RunNightlyDecayBatchAsync(cts.Token));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ApplyDecayToUnknownAsync Tests
|
|
|
|
[Fact]
|
|
public async Task ApplyDecayToUnknownAsync_UpdatesScoringFields()
|
|
{
|
|
var (decayService, _) = CreateServices();
|
|
var now = _timeProvider.GetUtcNow();
|
|
|
|
var unknown = new UnknownSymbolDocument
|
|
{
|
|
Id = "unknown-1",
|
|
SubjectKey = "test|1.0.0",
|
|
Purl = "pkg:npm/test@1.0.0",
|
|
LastAnalyzedAt = now.AddDays(-7),
|
|
Flags = new UnknownFlags { NoProvenanceAnchor = true },
|
|
CreatedAt = now.AddDays(-10),
|
|
Score = 0,
|
|
Band = UnknownsBand.Cold
|
|
};
|
|
|
|
_deploymentRefs.SetDeploymentCount("pkg:npm/test@1.0.0", 50);
|
|
|
|
var result = await decayService.ApplyDecayToUnknownAsync(unknown, CancellationToken.None);
|
|
|
|
// Verify scoring fields were updated
|
|
Assert.True(result.Score > 0);
|
|
Assert.True(result.PopularityScore > 0);
|
|
Assert.True(result.StalenessScore > 0);
|
|
Assert.True(result.UncertaintyScore > 0);
|
|
Assert.NotNull(result.NextScheduledRescan);
|
|
Assert.NotNull(result.NormalizationTrace);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ApplyDecayToUnknownAsync_SetsNextRescanBasedOnBand()
|
|
{
|
|
var (decayService, _) = CreateServices();
|
|
var now = _timeProvider.GetUtcNow();
|
|
|
|
// Create unknown that will be scored as COLD
|
|
var coldUnknown = new UnknownSymbolDocument
|
|
{
|
|
Id = "cold-unknown",
|
|
SubjectKey = "test|1.0.0",
|
|
LastAnalyzedAt = now, // Fresh
|
|
Flags = new UnknownFlags(),
|
|
CreatedAt = now.AddDays(-1)
|
|
};
|
|
|
|
var result = await decayService.ApplyDecayToUnknownAsync(coldUnknown, CancellationToken.None);
|
|
|
|
Assert.Equal(UnknownsBand.Cold, result.Band);
|
|
Assert.Equal(now.AddDays(_scoringOptions.ColdRescanDays), result.NextScheduledRescan);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Decay Result Aggregation Tests
|
|
|
|
[Fact]
|
|
public async Task ApplyDecayAsync_ResultCountsAreAccurate()
|
|
{
|
|
var (decayService, _) = CreateServices();
|
|
var now = _timeProvider.GetUtcNow();
|
|
const string subjectKey = "test|1.0.0";
|
|
|
|
// Create unknowns that will end up in different bands
|
|
var unknowns = new List<UnknownSymbolDocument>();
|
|
|
|
// This will be COLD (fresh, no flags)
|
|
unknowns.Add(new UnknownSymbolDocument
|
|
{
|
|
Id = "cold-1",
|
|
SubjectKey = subjectKey,
|
|
LastAnalyzedAt = now,
|
|
Flags = new UnknownFlags(),
|
|
CreatedAt = now.AddDays(-1)
|
|
});
|
|
|
|
// Add more with varying staleness and flags
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
unknowns.Add(new UnknownSymbolDocument
|
|
{
|
|
Id = $"unknown-{i}",
|
|
SubjectKey = subjectKey,
|
|
LastAnalyzedAt = now.AddDays(-i * 2),
|
|
Flags = new UnknownFlags
|
|
{
|
|
NoProvenanceAnchor = i > 2,
|
|
VersionRange = i > 3
|
|
},
|
|
CreatedAt = now.AddDays(-i * 2 - 5)
|
|
});
|
|
}
|
|
|
|
await _unknownsRepo.UpsertAsync(subjectKey, unknowns, CancellationToken.None);
|
|
|
|
var result = await decayService.ApplyDecayAsync(subjectKey, CancellationToken.None);
|
|
|
|
Assert.Equal(6, result.ProcessedCount);
|
|
Assert.Equal(6, result.HotCount + result.WarmCount + result.ColdCount);
|
|
Assert.True(result.ColdCount >= 1); // At least the fresh one should be cold
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Test Infrastructure
|
|
|
|
private sealed class MockTimeProvider : TimeProvider
|
|
{
|
|
private DateTimeOffset _now;
|
|
|
|
public MockTimeProvider(DateTimeOffset now) => _now = now;
|
|
|
|
public override DateTimeOffset GetUtcNow() => _now;
|
|
|
|
public void Advance(TimeSpan duration) => _now = _now.Add(duration);
|
|
}
|
|
|
|
private sealed class InMemoryUnknownsRepository : IUnknownsRepository
|
|
{
|
|
private readonly List<UnknownSymbolDocument> _stored = new();
|
|
|
|
public Task UpsertAsync(string subjectKey, IEnumerable<UnknownSymbolDocument> items, CancellationToken cancellationToken)
|
|
{
|
|
_stored.RemoveAll(x => x.SubjectKey == subjectKey);
|
|
_stored.AddRange(items);
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task<IReadOnlyList<UnknownSymbolDocument>> GetBySubjectAsync(string subjectKey, CancellationToken cancellationToken)
|
|
{
|
|
return Task.FromResult<IReadOnlyList<UnknownSymbolDocument>>(
|
|
_stored.Where(x => x.SubjectKey == subjectKey).ToList());
|
|
}
|
|
|
|
public Task<int> CountBySubjectAsync(string subjectKey, CancellationToken cancellationToken)
|
|
{
|
|
return Task.FromResult(_stored.Count(x => x.SubjectKey == subjectKey));
|
|
}
|
|
|
|
public Task BulkUpdateAsync(IEnumerable<UnknownSymbolDocument> items, CancellationToken cancellationToken)
|
|
{
|
|
foreach (var item in items)
|
|
{
|
|
var existing = _stored.FindIndex(x => x.Id == item.Id);
|
|
if (existing >= 0)
|
|
_stored[existing] = item;
|
|
else
|
|
_stored.Add(item);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task<IReadOnlyList<string>> GetAllSubjectKeysAsync(CancellationToken cancellationToken)
|
|
{
|
|
return Task.FromResult<IReadOnlyList<string>>(
|
|
_stored.Select(x => x.SubjectKey).Distinct().ToList());
|
|
}
|
|
|
|
public Task<IReadOnlyList<UnknownSymbolDocument>> GetDueForRescanAsync(UnknownsBand band, int limit, CancellationToken cancellationToken)
|
|
{
|
|
return Task.FromResult<IReadOnlyList<UnknownSymbolDocument>>(
|
|
_stored.Where(x => x.Band == band).Take(limit).ToList());
|
|
}
|
|
|
|
public Task<IReadOnlyList<UnknownSymbolDocument>> QueryAsync(UnknownsBand? band, int limit, int offset, CancellationToken cancellationToken)
|
|
{
|
|
var query = _stored.AsEnumerable();
|
|
if (band.HasValue)
|
|
query = query.Where(x => x.Band == band.Value);
|
|
return Task.FromResult<IReadOnlyList<UnknownSymbolDocument>>(
|
|
query.Skip(offset).Take(limit).ToList());
|
|
}
|
|
|
|
public Task<UnknownSymbolDocument?> GetByIdAsync(string id, CancellationToken cancellationToken)
|
|
{
|
|
return Task.FromResult(_stored.FirstOrDefault(x => x.Id == id));
|
|
}
|
|
}
|
|
|
|
private sealed class InMemoryDeploymentRefsRepository : IDeploymentRefsRepository
|
|
{
|
|
private readonly Dictionary<string, int> _counts = new();
|
|
|
|
public void SetDeploymentCount(string purl, int count) => _counts[purl] = count;
|
|
|
|
public Task<int> CountDeploymentsAsync(string purl, CancellationToken cancellationToken)
|
|
{
|
|
return Task.FromResult(_counts.TryGetValue(purl, out var count) ? count : 0);
|
|
}
|
|
|
|
public Task<IReadOnlyList<string>> GetDeploymentIdsAsync(string purl, int limit, CancellationToken cancellationToken)
|
|
{
|
|
return Task.FromResult<IReadOnlyList<string>>(Array.Empty<string>());
|
|
}
|
|
|
|
public Task UpsertAsync(DeploymentRef deployment, CancellationToken cancellationToken) => Task.CompletedTask;
|
|
|
|
public Task BulkUpsertAsync(IEnumerable<DeploymentRef> deployments, CancellationToken cancellationToken) => Task.CompletedTask;
|
|
|
|
public Task<DeploymentSummary?> GetSummaryAsync(string purl, CancellationToken cancellationToken) =>
|
|
Task.FromResult<DeploymentSummary?>(null);
|
|
}
|
|
|
|
private sealed class InMemoryGraphMetricsRepository : IGraphMetricsRepository
|
|
{
|
|
private readonly Dictionary<string, GraphMetrics> _metrics = new();
|
|
|
|
public void SetMetrics(string symbolId, string callgraphId, GraphMetrics metrics)
|
|
{
|
|
_metrics[$"{symbolId}:{callgraphId}"] = metrics;
|
|
}
|
|
|
|
public Task<GraphMetrics?> GetMetricsAsync(string symbolId, string callgraphId, CancellationToken cancellationToken)
|
|
{
|
|
_metrics.TryGetValue($"{symbolId}:{callgraphId}", out var metrics);
|
|
return Task.FromResult(metrics);
|
|
}
|
|
|
|
public Task UpsertAsync(GraphMetrics metrics, CancellationToken cancellationToken) => Task.CompletedTask;
|
|
|
|
public Task BulkUpsertAsync(IEnumerable<GraphMetrics> metrics, CancellationToken cancellationToken) => Task.CompletedTask;
|
|
|
|
public Task<IReadOnlyList<string>> GetStaleCallgraphsAsync(TimeSpan maxAge, int limit, CancellationToken cancellationToken) =>
|
|
Task.FromResult<IReadOnlyList<string>>(Array.Empty<string>());
|
|
|
|
public Task DeleteByCallgraphAsync(string callgraphId, CancellationToken cancellationToken) => Task.CompletedTask;
|
|
}
|
|
|
|
#endregion
|
|
}
|