From de069a3824abc063bef8f95032f3702b7bc76f29 Mon Sep 17 00:00:00 2001 From: JT <975824+Hawxy@users.noreply.github.com> Date: Mon, 6 Jan 2025 21:24:18 +0800 Subject: [PATCH] Convert test suite to TUnit (#17) * Fix build * Update to xunit v3 * Install .NET 8 as well. * . * Correct output type * Remove assertion and retest * . * Test standalone * Test adjustment to serializer * Convert test suite to TUnit --------- Co-authored-by: JT --- .github/workflows/Build_&_Test.yml | 8 +- .github/workflows/Manual_Nuget_Push.yml | 8 +- Package.Build.props | 2 +- build/Build.SetupDotNet.cs | 58 ++++++ build/Build.cs | 20 +-- build/_build.csproj | 4 +- tests/Fga.Net.Tests/Client/EndpointTests.cs | 103 ++--------- .../Client/EndpointWebAppFixture.cs | 13 +- tests/Fga.Net.Tests/Fga.Net.Tests.csproj | 19 +- .../Middleware/MiddlewareTests.cs | 24 +-- .../Fga.Net.Tests/Middleware/WebAppFixture.cs | 23 +-- tests/Fga.Net.Tests/TheoryData.cs | 25 --- tests/Fga.Net.Tests/Unit/AttributeTests.cs | 51 +++--- tests/Fga.Net.Tests/Unit/ExtensionScenario.cs | 22 +-- tests/Fga.Net.Tests/Unit/ExtensionTests.cs | 168 +++++++++--------- tests/Fga.Net.Tests/Unit/ValidationTests.cs | 18 +- 16 files changed, 244 insertions(+), 322 deletions(-) create mode 100644 build/Build.SetupDotNet.cs delete mode 100644 tests/Fga.Net.Tests/TheoryData.cs diff --git a/.github/workflows/Build_&_Test.yml b/.github/workflows/Build_&_Test.yml index 256c10b..ecd73fa 100644 --- a/.github/workflows/Build_&_Test.yml +++ b/.github/workflows/Build_&_Test.yml @@ -5,7 +5,7 @@ # # - To turn off auto-generation set: # -# [GitHubActions (AutoGenerate = false)] +# [GithubActionsExtended (AutoGenerate = false)] # # - To trigger manual generation invoke: # @@ -29,10 +29,12 @@ jobs: name: ubuntu-latest runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v4 with: - dotnet-version: '9' + dotnet-version: | + 8.0 + 9.0 + - uses: actions/checkout@v4 - name: 'Cache: .nuke/temp, ~/.nuget/packages' uses: actions/cache@v4 with: diff --git a/.github/workflows/Manual_Nuget_Push.yml b/.github/workflows/Manual_Nuget_Push.yml index 4299d2a..7878728 100644 --- a/.github/workflows/Manual_Nuget_Push.yml +++ b/.github/workflows/Manual_Nuget_Push.yml @@ -5,7 +5,7 @@ # # - To turn off auto-generation set: # -# [GitHubActions (AutoGenerate = false)] +# [GithubActionsExtended (AutoGenerate = false)] # # - To trigger manual generation invoke: # @@ -23,10 +23,12 @@ jobs: name: ubuntu-latest runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v4 with: - dotnet-version: '9' + dotnet-version: | + 8.0 + 9.0 + - uses: actions/checkout@v4 - name: 'Cache: .nuke/temp, ~/.nuget/packages' uses: actions/cache@v4 with: diff --git a/Package.Build.props b/Package.Build.props index f24381a..722fc48 100644 --- a/Package.Build.props +++ b/Package.Build.props @@ -7,7 +7,7 @@ https://github.com/Hawxy/Fga.Net https://github.com/Hawxy/Fga.Net git - Hawxy 2022-2024 + Hawxy 2022-2025 true README.md diff --git a/build/Build.SetupDotNet.cs b/build/Build.SetupDotNet.cs new file mode 100644 index 0000000..e53f090 --- /dev/null +++ b/build/Build.SetupDotNet.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using Nuke.Common.CI.GitHubActions; +using Nuke.Common.CI.GitHubActions.Configuration; +using Nuke.Common.Execution; +using Nuke.Common.Utilities; + +public class GitHubActionsSetupDotNetStep : GitHubActionsStep +{ + public GitHubActionsSetupDotNetStep(string[] versions) + { + Versions = versions; + } + + string[] Versions { get; } + + public override void Write(CustomFileWriter writer) + { + writer.WriteLine("- uses: actions/setup-dotnet@v4"); + + using (writer.Indent()) + { + writer.WriteLine("with:"); + using (writer.Indent()) + { + writer.WriteLine("dotnet-version: |"); + using (writer.Indent()) + { + foreach (var version in Versions) + { + writer.WriteLine(version); + } + } + } + } + } +} + +public class GithubActionsExtendedAttribute : GitHubActionsAttribute +{ + public GithubActionsExtendedAttribute(string name, GitHubActionsImage image, params GitHubActionsImage[] images) : base(name, image, images) + { + } + + protected override GitHubActionsJob GetJobs(GitHubActionsImage image, + IReadOnlyCollection relevantTargets) + { + var job = base.GetJobs(image, relevantTargets); + + var newSteps = new List(job.Steps); + newSteps.Insert(0, new GitHubActionsSetupDotNetStep([ + "8.0", "9.0" + ])); + + job.Steps = newSteps.ToArray(); + + return job; + } +} \ No newline at end of file diff --git a/build/Build.cs b/build/Build.cs index 977000b..70be675 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -10,21 +10,19 @@ using static Nuke.Common.Tools.DotNet.DotNetTasks; [ShutdownDotNetAfterServerBuild] -[GitHubActions( +[GithubActionsExtended( "Build & Test", GitHubActionsImage.UbuntuLatest, - AutoGenerate = false, - OnPushBranches = new []{ "main" }, - OnPullRequestBranches = new []{ "main" }, - InvokedTargets = new[] { nameof(Test) }, - ImportSecrets = new []{ nameof(FgaStoreId), nameof(FgaClientId), nameof(FgaClientSecret) })] -[GitHubActions( + OnPushBranches = ["main"], + OnPullRequestBranches = ["main"], + InvokedTargets = [nameof(Test)], + ImportSecrets = [nameof(FgaStoreId), nameof(FgaClientId), nameof(FgaClientSecret)])] +[GithubActionsExtended( "Manual Nuget Push", GitHubActionsImage.UbuntuLatest, - AutoGenerate = false, - On = new[] { GitHubActionsTrigger.WorkflowDispatch }, - InvokedTargets = new[] { nameof(NugetPush) }, - ImportSecrets = new[] { nameof(NugetApiKey) })] + On = [GitHubActionsTrigger.WorkflowDispatch], + InvokedTargets = [nameof(NugetPush)], + ImportSecrets = [nameof(NugetApiKey)])] class Build : NukeBuild { /// Support plugins are available for: diff --git a/build/_build.csproj b/build/_build.csproj index 446c9fd..96c9f80 100644 --- a/build/_build.csproj +++ b/build/_build.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 CS0649;CS0169 .. @@ -11,7 +11,7 @@ - + diff --git a/tests/Fga.Net.Tests/Client/EndpointTests.cs b/tests/Fga.Net.Tests/Client/EndpointTests.cs index ebe6384..6434cff 100644 --- a/tests/Fga.Net.Tests/Client/EndpointTests.cs +++ b/tests/Fga.Net.Tests/Client/EndpointTests.cs @@ -1,111 +1,38 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Alba; -using Fga.Net.DependencyInjection.Configuration; +using Fga.Net.DependencyInjection.Configuration; +using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using OpenFga.Sdk.Api; using OpenFga.Sdk.Client; -using OpenFga.Sdk.Client.Model; -using OpenFga.Sdk.Model; -using Xunit; namespace Fga.Net.Tests.Client; -[Collection(nameof(EndpointWebAppCollection))] -public class EndpointTests +[ClassDataSource(Shared = SharedType.PerAssembly)] +public class EndpointTests(EndpointWebAppFixture fixture) : EndpointWebAppBase(fixture) { - private readonly IAlbaHost _host; - - public EndpointTests(EndpointWebAppFixture fixture) - { - _host = fixture.AlbaHost; - } - - [Fact] + [Test] private async Task GetEndpoints_OpenFgaApi_Return_200() { - using var scope = _host.Services.CreateScope(); + using var scope = Host.Services.CreateScope(); var client = scope.ServiceProvider.GetRequiredService(); var config = scope.ServiceProvider.GetRequiredService>().Value; var modelsResponse = await client.ReadAuthorizationModels(config.StoreId!); - - Assert.NotNull(modelsResponse); - Assert.NotNull(modelsResponse.AuthorizationModels); - Assert.True(modelsResponse.AuthorizationModels?.Count > 0); - - var modelId = modelsResponse.AuthorizationModels?.First().Id!; - - var modelResponse = await client.ReadAuthorizationModel(config.StoreId!, modelId); - - Assert.NotNull(modelResponse); - Assert.NotNull(modelResponse.AuthorizationModel?.Id); - - var assertions = await client.ReadAssertions(config.StoreId!, modelId); - - Assert.NotNull(assertions); - Assert.True(assertions.Assertions?.Count > 0); - var assertion = assertions.Assertions!.First().TupleKey; - - Assert.NotEmpty(assertion!.Object!); - Assert.NotEmpty(assertion.Relation!); - Assert.NotEmpty(assertion.User!); - - var graph = await client.Expand(config.StoreId!, new ExpandRequest() - { - AuthorizationModelId = modelId, - TupleKey = new ExpandRequestTupleKey(assertion.Relation, assertion.Object) - }); - - Assert.NotNull(graph.Tree); - Assert.NotNull(graph.Tree!.Root!.Name); - - var watch = await client.ReadChanges(config.StoreId!); - Assert.NotNull(watch); - - + + modelsResponse.Should().NotBeNull(); + modelsResponse.AuthorizationModels.Should().NotBeNull(); + modelsResponse.AuthorizationModels.Count.Should().BePositive(); } - [Fact] + [Test] private async Task GetEndpoints_OpenFgaClient_Return_200() { - using var scope = _host.Services.CreateScope(); + using var scope = Host.Services.CreateScope(); var client = scope.ServiceProvider.GetRequiredService(); var modelsResponse = await client.ReadAuthorizationModels(); - Assert.NotNull(modelsResponse); - Assert.NotNull(modelsResponse.AuthorizationModels); - Assert.True(modelsResponse.AuthorizationModels?.Count > 0); - - var modelId = modelsResponse.AuthorizationModels?.First().Id!; - - var modelResponse = await client.ReadAuthorizationModel(new ClientReadAuthorizationModelOptions() {AuthorizationModelId = modelId}); - - Assert.NotNull(modelResponse); - Assert.NotNull(modelResponse.AuthorizationModel?.Id); - - var assertions = await client.ReadAssertions(new ClientReadAssertionsOptions() { AuthorizationModelId = modelId}); - - Assert.NotNull(assertions); - Assert.True(assertions.Assertions?.Count > 0); - var assertion = assertions.Assertions!.First().TupleKey; - - Assert.NotEmpty(assertion!.Object!); - Assert.NotEmpty(assertion.Relation!); - Assert.NotEmpty(assertion.User!); - - var graph = await client.Expand(new ClientExpandRequest() - { - Object = assertion.Object!, - Relation = assertion.Relation! - }); - - Assert.NotNull(graph.Tree); - Assert.NotNull(graph.Tree!.Root!.Name); - - var watch = await client.ReadChanges(new ClientReadChangesRequest() {Type = "document"}); - Assert.NotNull(watch); + modelsResponse.Should().NotBeNull(); + modelsResponse.AuthorizationModels.Should().NotBeNull(); + modelsResponse.AuthorizationModels.Count.Should().BePositive(); } } \ No newline at end of file diff --git a/tests/Fga.Net.Tests/Client/EndpointWebAppFixture.cs b/tests/Fga.Net.Tests/Client/EndpointWebAppFixture.cs index e23a0ca..0e6d644 100644 --- a/tests/Fga.Net.Tests/Client/EndpointWebAppFixture.cs +++ b/tests/Fga.Net.Tests/Client/EndpointWebAppFixture.cs @@ -1,11 +1,10 @@ -using System.Threading.Tasks; -using Alba; +using Alba; using Fga.Net.Tests.Middleware; -using Xunit; +using TUnit.Core.Interfaces; namespace Fga.Net.Tests.Client; -public class EndpointWebAppFixture : IAsyncLifetime +public class EndpointWebAppFixture : IAsyncInitializer, IAsyncDisposable { public IAlbaHost AlbaHost = null!; @@ -14,13 +13,13 @@ public async Task InitializeAsync() AlbaHost = await Alba.AlbaHost.For(_ => { }, MockJwtConfiguration.GetDefaultStubConfiguration()); } - public async Task DisposeAsync() + public async ValueTask DisposeAsync() { await AlbaHost.DisposeAsync(); } } -[CollectionDefinition(nameof(EndpointWebAppCollection))] -public class EndpointWebAppCollection : ICollectionFixture +public abstract class EndpointWebAppBase(EndpointWebAppFixture fixture) { + protected IAlbaHost Host => fixture.AlbaHost; } \ No newline at end of file diff --git a/tests/Fga.Net.Tests/Fga.Net.Tests.csproj b/tests/Fga.Net.Tests/Fga.Net.Tests.csproj index 3cc8775..3bc9ec0 100644 --- a/tests/Fga.Net.Tests/Fga.Net.Tests.csproj +++ b/tests/Fga.Net.Tests/Fga.Net.Tests.csproj @@ -1,26 +1,19 @@  - net8.0 + net8.0;net9.0 enable - + enable + Exe false - - + + - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - + diff --git a/tests/Fga.Net.Tests/Middleware/MiddlewareTests.cs b/tests/Fga.Net.Tests/Middleware/MiddlewareTests.cs index 510390a..e4ef112 100644 --- a/tests/Fga.Net.Tests/Middleware/MiddlewareTests.cs +++ b/tests/Fga.Net.Tests/Middleware/MiddlewareTests.cs @@ -1,34 +1,26 @@ -using System; -using System.Net; +using System.Net; using System.Security.Claims; -using System.Threading.Tasks; using Alba; -using Xunit; namespace Fga.Net.Tests.Middleware; -[Collection(nameof(WebAppCollection))] -public class MiddlewareTests +[ClassDataSource(Shared = SharedType.PerAssembly)] +public class MiddlewareTests(WebAppFixture fixture) : WebAppBase(fixture) { - private readonly IAlbaHost _alba; - - public MiddlewareTests(WebAppFixture fixture) - { - _alba = fixture.AlbaHost; - } - [Fact] + [Test] public async Task Authorization_HappyPath_Succeeds() { - await _alba.Scenario(_ => + await Host.Scenario(_ => { _.Get.Url($"/test/{Guid.NewGuid()}"); _.StatusCodeShouldBeOk(); }); } - [Fact] + + [Test] public async Task Authorization_UnhappyPath_Forbidden() { - await _alba.Scenario(_ => + await Host.Scenario(_ => { _.RemoveClaim(ClaimTypes.NameIdentifier); _.WithClaim(new Claim(ClaimTypes.NameIdentifier, MockJwtConfiguration.FakeUser)); diff --git a/tests/Fga.Net.Tests/Middleware/WebAppFixture.cs b/tests/Fga.Net.Tests/Middleware/WebAppFixture.cs index eaaa0a0..a0f3c92 100644 --- a/tests/Fga.Net.Tests/Middleware/WebAppFixture.cs +++ b/tests/Fga.Net.Tests/Middleware/WebAppFixture.cs @@ -1,19 +1,14 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Alba; +using Alba; using Fga.Net.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Moq; using OpenFga.Sdk.Client.Model; -using OpenFga.Sdk.Model; -using Xunit; +using TUnit.Core.Interfaces; namespace Fga.Net.Tests.Middleware; -public class WebAppFixture : IAsyncLifetime +public class WebAppFixture : IAsyncInitializer, IAsyncDisposable { public IAlbaHost AlbaHost = null!; @@ -28,12 +23,10 @@ public async Task InitializeAsync() { var entry = res.First(); return entry.User == $"user:{MockJwtConfiguration.DefaultUser}" - ? new BatchCheckResponse() { Responses = new List() { new(true, entry) } } - : new BatchCheckResponse() { Responses = new List() { new(false, entry) } }; + ? new BatchCheckResponse() { Responses = [new(true, entry)] } + : new BatchCheckResponse() { Responses = [new(false, entry)] }; }); - - AlbaHost = await Alba.AlbaHost.For(builder => { builder.ConfigureServices(s => @@ -44,14 +37,14 @@ public async Task InitializeAsync() }, MockJwtConfiguration.GetDefaultStubConfiguration()); } - public async Task DisposeAsync() + public async ValueTask DisposeAsync() { if (AlbaHost is not null) await AlbaHost.DisposeAsync(); } } -[CollectionDefinition(nameof(WebAppCollection))] -public class WebAppCollection : ICollectionFixture +public abstract class WebAppBase(WebAppFixture fixture) { + protected IAlbaHost Host => fixture.AlbaHost; } \ No newline at end of file diff --git a/tests/Fga.Net.Tests/TheoryData.cs b/tests/Fga.Net.Tests/TheoryData.cs deleted file mode 100644 index 67d48d3..0000000 --- a/tests/Fga.Net.Tests/TheoryData.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections; -using System.Collections.Generic; - -namespace Fga.Net.Tests; - -// https://andrewlock.net/creating-strongly-typed-xunit-theory-test-data-with-theorydata/ -public abstract class TheoryData : IEnumerable -{ - readonly List data = new List(); - - protected void AddRow(params object[] values) - { - data.Add(values); - } - - public IEnumerator GetEnumerator() - { - return data.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } -} \ No newline at end of file diff --git a/tests/Fga.Net.Tests/Unit/AttributeTests.cs b/tests/Fga.Net.Tests/Unit/AttributeTests.cs index a346b51..3645015 100644 --- a/tests/Fga.Net.Tests/Unit/AttributeTests.cs +++ b/tests/Fga.Net.Tests/Unit/AttributeTests.cs @@ -1,23 +1,16 @@ -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Threading.Tasks; -using Fga.Net.AspNetCore.Authorization.Attributes; +using Fga.Net.AspNetCore.Authorization.Attributes; using Fga.Net.AspNetCore.Exceptions; +using FluentAssertions; using HttpContextMoq; using HttpContextMoq.Extensions; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Primitives; -using Moq; -using Xunit; namespace Fga.Net.Tests.Unit; public class AttributeTests { - [Fact] + [Test] public async Task HeaderObjectAttribute_WorksAsExpected() { var httpContext = new HttpContextMock() @@ -27,10 +20,10 @@ public async Task HeaderObjectAttribute_WorksAsExpected() var obj = await attribute.GetObject(httpContext); - Assert.Equal("type:12345", obj); + obj.Should().Be("type:12345"); } - [Fact] + [Test] public async Task HeaderObjectAttribute_ThrowsOnMissingHeader() { var httpContext = new HttpContextMock() @@ -39,10 +32,11 @@ public async Task HeaderObjectAttribute_ThrowsOnMissingHeader() var attribute = new FgaHeaderObjectAttribute("relation", "type", "randomHeader"); var exception = await Assert.ThrowsAsync(async () => await attribute.GetObject(httpContext)); - Assert.Equal("Header randomHeader was not present in the request", exception.Message); + + exception.Message.Should().Be("Header randomHeader was not present in the request"); } - [Fact] + [Test] public async Task PropertyObjectAttribute_WorksAsExpected() { var data = "{\"Foo\":\"Bar\",\"Array\":[]}"u8.ToArray(); @@ -58,10 +52,10 @@ public async Task PropertyObjectAttribute_WorksAsExpected() var obj = await attribute.GetObject(httpContext); - Assert.Equal("type:Bar", obj); + obj.Should().Be("type:Bar"); } - [Fact] + [Test] public async Task PropertyObjectAttribute_ThrowsOnMissingProperty() { var data = "{\"Foo\":\"Bar\",\"Array\":[]}"u8.ToArray(); @@ -75,12 +69,12 @@ public async Task PropertyObjectAttribute_ThrowsOnMissingProperty() var attribute = new FgaPropertyObjectAttribute("relation", "type", "RandomProperty"); - var exception = await Assert.ThrowsAsync(async () => await attribute.GetObject(httpContext)); - - Assert.Equal("Unable to resolve JSON property RandomProperty", exception.Message); + var exception = await Assert.ThrowsAsync(async () => await attribute.GetObject(httpContext)); + + exception.Message.Should().Be("Unable to resolve JSON property RandomProperty"); } - [Fact] + [Test] public async Task QueryObjectAttribute_WorksAsExpected() { var httpContext = new HttpContextMock() @@ -89,11 +83,11 @@ public async Task QueryObjectAttribute_WorksAsExpected() var attribute = new FgaQueryObjectAttribute("relation", "type", "documentId"); var obj = await attribute.GetObject(httpContext); - - Assert.Equal("type:12345", obj); + + obj.Should().Be("type:12345"); } - [Fact] + [Test] public async Task QueryObjectAttribute_ThrowsOnMissingQueryKey() { var httpContext = new HttpContextMock() @@ -102,10 +96,11 @@ public async Task QueryObjectAttribute_ThrowsOnMissingQueryKey() var attribute = new FgaQueryObjectAttribute("relation", "type", "randomQueryKey"); var exception = await Assert.ThrowsAsync(async () => await attribute.GetObject(httpContext)); - Assert.Equal("Query key randomQueryKey was not present in the request", exception.Message); + + exception.Message.Should().Be("Query key randomQueryKey was not present in the request"); } - [Fact] + [Test] public async Task RouteObjectAttribute_WorksAsExpected() { var httpContext = new HttpContextMock() @@ -120,10 +115,10 @@ public async Task RouteObjectAttribute_WorksAsExpected() var obj = await attribute.GetObject(httpContext); - Assert.Equal("type:12345", obj); + obj.Should().Be("type:12345"); } - [Fact] + [Test] public async Task RouteObjectAttribute_ThrowsOnMissingRouteValue() { var httpContext = new HttpContextMock() @@ -133,7 +128,7 @@ public async Task RouteObjectAttribute_ThrowsOnMissingRouteValue() var exception = await Assert.ThrowsAsync(async () => await attribute.GetObject(httpContext)); - Assert.Equal("Route value randomKey was not present in the request", exception.Message); + exception.Message.Should().Be("Route value randomKey was not present in the request"); } } \ No newline at end of file diff --git a/tests/Fga.Net.Tests/Unit/ExtensionScenario.cs b/tests/Fga.Net.Tests/Unit/ExtensionScenario.cs index f63cfe2..6f30e54 100644 --- a/tests/Fga.Net.Tests/Unit/ExtensionScenario.cs +++ b/tests/Fga.Net.Tests/Unit/ExtensionScenario.cs @@ -1,15 +1,9 @@ -using System; using Fga.Net.DependencyInjection; -using Xunit.Abstractions; namespace Fga.Net.Tests.Unit; -public sealed class ExtensionScenario : IXunitSerializable +public sealed class ExtensionScenario { - public ExtensionScenario() - { - } - public ExtensionScenario(string description, Action configuration) { Description = description; @@ -20,15 +14,7 @@ public override string ToString() { return Description; } - - public void Deserialize(IXunitSerializationInfo info) - { } - - public void Serialize(IXunitSerializationInfo info) - { - info.AddValue(nameof(Description), Description); - } - - public string Description { get; init; } = null!; - public Action Configuration { get; } = null!; + + public string Description { get; } + public Action Configuration { get; private set; } } \ No newline at end of file diff --git a/tests/Fga.Net.Tests/Unit/ExtensionTests.cs b/tests/Fga.Net.Tests/Unit/ExtensionTests.cs index ef92875..2b761f2 100644 --- a/tests/Fga.Net.Tests/Unit/ExtensionTests.cs +++ b/tests/Fga.Net.Tests/Unit/ExtensionTests.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Fga.Net.AspNetCore; using Fga.Net.AspNetCore.Authorization; using Fga.Net.AspNetCore.Authorization.Attributes; using Fga.Net.DependencyInjection; using Fga.Net.DependencyInjection.Configuration; +using FluentAssertions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; @@ -14,51 +11,87 @@ using Microsoft.Extensions.Options; using OpenFga.Sdk.Api; using OpenFga.Sdk.Client; -using OpenFga.Sdk.Configuration; -using Xunit; namespace Fga.Net.Tests.Unit; -public class ExtensionTests +public static class ExtensionTestDataSource { - public static TheoryData BadExtensions => - new() + public static IEnumerable> BadExtensions() + { + yield return () => new ExtensionScenario("Empty Extension Config - Auth0 FGA", + config => config.ConfigureAuth0Fga(x => { })); + + yield return () => new ExtensionScenario("Empty Schema", + config => config.ConfigureOpenFga(x => { x.SetConnection("localhost"); })); + + yield return () => new ExtensionScenario("Empty API Key", config => config.ConfigureOpenFga(x => { - new ExtensionScenario("Empty Extension Config - Auth0 FGA", - config => config.ConfigureAuth0Fga(x => { })), - new ExtensionScenario("Empty Schema", - config => config.ConfigureOpenFga(x => { x.SetConnection("localhost"); })), - new ExtensionScenario("Empty API Key", config => config.ConfigureOpenFga(x => + x.SetConnection("http://localhost") + .WithApiKeyAuthentication(""); + })); + + yield return () => new ExtensionScenario("Empty OIDC configuration", config => config.ConfigureOpenFga(x => + { + x.SetConnection("http://localhost") + .WithOidcAuthentication("clientId", "", "issuer", "audience"); + })); + } + + public static IEnumerable> WorkingExtensions() + { + yield return () => new ExtensionScenario("Auth0 FGA", + config => config.ConfigureAuth0Fga(x => + { + x.SetEnvironment(FgaEnvironment.AU) + .WithAuthentication(Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); + })); + + yield return () => new ExtensionScenario("Auth0 FGA", + config => config.ConfigureAuth0Fga(x => + { + x.SetEnvironment(FgaEnvironment.AU) + .WithAuthentication(Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); + })); + yield return () => new ExtensionScenario("OpenFGA - No Credentials", + config => config.ConfigureOpenFga(x => + { + x.SetConnection("http://localhost"); + + })); + yield return () => new ExtensionScenario("OpenFGA - API Key Auth", + config => config.ConfigureOpenFga(x => { x.SetConnection("http://localhost") - .WithApiKeyAuthentication(""); - })), - new ExtensionScenario("Empty OIDC configuration", config => config.ConfigureOpenFga(x => + .WithApiKeyAuthentication("my-special-key"); + })); + yield return () => new ExtensionScenario("OpenFGA - OIDC Auth", + config => config.ConfigureOpenFga(x => { x.SetConnection("http://localhost") - .WithOidcAuthentication("clientId", "", "issuer", "audience"); - })), - }; - - [Theory] - [MemberData(nameof(BadExtensions))] + .WithOidcAuthentication("clientId", "clientSecret", "issuer", "audience"); + })); + } +} +public class ExtensionTests +{ + [Test] + [MethodDataSource(typeof(ExtensionTestDataSource), nameof(ExtensionTestDataSource.BadExtensions))] public void InvalidConfiguration_ThrowsException(ExtensionScenario scenario) { var collection = new ServiceCollection(); - Assert.Throws(() => - collection.AddOpenFgaClient(config => - { - config.SetStoreId(Guid.NewGuid().ToString()); + collection.Invoking(x => x.AddOpenFgaClient(config => + { + config.SetStoreId(Guid.NewGuid().ToString()); - scenario.Configuration(config); - })); + scenario.Configuration(config); + })) + .Should() + .ThrowExactly(); } - - - - [Theory] - [MemberData(nameof(WorkingExtensions))] + + [Test] + [MethodDataSource(typeof(ExtensionTestDataSource), nameof(ExtensionTestDataSource.WorkingExtensions))] public void ClientExtensions_RegisterCorrectly(ExtensionScenario scenario) { var collection = new ServiceCollection(); @@ -74,17 +107,17 @@ public void ClientExtensions_RegisterCorrectly(ExtensionScenario scenario) var provider = collection.BuildServiceProvider(); var apiClient = provider.GetService(); - Assert.NotNull(apiClient); - Assert.IsType(apiClient); + apiClient.Should().NotBeNull(); + apiClient.Should().BeOfType(); var fgaClient = provider.GetService(); - Assert.NotNull(fgaClient); - Assert.IsType(fgaClient); + fgaClient.Should().NotBeNull(); + fgaClient.Should().BeOfType(); } - [Theory] - [MemberData(nameof(WorkingExtensions))] + [Test] + [MethodDataSource(typeof(ExtensionTestDataSource), nameof(ExtensionTestDataSource.WorkingExtensions))] public void AspNetCoreServiceExtensions_RegisterCorrectly(ExtensionScenario scenario) { var collection = new ServiceCollection(); @@ -104,46 +137,18 @@ public void AspNetCoreServiceExtensions_RegisterCorrectly(ExtensionScenario scen var col = provider.GetServices(); - Assert.Contains(col, handler => handler is FineGrainedAuthorizationHandler); + col.Should().Contain(handler => handler is FineGrainedAuthorizationHandler); } - public static TheoryData WorkingExtensions => - new() - { - new ExtensionScenario("Auth0 FGA", - config => config.ConfigureAuth0Fga(x => - { - x.SetEnvironment(FgaEnvironment.AU) - .WithAuthentication(Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); - })), - new ExtensionScenario("OpenFGA - No Credentials", - config => config.ConfigureOpenFga(x => - { x.SetConnection("http://localhost"); - - })), - new ExtensionScenario("OpenFGA - API Key Auth", - config => config.ConfigureOpenFga(x => - { - x.SetConnection("http://localhost") - .WithApiKeyAuthentication("my-special-key"); - })), - new ExtensionScenario("OpenFGA - OIDC Auth", - config => config.ConfigureOpenFga(x => - { - x.SetConnection("http://localhost") - .WithOidcAuthentication("clientId", "clientSecret", "issuer", "audience"); - })), - }; - - [Fact] + [Test] public void AuthorizationPolicyExtension_RegisterCorrectly() { var policy = new AuthorizationPolicyBuilder().AddFgaRequirement().Build(); - Assert.Contains(policy.Requirements, requirement => requirement is FineGrainedAuthorizationRequirement); + policy.Requirements.Should().Contain(requirement => requirement is FineGrainedAuthorizationRequirement); } - [Fact] + [Test] public void MinimalExtensions_RegisterAttributesCorrectly() { var builder = new TestEndpointRouteBuilder(); @@ -157,13 +162,11 @@ public void MinimalExtensions_RegisterAttributesCorrectly() var endpoint = builder.DataSources.Single().Endpoints.Single(); var metadata = endpoint.Metadata.GetOrderedMetadata(); - - Assert.Equal(4, metadata.Count); - Assert.Contains(metadata, attribute => attribute is FgaHeaderObjectAttribute); - Assert.Contains(metadata, attribute => attribute is FgaRouteObjectAttribute); - Assert.Contains(metadata, attribute => attribute is FgaQueryObjectAttribute); - Assert.Contains(metadata, attribute => attribute is FgaPropertyObjectAttribute); - + metadata.Count.Should().Be(4); + metadata.Should().Contain(attribute => attribute is FgaHeaderObjectAttribute); + metadata.Should().Contain(attribute => attribute is FgaRouteObjectAttribute); + metadata.Should().Contain(attribute => attribute is FgaQueryObjectAttribute); + metadata.Should().Contain(attribute => attribute is FgaPropertyObjectAttribute); } private class TestEndpointRouteBuilder : IEndpointRouteBuilder @@ -177,7 +180,7 @@ public IApplicationBuilder CreateApplicationBuilder() } - [Fact] + [Test] public void PostConfigureOptions_OverridesSettings() { var collection = new ServiceCollection(); @@ -203,10 +206,9 @@ public void PostConfigureOptions_OverridesSettings() var provider = collection.BuildServiceProvider(); var config = provider.GetRequiredService>().Value; - - Assert.Null(config.Credentials); - Assert.Equal(openFgaUrl, config.ApiUrl); + config.Credentials.Should().BeNull(); + config.ApiUrl.Should().Be(openFgaUrl); } } \ No newline at end of file diff --git a/tests/Fga.Net.Tests/Unit/ValidationTests.cs b/tests/Fga.Net.Tests/Unit/ValidationTests.cs index 5b80422..c389237 100644 --- a/tests/Fga.Net.Tests/Unit/ValidationTests.cs +++ b/tests/Fga.Net.Tests/Unit/ValidationTests.cs @@ -1,19 +1,19 @@ using Fga.Net.AspNetCore.Authorization; -using Xunit; +using FluentAssertions; namespace Fga.Net.Tests.Unit; public class ValidationTests { - [Theory] - [InlineData("*", true)] - [InlineData("type:id", true)] - [InlineData("*asdf", false)] - [InlineData("type:", false)] - [InlineData(":user", false)] - [InlineData(":", false)] + [Test] + [Arguments("*", true)] + [Arguments("type:id", true)] + [Arguments("*asdf", false)] + [Arguments("type:", false)] + [Arguments(":user", false)] + [Arguments(":", false)] public void UserValidation_ValidatesCorrectly(string user, bool expected) { - Assert.Equal(expected, Validation.IsValidUser(user)); + Validation.IsValidUser(user).Should().Be(expected); } }