File: Classification\DataClassificationTypeConverterTests.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.Collections.Generic;
using FluentAssertions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;
 
namespace Microsoft.Extensions.Compliance.Classification.Tests;
 
public class DataClassificationTypeConverterTests
{
    public static IEnumerable<object[]> DefaultDataClassificationTestData()
    {
        yield return new object[] { "None", DataClassification.None };
        yield return new object[] { "Unknown", DataClassification.Unknown };
    }
 
    public static IEnumerable<object[]> CustomDataClassificationTestData()
    {
        yield return new object[] { "Example:Test", new DataClassification("Example", "Test") };
        yield return new object[] { "Taxonomy:Value", new DataClassification("Taxonomy", "Value") };
        yield return new object[] { "Custom:Data", new DataClassification("Custom", "Data") };
    }
 
    [Fact]
    public void BindServiceCollection_ShouldReturnIOptionsWithExpectedDataClassification()
    {
        // Arrange
        var configuration = new ConfigurationBuilder()
            .AddInMemoryCollection(new[]
            {
                new KeyValuePair<string, string?>("Key:Example", "Example:Test"),
                new KeyValuePair<string, string?>("Key:Facts:Value", "Taxonomy:Value"),
                new KeyValuePair<string, string?>("Key:Facts:Data", "Custom:Data"),
                new KeyValuePair<string, string?>("Key:Facts:None", "None"),
                new KeyValuePair<string, string?>("Key:Facts:Unknown", "Unknown"),
                new KeyValuePair<string, string?>("Key:Facts:Invalid", "Invalid"),
            })
            .Build();
 
        IServiceCollection serviceCollection = new ServiceCollection()
            .Configure<TestOptions>(configuration.GetSection("Key"));
 
        // Act
        using var sp = serviceCollection.BuildServiceProvider();
        var options = sp.GetRequiredService<IOptions<TestOptions>>();
 
        var expected = new Dictionary<string, DataClassification>
        {
           { "Value", new DataClassification("Taxonomy", "Value") },
           { "Data", new DataClassification("Custom", "Data") },
           { "None", DataClassification.None },
           { "Unknown", DataClassification.Unknown },
        };
 
        // Assert
        options.Value.Example.Should().NotBeNull().And.Be(new DataClassification("Example", "Test"));
        options.Value.Facts.Should().NotBeEmpty().And.Equal(expected);
 
        // Odd quirk: binding to dictionary succeeds but doesn't include invalid values
        options.Value.Facts.Should().NotContainKey("Invalid");
    }
 
    [Theory]
    [InlineData(typeof(string), true)]
    [InlineData(typeof(int), false)]
    [InlineData(typeof(DataClassification), false)]
    public void CanConvertFrom_ShouldReturnExpectedResult(Type sourceType, bool expected)
    {
        // Arrange
        var converter = new DataClassificationTypeConverter();
 
        // Act
        var result = converter.CanConvertFrom(null, sourceType);
 
        // Assert
        result.Should().Be(expected);
    }
 
    [Theory]
    [InlineData(typeof(DataClassification), true)]
    [InlineData(typeof(int), false)]
    [InlineData(typeof(string), false)]
    public void CanConvertTo_ShouldReturnExpectedResult(Type destinationType, bool expected)
    {
        // Arrange
        var converter = new DataClassificationTypeConverter();
 
        // Act
        var result = converter.CanConvertTo(null, destinationType);
 
        // Assert
        result.Should().Be(expected);
    }
 
    [Theory]
    [InlineData("None", "", "None")]
    [InlineData("Unknown", "", "Unknown")]
    [InlineData("Example:Test", "Example", "Test")]
    public void ConvertFrom_ShouldReturnExpectedResult_ForValidInput(string input, string expectedTaxonomyName, string expectedValue)
    {
        // Arrange
        var converter = new DataClassificationTypeConverter();
 
        // Act
        var result = converter.ConvertFrom(null, null, input);
 
        // Assert
        result.Should().NotBeNull();
        result.Should().BeOfType<DataClassification>();
 
#pragma warning disable CS8605 // Unboxing a possibly null value.
        var dataClassification = (DataClassification)result;
#pragma warning restore CS8605 // Unboxing a possibly null value.
 
        dataClassification.TaxonomyName.Should().Be(expectedTaxonomyName);
        dataClassification.Value.Should().Be(expectedValue);
    }
 
    [Theory]
    [InlineData("InvalidFormat", typeof(FormatException))]
    [InlineData("InvalidFormat:", typeof(FormatException))]
    [InlineData(":InvalidFormat", typeof(FormatException))]
    [InlineData(":", typeof(FormatException))]
    [InlineData("", typeof(FormatException))]
    [InlineData("\t", typeof(FormatException))]
    [InlineData("\n", typeof(FormatException))]
    [InlineData(42, typeof(ArgumentException))]
    [InlineData(false, typeof(ArgumentException))]
    [InlineData(null, typeof(ArgumentException))]
    public void ConvertFrom_ShouldThrowException_ForInvalidInput(object? input, Type expectedException)
    {
        // Arrange
        var converter = new DataClassificationTypeConverter();
 
        // Act
        var act = () => converter.ConvertFrom(null, null, input!);
 
        // Assert
        Assert.Throws(expectedException, act);
    }
 
    [Theory]
    [InlineData("None", true)]
    [InlineData("Unknown", true)]
    [InlineData("Example:Test", true)]
    [InlineData("InvalidFormat", false)]
    [InlineData("InvalidFormat:", false)]
    [InlineData(":InvalidFormat", false)]
    [InlineData(":", false)]
    [InlineData("", false)]
    [InlineData("\t", false)]
    [InlineData("\n", false)]
    [InlineData(42, false)]
    [InlineData(false, false)]
    [InlineData(null, false)]
    public void IsValid_ShouldReturnExpectedResult(object? input, bool expected)
    {
        // Arrange
        var converter = new DataClassificationTypeConverter();
 
        // Act
        var result = converter.IsValid(null, input);
 
        // Assert
        result.Should().Be(expected);
    }
 
    private class TestOptions
    {
#pragma warning disable S3459 // Unassigned members should be removed
#pragma warning disable S1144 // Unused private types or members should be removed
        public DataClassification? Example { get; set; }
#pragma warning restore S1144 // Unused private types or members should be removed
#pragma warning restore S3459 // Unassigned members should be removed
        public IDictionary<string, DataClassification> Facts { get; set; } = new Dictionary<string, DataClassification>();
    }
}