save checkpoint: save features
This commit is contained in:
105
src/Tools/__Tests/FixtureUpdater.Tests/FixtureUpdaterAppTests.cs
Normal file
105
src/Tools/__Tests/FixtureUpdater.Tests/FixtureUpdaterAppTests.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using StellaOps.Tools.FixtureUpdater;
|
||||
|
||||
public sealed class FixtureUpdaterAppTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task RunAsync_ReturnsZero_WithValidExplicitPaths()
|
||||
{
|
||||
var repoRoot = FindRepoRoot();
|
||||
using var temp = new TempDirectory();
|
||||
|
||||
var osvDir = Path.Combine(temp.Path, "osv");
|
||||
var ghsaDir = Path.Combine(temp.Path, "ghsa");
|
||||
var nvdDir = Path.Combine(temp.Path, "nvd");
|
||||
Directory.CreateDirectory(osvDir);
|
||||
Directory.CreateDirectory(ghsaDir);
|
||||
Directory.CreateDirectory(nvdDir);
|
||||
|
||||
File.Copy(
|
||||
Path.Combine(repoRoot, "src", "Concelier", "__Tests", "StellaOps.Concelier.Connector.Osv.Tests", "Fixtures", "osv-ghsa.raw-osv.json"),
|
||||
Path.Combine(osvDir, "osv-ghsa.raw-osv.json"));
|
||||
File.Copy(
|
||||
Path.Combine(repoRoot, "src", "Concelier", "__Tests", "StellaOps.Concelier.Connector.Ghsa.Tests", "Fixtures", "osv-ghsa.raw-ghsa.json"),
|
||||
Path.Combine(ghsaDir, "osv-ghsa.raw-ghsa.json"));
|
||||
|
||||
var exitCode = await FixtureUpdaterApp.RunAsync(new[]
|
||||
{
|
||||
"--repo-root", repoRoot,
|
||||
"--osv-fixtures", osvDir,
|
||||
"--ghsa-fixtures", ghsaDir,
|
||||
"--nvd-fixtures", nvdDir,
|
||||
"--fixed-time", "2025-01-01T00:00:00Z"
|
||||
});
|
||||
|
||||
Assert.Equal(0, exitCode);
|
||||
Assert.True(File.Exists(Path.Combine(ghsaDir, "osv-ghsa.ghsa.json")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RunAsync_ReturnsNonZero_WhenFixedTimeIsInvalid()
|
||||
{
|
||||
var repoRoot = FindRepoRoot();
|
||||
using var temp = new TempDirectory();
|
||||
|
||||
var osvDir = Path.Combine(temp.Path, "osv");
|
||||
var ghsaDir = Path.Combine(temp.Path, "ghsa");
|
||||
var nvdDir = Path.Combine(temp.Path, "nvd");
|
||||
Directory.CreateDirectory(osvDir);
|
||||
Directory.CreateDirectory(ghsaDir);
|
||||
Directory.CreateDirectory(nvdDir);
|
||||
|
||||
File.Copy(
|
||||
Path.Combine(repoRoot, "src", "Concelier", "__Tests", "StellaOps.Concelier.Connector.Osv.Tests", "Fixtures", "osv-ghsa.raw-osv.json"),
|
||||
Path.Combine(osvDir, "osv-ghsa.raw-osv.json"));
|
||||
File.Copy(
|
||||
Path.Combine(repoRoot, "src", "Concelier", "__Tests", "StellaOps.Concelier.Connector.Ghsa.Tests", "Fixtures", "osv-ghsa.raw-ghsa.json"),
|
||||
Path.Combine(ghsaDir, "osv-ghsa.raw-ghsa.json"));
|
||||
|
||||
var exitCode = await FixtureUpdaterApp.RunAsync(new[]
|
||||
{
|
||||
"--repo-root", repoRoot,
|
||||
"--osv-fixtures", osvDir,
|
||||
"--ghsa-fixtures", ghsaDir,
|
||||
"--nvd-fixtures", nvdDir,
|
||||
"--fixed-time", "not-a-date"
|
||||
});
|
||||
|
||||
Assert.NotEqual(0, exitCode);
|
||||
}
|
||||
|
||||
private static string FindRepoRoot()
|
||||
{
|
||||
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (current is not null)
|
||||
{
|
||||
var solution = Path.Combine(current.FullName, "src", "StellaOps.sln");
|
||||
if (File.Exists(solution))
|
||||
{
|
||||
return current.FullName;
|
||||
}
|
||||
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Repository root not found.");
|
||||
}
|
||||
|
||||
private sealed class TempDirectory : IDisposable
|
||||
{
|
||||
public TempDirectory()
|
||||
{
|
||||
Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"fixture-updater-app-{Guid.NewGuid():N}");
|
||||
Directory.CreateDirectory(Path);
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Directory.Exists(Path))
|
||||
{
|
||||
Directory.Delete(Path, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Collections.Immutable;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Tools.GoldenPairs;
|
||||
using StellaOps.Tools.GoldenPairs.Models;
|
||||
using StellaOps.Tools.GoldenPairs.Serialization;
|
||||
|
||||
namespace StellaOps.Tools.GoldenPairs.Tests;
|
||||
|
||||
public sealed class GoldenPairsAppCliTests
|
||||
{
|
||||
[Fact]
|
||||
public void RunAsync_MirrorDiffValidate_Succeeds_ForLocalDataset()
|
||||
{
|
||||
var repoRoot = FindRepoRoot();
|
||||
using var temp = new TempDirectory();
|
||||
|
||||
var datasetRoot = Path.Combine(temp.Path, "dataset");
|
||||
var pairDir = Path.Combine(datasetRoot, "CVE-2099-0001");
|
||||
var originalDir = Path.Combine(pairDir, "original");
|
||||
var patchedDir = Path.Combine(pairDir, "patched");
|
||||
var sourceDir = Path.Combine(temp.Path, "source");
|
||||
|
||||
Directory.CreateDirectory(originalDir);
|
||||
Directory.CreateDirectory(patchedDir);
|
||||
Directory.CreateDirectory(sourceDir);
|
||||
|
||||
var originalSourcePath = Path.Combine(sourceDir, "original.bin");
|
||||
var patchedSourcePath = Path.Combine(sourceDir, "patched.bin");
|
||||
File.WriteAllText(originalSourcePath, "ORIGINAL-BINARY-DATA-001");
|
||||
File.WriteAllText(patchedSourcePath, "PATCHED-BINARY-DATA-002");
|
||||
|
||||
var metadata = CreateMetadata(originalSourcePath, patchedSourcePath);
|
||||
var index = CreateIndex();
|
||||
|
||||
File.WriteAllText(Path.Combine(pairDir, "metadata.json"), GoldenPairsJsonSerializer.Serialize(metadata));
|
||||
File.WriteAllText(Path.Combine(datasetRoot, "index.json"), GoldenPairsJsonSerializer.Serialize(index));
|
||||
|
||||
var originalSections = TestData.CreateSectionHashSet(
|
||||
filePath: "original/sample.bin",
|
||||
fileHash: metadata.Original.Sha256,
|
||||
new SectionHashEntry { Name = ".text", Sha256 = "1111", Size = 100 },
|
||||
new SectionHashEntry { Name = ".rodata", Sha256 = "2222", Size = 50 },
|
||||
new SectionHashEntry { Name = ".data", Sha256 = "4444", Size = 30 });
|
||||
|
||||
var patchedSections = TestData.CreateSectionHashSet(
|
||||
filePath: "patched/sample.bin",
|
||||
fileHash: metadata.Patched.Sha256,
|
||||
new SectionHashEntry { Name = ".text", Sha256 = "3333", Size = 110 },
|
||||
new SectionHashEntry { Name = ".rodata", Sha256 = "2222", Size = 50 },
|
||||
new SectionHashEntry { Name = ".data", Sha256 = "4444", Size = 30 });
|
||||
|
||||
File.WriteAllText(Path.Combine(originalDir, "sample.bin.sections.json"), GoldenPairsJsonSerializer.Serialize(originalSections));
|
||||
File.WriteAllText(Path.Combine(patchedDir, "sample.bin.sections.json"), GoldenPairsJsonSerializer.Serialize(patchedSections));
|
||||
|
||||
var mirrorExitCode = GoldenPairsApp.RunAsync(new[]
|
||||
{
|
||||
"--repo-root", repoRoot,
|
||||
"--dataset-root", datasetRoot,
|
||||
"mirror", "CVE-2099-0001"
|
||||
});
|
||||
mirrorExitCode.Should().Be(0);
|
||||
|
||||
File.Exists(Path.Combine(originalDir, "sample.bin")).Should().BeTrue();
|
||||
File.Exists(Path.Combine(patchedDir, "sample.bin")).Should().BeTrue();
|
||||
|
||||
var diffExitCode = GoldenPairsApp.RunAsync(new[]
|
||||
{
|
||||
"--repo-root", repoRoot,
|
||||
"--dataset-root", datasetRoot,
|
||||
"diff", "CVE-2099-0001",
|
||||
"--output", "table"
|
||||
});
|
||||
diffExitCode.Should().Be(0);
|
||||
File.Exists(Path.Combine(pairDir, "diff-report.json")).Should().BeTrue();
|
||||
|
||||
var validateExitCode = GoldenPairsApp.RunAsync(new[]
|
||||
{
|
||||
"--repo-root", repoRoot,
|
||||
"--dataset-root", datasetRoot,
|
||||
"validate"
|
||||
});
|
||||
validateExitCode.Should().Be(0);
|
||||
|
||||
var missingCveExitCode = GoldenPairsApp.RunAsync(new[]
|
||||
{
|
||||
"--repo-root", repoRoot,
|
||||
"--dataset-root", datasetRoot,
|
||||
"diff", "CVE-2099-9999",
|
||||
"--output", "table"
|
||||
});
|
||||
missingCveExitCode.Should().NotBe(0);
|
||||
}
|
||||
|
||||
private static GoldenPairMetadata CreateMetadata(string originalSourcePath, string patchedSourcePath)
|
||||
{
|
||||
return new GoldenPairMetadata
|
||||
{
|
||||
Cve = "CVE-2099-0001",
|
||||
Name = "Synthetic CLI Diff Pair",
|
||||
Description = "Deterministic local golden pair for CLI tests.",
|
||||
Severity = SeverityLevel.High,
|
||||
Artifact = new ArtifactInfo
|
||||
{
|
||||
Name = "sample.bin",
|
||||
Format = BinaryFormat.Elf,
|
||||
Architecture = "x86_64",
|
||||
Os = "linux"
|
||||
},
|
||||
Original = new BinaryArtifact
|
||||
{
|
||||
Package = "sample-pkg",
|
||||
Version = "1.0.0",
|
||||
Distro = "local",
|
||||
Source = ToFileUri(originalSourcePath),
|
||||
Sha256 = ComputeSha256(originalSourcePath),
|
||||
BuildId = "orig-build",
|
||||
HasDebugSymbols = false
|
||||
},
|
||||
Patched = new BinaryArtifact
|
||||
{
|
||||
Package = "sample-pkg",
|
||||
Version = "1.0.1",
|
||||
Distro = "local",
|
||||
Source = ToFileUri(patchedSourcePath),
|
||||
Sha256 = ComputeSha256(patchedSourcePath),
|
||||
BuildId = "pat-build",
|
||||
HasDebugSymbols = false
|
||||
},
|
||||
Patch = new PatchInfo
|
||||
{
|
||||
Commit = "synthetic-commit",
|
||||
Upstream = "https://example.invalid/synthetic",
|
||||
FunctionsChanged = ImmutableArray.Create("f1", "f2"),
|
||||
FilesChanged = ImmutableArray.Create("a.c", "b.c"),
|
||||
Summary = "Synthetic diff fixture for CLI tests."
|
||||
},
|
||||
Advisories = ImmutableArray.Create(
|
||||
new AdvisoryRef
|
||||
{
|
||||
Source = "local",
|
||||
Id = "LOCAL-2099-0001",
|
||||
Url = "https://example.invalid/advisory"
|
||||
}
|
||||
),
|
||||
ExpectedDiff = new ExpectedDiff
|
||||
{
|
||||
SectionsChanged = ImmutableArray.Create(".text"),
|
||||
SectionsIdentical = ImmutableArray.Create(".rodata", ".data"),
|
||||
Verdict = GoldenDiffVerdict.Patched,
|
||||
ConfidenceMin = 0.9
|
||||
},
|
||||
CreatedAt = new DateTimeOffset(2026, 2, 11, 0, 0, 0, TimeSpan.Zero),
|
||||
CreatedBy = "QA Tier2 CLI Tests"
|
||||
};
|
||||
}
|
||||
|
||||
private static GoldenPairsIndex CreateIndex()
|
||||
{
|
||||
return new GoldenPairsIndex
|
||||
{
|
||||
Version = "1.0.0",
|
||||
GeneratedAt = new DateTimeOffset(2026, 2, 11, 0, 0, 0, TimeSpan.Zero),
|
||||
Pairs = ImmutableArray.Create(
|
||||
new GoldenPairSummary
|
||||
{
|
||||
Cve = "CVE-2099-0001",
|
||||
Name = "Synthetic CLI Diff Pair",
|
||||
Severity = SeverityLevel.High,
|
||||
Format = BinaryFormat.Elf,
|
||||
Status = GoldenPairStatus.Pending,
|
||||
Path = "CVE-2099-0001"
|
||||
}
|
||||
),
|
||||
Summary = new GoldenPairsIndexSummary
|
||||
{
|
||||
Total = 1,
|
||||
Validated = 0,
|
||||
Failed = 0,
|
||||
Pending = 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static string ToFileUri(string path)
|
||||
=> $"file:///{path.Replace('\\', '/')}";
|
||||
|
||||
private static string ComputeSha256(string path)
|
||||
{
|
||||
using var stream = File.OpenRead(path);
|
||||
using var sha = SHA256.Create();
|
||||
var hash = sha.ComputeHash(stream);
|
||||
return string.Concat(hash.Select(value => value.ToString("x2", System.Globalization.CultureInfo.InvariantCulture)));
|
||||
}
|
||||
|
||||
private static string FindRepoRoot()
|
||||
{
|
||||
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (current is not null)
|
||||
{
|
||||
var solution = Path.Combine(current.FullName, "src", "StellaOps.sln");
|
||||
if (File.Exists(solution))
|
||||
{
|
||||
return current.FullName;
|
||||
}
|
||||
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Repository root not found.");
|
||||
}
|
||||
|
||||
private sealed class TempDirectory : IDisposable
|
||||
{
|
||||
public TempDirectory()
|
||||
{
|
||||
Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"golden-pairs-app-{Guid.NewGuid():N}");
|
||||
Directory.CreateDirectory(Path);
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Directory.Exists(Path))
|
||||
{
|
||||
Directory.Delete(Path, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user