// // Copyright (c) Stella Operations. Licensed under BUSL-1.1. // using Xunit; namespace StellaOps.Testing.FixtureHarvester.Tests; /// /// Unit tests for OciPinCommand. /// @sprint SPRINT_20251229_004_LIB_fixture_harvester (FH-004) /// public sealed class OciPinCommandTests : IDisposable { private readonly string _testOutputDir; public OciPinCommandTests() { _testOutputDir = Path.Combine(Path.GetTempPath(), $"fixture-harvester-test-{Guid.NewGuid():N}"); Directory.CreateDirectory(_testOutputDir); } public void Dispose() { if (Directory.Exists(_testOutputDir)) { Directory.Delete(_testOutputDir, recursive: true); } } [Fact] public void ParseImageRef_SimpleImage_ReturnsDockerHub() { // Arrange & Act var result = ParseImageRefTestHelper("alpine:3.19"); // Assert Assert.Equal("docker.io", result.Registry); Assert.Equal("library/alpine", result.Repository); Assert.Equal("3.19", result.Tag); } [Fact] public void ParseImageRef_ImageWithoutTag_DefaultsToLatest() { // Arrange & Act var result = ParseImageRefTestHelper("nginx"); // Assert Assert.Equal("docker.io", result.Registry); Assert.Equal("library/nginx", result.Repository); Assert.Equal("latest", result.Tag); } [Fact] public void ParseImageRef_NamespacedImage_ParsesCorrectly() { // Arrange & Act var result = ParseImageRefTestHelper("myuser/myapp:v1.0.0"); // Assert Assert.Equal("docker.io", result.Registry); Assert.Equal("myuser/myapp", result.Repository); Assert.Equal("v1.0.0", result.Tag); } [Fact] public void ParseImageRef_CustomRegistry_ParsesCorrectly() { // Arrange & Act var result = ParseImageRefTestHelper("ghcr.io/owner/repo:latest"); // Assert Assert.Equal("ghcr.io", result.Registry); Assert.Equal("owner/repo", result.Repository); Assert.Equal("latest", result.Tag); } [Fact] public void ParseImageRef_RegistryWithPort_ParsesCorrectly() { // Arrange & Act var result = ParseImageRefTestHelper("localhost:5000/myimage:test"); // Assert Assert.Equal("localhost:5000", result.Registry); Assert.Equal("myimage", result.Repository); Assert.Equal("test", result.Tag); } [Fact] public void ParseImageRef_GcrImage_ParsesCorrectly() { // Arrange & Act var result = ParseImageRefTestHelper("gcr.io/distroless/static-debian12:nonroot"); // Assert Assert.Equal("gcr.io", result.Registry); Assert.Equal("distroless/static-debian12", result.Repository); Assert.Equal("nonroot", result.Tag); } [Fact] public void SanitizeId_RemovesInvalidCharacters() { // Arrange var input = "oci-my/image:v1.0+build"; // Act var result = SanitizeIdTestHelper(input); // Assert Assert.DoesNotContain("/", result); Assert.DoesNotContain(":", result); Assert.DoesNotContain("+", result); Assert.Contains("oci", result); Assert.Contains("image", result); } [Fact] public void SanitizeId_PreservesValidCharacters() { // Arrange var input = "valid-id_123"; // Act var result = SanitizeIdTestHelper(input); // Assert Assert.Equal("valid-id_123", result); } // Helper methods that mirror the internal logic for testing private static (string Registry, string Repository, string Tag) ParseImageRefTestHelper(string imageRef) { var tag = "latest"; var repository = imageRef; var registry = "docker.io"; // Extract tag var tagIndex = imageRef.LastIndexOf(':'); var slashAfterTag = tagIndex > 0 ? imageRef.IndexOf('/', tagIndex) : -1; if (tagIndex > 0 && slashAfterTag < 0 && !imageRef.Substring(tagIndex + 1).Contains('/')) { tag = imageRef.Substring(tagIndex + 1); repository = imageRef.Substring(0, tagIndex); } // Extract registry var firstSlash = repository.IndexOf('/'); if (firstSlash > 0) { var possibleRegistry = repository.Substring(0, firstSlash); if (possibleRegistry.Contains('.') || possibleRegistry.Contains(':') || possibleRegistry == "localhost") { registry = possibleRegistry; repository = repository.Substring(firstSlash + 1); } } // Handle Docker Hub library images if (registry == "docker.io" && !repository.Contains('/')) { repository = $"library/{repository}"; } return (registry, repository, tag); } private static string SanitizeIdTestHelper(string input) { return new string(input.Where(c => char.IsLetterOrDigit(c) || c == '-' || c == '_').ToArray()); } }