Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(fitness): add architecture fitness tests #145

Merged
merged 2 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,44 @@
using Joba.IBM.RPA.Server;
using System.Net.Http.Json;

namespace Joba.IBM.RPA
namespace Joba.IBM.RPA.Cli
{
internal class AccountAuthenticatorFactory : IAccountAuthenticatorFactory
{
private readonly IRpaClientFactory clientFactory;
private readonly IRpaHttpClientFactory httpFactory;

public AccountAuthenticatorFactory(IRpaClientFactory clientFactory, IRpaHttpClientFactory httpFactory)
{
this.clientFactory = clientFactory;
this.httpFactory = httpFactory;
}

IAccountAuthenticator IAccountAuthenticatorFactory.Create(DeploymentOption deployment, AuthenticationMethod authenticationMethod, Region region, PropertyOptions properties)
{
if (deployment == DeploymentOption.OCP && authenticationMethod == AuthenticationMethod.OIDC)
{
var cloudPakAddress = properties[PropertyOptions.CloudPakConsoleAddress];
return cloudPakAddress == null
? throw new NotSupportedException($"Since the server is deployed in the CloudPak cluster, the CloudPak Console URL is required and it was not provided. More information https://ibm.github.io/ibm-rpa-cli/#/guide/environment?id=red-hat®-openshift®-support-with-single-sign-on.")
: (IAccountAuthenticator)new RedHatOpenshiftOidcAuthenticator(httpFactory, region, new Uri(cloudPakAddress));
}

var client = clientFactory.CreateFromRegion(region);
return new WdgAuthenticator(client.Account);
}
}

class WdgAuthenticator : IAccountAuthenticator
{
private readonly IAccountResource resource;

public WdgAuthenticator(IAccountResource resource) => this.resource = resource;

async Task<CreatedSession> IAccountAuthenticator.AuthenticateAsync(AccountCredentials credentials, CancellationToken cancellation) =>
await resource.AuthenticateAsync(credentials.TenantCode, credentials.UserName, credentials.Password, cancellation);
}

class RedHatOpenshiftOidcAuthenticator : IAccountAuthenticator
{
private readonly IRpaHttpClientFactory httpFactory;
Expand Down
2 changes: 1 addition & 1 deletion src/Joba.IBM.RPA.Cli/Client/RpaClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Joba.IBM.RPA.Cli
{
public class RpaClientFactory : IRpaClientFactory
internal class RpaClientFactory : IRpaClientFactory
{
private readonly IConsole console;
private readonly IRpaHttpClientFactory httpFactory;
Expand Down
6 changes: 6 additions & 0 deletions src/Joba.IBM.RPA.Cli/Client/RpaHttpClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@

namespace Joba.IBM.RPA.Cli
{
internal interface IRpaHttpClientFactory
{
HttpClient Create(Uri address);
HttpClient Create(Uri address, IRenewExpiredSession sessionRenewal);
}

internal class RpaHttpClientFactory : IRpaHttpClientFactory
{
private const int MaxParallelism = 10;
Expand Down
26 changes: 0 additions & 26 deletions src/Joba.IBM.RPA/Account/IAccountAuthenticator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,4 @@ public interface IAccountAuthenticatorFactory
{
IAccountAuthenticator Create(DeploymentOption deployment, AuthenticationMethod authenticationMethod, Region region, PropertyOptions properties);
}

public class AccountAuthenticatorFactory : IAccountAuthenticatorFactory
{
private readonly IRpaClientFactory clientFactory;
private readonly IRpaHttpClientFactory httpFactory;

public AccountAuthenticatorFactory(IRpaClientFactory clientFactory, IRpaHttpClientFactory httpFactory)
{
this.clientFactory = clientFactory;
this.httpFactory = httpFactory;
}

IAccountAuthenticator IAccountAuthenticatorFactory.Create(DeploymentOption deployment, AuthenticationMethod authenticationMethod, Region region, PropertyOptions properties)
{
if (deployment == DeploymentOption.OCP && authenticationMethod == AuthenticationMethod.OIDC)
{
var cloudPakAddress = properties[PropertyOptions.CloudPakConsoleAddress];
return cloudPakAddress == null
? throw new NotSupportedException($"Since the server is deployed in the CloudPak cluster, the CloudPak Console URL is required and it was not provided. More information https://ibm.github.io/ibm-rpa-cli/#/guide/environment?id=red-hat®-openshift®-support-with-single-sign-on.")
: (IAccountAuthenticator)new RedHatOpenshiftOidcAuthenticator(httpFactory, region, new Uri(cloudPakAddress));
}

var client = clientFactory.CreateFromRegion(region);
return new WdgAuthenticator(client.Account);
}
}
}
12 changes: 0 additions & 12 deletions src/Joba.IBM.RPA/Account/WdgAuthenticator.cs

This file was deleted.

6 changes: 0 additions & 6 deletions src/Joba.IBM.RPA/IRpaClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ public interface IRpaClientFactory
IRpaClient CreateFromPackageSource(PackageSource source);
}

public interface IRpaHttpClientFactory
{
HttpClient Create(Uri address);
HttpClient Create(Uri address, IRenewExpiredSession sessionRenewal);
}

public interface IRenewExpiredSession
{
Task<Session> RenewAsync(CancellationToken cancellation);
Expand Down
2 changes: 1 addition & 1 deletion src/Joba.IBM.RPA/Pipeline/AsyncPipeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
/// Based on: https://github.com/ipvalverde/PipelineNet
/// </summary>
/// <typeparam name="TContext">The parameter type.</typeparam>
public sealed class AsyncPipeline<TContext> : IAsyncPipeline<TContext>
sealed class AsyncPipeline<TContext> : IAsyncPipeline<TContext>
{
private readonly IList<IAsyncMiddleware<TContext>> middlewares;
private IAsyncMiddleware<TContext> @finally = EmptyMiddleware.None;
private Func<TContext, PipelineExceptionSource, CancellationToken, Task> @catch;

private AsyncPipeline()

Check warning on line 14 in src/Joba.IBM.RPA/Pipeline/AsyncPipeline.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field 'catch' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
{
middlewares = new List<IAsyncMiddleware<TContext>>();
}
Expand Down Expand Up @@ -49,7 +49,7 @@
return;

var index = 0;
Func<TContext, Task> action = null;

Check warning on line 52 in src/Joba.IBM.RPA/Pipeline/AsyncPipeline.cs

View workflow job for this annotation

GitHub Actions / build

Converting null literal or possible null value to non-nullable type.
action = async (param) =>
{
cancellation.ThrowIfCancellationRequested();
Expand All @@ -61,7 +61,7 @@
if (index == middlewares.Count)
action = (p) => Task.CompletedTask;

await middleware.Run(param, action, cancellation).ConfigureAwait(false);

Check warning on line 64 in src/Joba.IBM.RPA/Pipeline/AsyncPipeline.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'next' in 'Task IAsyncMiddleware<TContext>.Run(TContext parameter, Func<TContext, Task> next, CancellationToken cancellation)'.
};

try
Expand Down Expand Up @@ -90,7 +90,7 @@
public static EmptyMiddleware None = new EmptyMiddleware();
private readonly Func<TContext, CancellationToken, Task> @finally;

private EmptyMiddleware() { }

Check warning on line 93 in src/Joba.IBM.RPA/Pipeline/AsyncPipeline.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable field 'finally' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

public EmptyMiddleware(Func<TContext, CancellationToken, Task> @finally)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Joba.IBM.RPA/Pipeline/DefaultPipelineMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// Default implementation of <see cref="IAsyncMiddleware{TParameter}"/>.
/// </summary>
/// <typeparam name="TContext"></typeparam>
public abstract class DefaultPipelineMiddleware<TContext> : IAsyncMiddleware<TContext>
abstract class DefaultPipelineMiddleware<TContext> : IAsyncMiddleware<TContext>
{
/// <summary>
/// Override this method to implement the middleware logic.
Expand Down
2 changes: 1 addition & 1 deletion src/Joba.IBM.RPA/Pipeline/IAsyncMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// Defines a piece of code (a step) to be executed inside a pipeline.
/// </summary>
/// <typeparam name="TParameter">The parameter type.</typeparam>
public interface IAsyncMiddleware<TParameter>
interface IAsyncMiddleware<TParameter>
{
/// <summary>
/// Runs this middleware and enables calling the next middleware.
Expand Down
2 changes: 1 addition & 1 deletion src/Joba.IBM.RPA/Pipeline/IAsyncPipeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// Defines and executes a pipeline.
/// </summary>
/// <typeparam name="TContext">The parameter type.</typeparam>
public interface IAsyncPipeline<TContext>
interface IAsyncPipeline<TContext>
{
/// <summary>
/// Adds a middleware.
Expand Down
2 changes: 1 addition & 1 deletion src/Joba.IBM.RPA/Pipeline/PipelineExceptionSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// <summary>
/// Defines the exception structure of a pipeline.
/// </summary>
public sealed class PipelineExceptionSource
sealed class PipelineExceptionSource
{
public PipelineExceptionSource(Exception ex)
{
Expand Down
File renamed without changes.
37 changes: 37 additions & 0 deletions src/Tests/Joba.IBM.RPA.Tests/Fitness/ArchitectureShould.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using ArchUnitNET.Domain;
using ArchUnitNET.Loader;
using ArchUnitNET.xUnit;
using static ArchUnitNET.Fluent.ArchRuleDefinition;

namespace Joba.IBM.RPA.Tests
{
public class ArchitectureShould
{
private static readonly System.Reflection.Assembly[] assemblies = new System.Reflection.Assembly[] { typeof(IProject).Assembly };
private static readonly Architecture architecture = new ArchLoader().LoadAssemblies(assemblies).Build();

[Fact]
public void DisallowUsingHttpClient()
{
var rule = Types().Should().NotDependOnAny(typeof(HttpClient))
.Because($"'{string.Join(", ", assemblies.Select(a => a.GetName().Name))}' require(s) using abstractions '{nameof(IRpaClient)} or {nameof(IRpaClientFactory)}'");
rule.Check(architecture);
}

[Fact]
public void EnsureSameNamespace()
{
var rule = Types().That().ArePublic().Should().ResideInNamespace(@"Joba.IBM.RPA(\.Server)?", useRegularExpressions: true)
.Because("exposing many namespaces is a bad practice.");
rule.Check(architecture);
}

//[Fact]
//public void DisallowReferencingSystemCommandLinePackage()
//{
// //ArchUnitTEST does not support
// //See: https://github.com/TNG/ArchUnitNET/issues/130
// //See: https://gaevoy.com/2022/05/19/review-dependencies-on-every-commit.html
//}
}
}
11 changes: 11 additions & 0 deletions src/Tests/Joba.IBM.RPA.Tests/Fitness/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Fitness functions
This directory contains fitness functions implemented as tests.

## What are fitness functions?
> Any mechanism that performs an objective integrity assessment of some architecture characteristic or combination of architecture characteristics.

In the RPA CLI project, we implement fitness functions as a mechanism to test the **architecture** of the system, enforcing rules to be followed by developers, like forbidding some library packages to be installed in the *Joba.IBM.RPA* core project.

## More details
- Article: [The Up-and-Running Guide to Architectural Fitness Functions](https://betterprogramming.pub/the-up-and-running-guide-to-architectural-fitness-functions-1621ebe46ea).
- Book: [Fundamentals of Software Architecture: An Engineering Approach](https://www.amazon.com/Fundamentals-Software-Architecture-Comprehensive-Characteristics/dp/1492043451).
1 change: 1 addition & 0 deletions src/Tests/Joba.IBM.RPA.Tests/Joba.IBM.RPA.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Moq" Version="4.20.69" />
<PackageReference Include="TngTech.ArchUnitNET.xUnit" Version="0.10.6" />
<PackageReference Include="Verify.Xunit" Version="22.1.4" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
Expand Down
Loading