//
// 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());
}
}