File: Redaction\RedactorTest.cs
Web Access
Project: src\test\Libraries\Microsoft.Extensions.Compliance.Abstractions.Tests\Microsoft.Extensions.Compliance.Abstractions.Tests.csproj (Microsoft.Extensions.Compliance.Abstractions.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Diagnostics.CodeAnalysis;
using Xunit;
 
namespace Microsoft.Extensions.Compliance.Redaction.Test;
 
public static class RedactorTest
{
    [Fact]
    [SuppressMessage("Style", "IDE0004:Remove Unnecessary Cast", Justification = "Cast is required to call extension method.")]
    public static void Redaction_Extensions_Return_Zero_On_Null_Input_Value()
    {
        var r = new PassthroughRedactor();
 
        Assert.Equal(string.Empty, r.Redact((string?)null));
        Assert.Equal(string.Empty, r.Redact<object?>(null));
        Assert.Equal(string.Empty, r.Redact(string.Empty.AsSpan()));
        Assert.Equal(0, r.Redact<string?>(null, new char[0]));
        Assert.Equal(0, r.Redact<object?>(null, new char[0]));
        Assert.True(r.TryRedact<object?>(null, new char[0], out _, string.Empty.AsSpan()));
        Assert.Equal(0, r.GetRedactedLength((string?)null));
    }
 
    [Theory]
    [InlineData(10)]
    [InlineData(100)]
    [InlineData(1000)]
    [InlineData(10000)]
    public static void User_Can_Get_String_From_IRedactor_Using_Extension_Method_With_Different_Input_Length(int length)
    {
        var data = new string('*', length);
 
        Redactor r = NullRedactor.Instance;
 
        string redacted = r.Redact(data);
 
        Assert.Equal(data, redacted);
    }
 
    [Fact]
    public static void Get_Redacted_String_API_Returns_Equivalent_Output_As_Span_Overload()
    {
        var data = new string('3', 3);
        var r = NullRedactor.Instance;
 
        int lengthFromExtension = r.GetRedactedLength(data);
        int length = r.GetRedactedLength(data);
 
        Assert.Equal(lengthFromExtension, length);
    }
 
    [Fact]
    public static void Redact_Extension_String_Span_Works_The_Same_Way_As_Native_Method()
    {
        var data = new string('3', 3);
 
        Span<char> extBuffer = stackalloc char[3];
        Span<char> buffer = stackalloc char[3];
 
        var r = new PassthroughRedactor();
        int extensionWritten = r.Redact(data, extBuffer);
        int written = r.Redact(data, buffer);
 
        Assert.Equal(extensionWritten, written);
        Assert.Equal(extBuffer.ToString(), buffer.ToString());
    }
 
#if NET6_0_OR_GREATER
    [Theory]
    [InlineData(100)]
    [InlineData(1000)]
    public static void SpanFormattable_Format_And_Redacts_Data(int inputSize)
    {
        var data = new string('&', inputSize);
 
        var spanFormattable = new TestSpanFormattable(data);
 
        var r = new PassthroughRedactor();
 
        string redacted = r.Redact(spanFormattable, null, null);
        string redactedDirectly = r.Redact(data);
 
        Assert.Equal(redactedDirectly, redacted);
    }
 
    [Theory]
    [InlineData(100)]
    [InlineData(1000)]
    public static void SpanFormattable_Format_And_Redacts_Data_With_Destination_Buffer(int inputSize)
    {
        var data = new string('^', inputSize);
 
        var spanFormattable = new TestSpanFormattable(data);
 
        var r = new PassthroughRedactor();
 
        var buffer = new char[data.Length];
        var bufferDirect = new char[data.Length];
 
        int redacted = r.Redact(spanFormattable, buffer, null, null);
        int redactedDirectly = r.Redact(data, bufferDirect);
 
        for (int i = 0; i < buffer.Length; i++)
        {
            Assert.Equal(buffer[i], bufferDirect[i]);
        }
    }
#endif
 
    [Fact]
    public static void Formattable_Format_And_Redacts_Data()
    {
        string data = Guid.NewGuid().ToString();
 
        var formattable = new TestFormattable(data);
 
        var r = new PassthroughRedactor();
 
        string redacted = r.Redact(formattable, null, null);
        string redactedDirectly = r.Redact(data);
 
        Assert.Equal(redactedDirectly, redacted);
    }
 
    [Fact]
    public static void Formattable_Format_And_Redacts_Data_With_Destination_Buffer()
    {
        var data = Guid.NewGuid().ToString();
 
        var spanFormattable = new TestFormattable(data);
 
        var r = new PassthroughRedactor();
 
        var buffer = new char[data.Length];
        var bufferDirect = new char[data.Length];
 
        int redacted = r.Redact(spanFormattable, buffer, null, null);
        int redactedDirectly = r.Redact(data, bufferDirect);
 
        for (int i = 0; i < buffer.Length; i++)
        {
            Assert.Equal(buffer[i], bufferDirect[i]);
        }
    }
 
    [Fact]
    public static void Object_Format_And_Redacts_Data()
    {
        var data = Guid.NewGuid().ToString();
 
        var obj = new TestObject(data);
 
        var r = new PassthroughRedactor();
 
        var buffer = new char[data.Length];
        var bufferDirect = new char[data.Length];
 
        string redacted = r.Redact(obj);
        string redactedDirectly = r.Redact(data);
 
        Assert.Equal(redactedDirectly, redacted);
    }
 
    [Fact]
    public static void Object_Format_And_Redacts_Data_With_Destination_Buffer()
    {
        var data = Guid.NewGuid().ToString();
 
        var obj = new TestObject(data);
 
        var r = new PassthroughRedactor();
 
        var buffer = new char[data.Length];
        var bufferDirect = new char[data.Length];
 
        int redacted = r.Redact(obj, buffer);
        int redactedDirectly = r.Redact(data, bufferDirect);
 
        for (int i = 0; i < buffer.Length; i++)
        {
            Assert.Equal(buffer[i], bufferDirect[i]);
        }
    }
 
    [Fact]
    public static void ArrayEmptyOfChar_Redacted_correctly()
    {
        var r = new PassthroughRedactor();
        string redacted = r.Redact(Array.Empty<char>());
 
        Assert.Equal("", redacted);
    }
 
    [Fact]
    public static void ArrayOfChar_Redacted_correctly()
    {
        var r = new PassthroughRedactor();
        string redacted = r.Redact(new char[0]);
 
        Assert.Equal("", redacted);
    }
 
    [Fact]
    public static void ArrayEmptyOfChar_With_Destination_Buffer_Redacted_correctly()
    {
        char[] buffer = new char[5];
 
        var r = new PassthroughRedactor();
        int written = r.Redact(Array.Empty<char>(), buffer);
 
        Assert.Equal(0, written);
 
        foreach (char item in buffer)
        {
            Assert.Equal('\0', item);
        }
    }
 
    [Fact]
    public static void ArrayOfChar_With_Destination_Buffer_Redacted_correctly()
    {
        char[] buffer = new char[5];
 
        var r = new PassthroughRedactor();
        int written = r.Redact(new char[0], buffer);
 
        Assert.Equal(0, written);
 
        foreach (char item in buffer)
        {
            Assert.Equal('\0', item);
        }
    }
 
    [Theory]
    [InlineData(35, false)]
    [InlineData(36, true)]
    [InlineData(37, true)]
    public static void TryRedact_BufferSizes_FOrmattable(int bufferSize, bool success)
    {
        var data = Guid.NewGuid();
        var r = new PassthroughRedactor();
        var buffer = new char[bufferSize];
 
        Assert.Equal(success, r.TryRedact(data, buffer, out int charsWritten, string.Empty.AsSpan(), null));
 
        if (success)
        {
            Assert.Equal(data.ToString(), new string(buffer, 0, charsWritten));
        }
    }
 
    [Theory]
    [InlineData(35, false)]
    [InlineData(36, true)]
    [InlineData(37, true)]
    public static void TryRedact_BufferSizes_NonFormattable(int bufferSize, bool success)
    {
        var data = new NonFormatable();
        var r = new PassthroughRedactor();
        var buffer = new char[bufferSize];
 
        Assert.Equal(success, r.TryRedact(data, buffer, out int charsWritten, string.Empty.AsSpan(), null));
 
        if (success)
        {
            Assert.Equal(data.ToString(), new string(buffer, 0, charsWritten));
        }
    }
 
    [Theory]
    [InlineData(28, false)]
    [InlineData(29, true)]
    [InlineData(30, true)]
    public static void TryRedact_BufferSizes_CustomFormat(int bufferSize, bool success)
    {
        var data = new DateTime(1, 2, 3);
        var r = new PassthroughRedactor();
        var buffer = new char[bufferSize];
 
        Assert.Equal(success, r.TryRedact(data, buffer, out int charsWritten, "R".AsSpan(), null));
 
        if (success)
        {
            Assert.Equal(data.ToString("R"), new string(buffer, 0, charsWritten));
        }
    }
 
    [Fact]
    public static void TryRedact_ArrayEmptyOfChar_With_Destination_Buffer_Redacted_correctly()
    {
        char[] buffer = new char[5];
 
        var r = new PassthroughRedactor();
        Assert.True(r.TryRedact(Array.Empty<char>(), buffer, out int charsWritten, string.Empty.AsSpan(), null));
 
        Assert.Equal(0, charsWritten);
 
        foreach (char item in buffer)
        {
            Assert.Equal('\0', item);
        }
    }
 
    [Fact]
    public static void TryRedact_ArrayOfChar_With_Destination_Buffer_Redacted_correctly()
    {
        char[] buffer = new char[5];
 
        var r = new PassthroughRedactor();
        Assert.True(r.TryRedact(new char[0], buffer, out int charsWritten, string.Empty.AsSpan(), null));
 
        Assert.Equal(0, charsWritten);
 
        foreach (char item in buffer)
        {
            Assert.Equal('\0', item);
        }
    }
 
    private class NonFormatable
    {
        public override string ToString() => "123456789012345678901234567890123456";
    }
 
    private class PassthroughRedactor : Redactor
    {
        public override int GetRedactedLength(ReadOnlySpan<char> input) => input.Length;
 
        public override int Redact(ReadOnlySpan<char> source, Span<char> destination)
        {
            source.CopyTo(destination);
            return source.Length;
        }
    }
}