Skip to content

Commit

Permalink
Consolidate Validation logic into a shared builder for re-use across …
Browse files Browse the repository at this point in the history
…modules (#35)

* Added in validation builder and unit tests before changing modules

* converted all modules to use new validation builder

* Tests for validation builder
  • Loading branch information
james-d12 authored Mar 1, 2025
1 parent cea7432 commit 78cc0b0
Show file tree
Hide file tree
Showing 21 changed files with 639 additions and 172 deletions.
28 changes: 23 additions & 5 deletions CodeHub.sln
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Frontend", "Frontend", "{FD
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeHub.Portal.Services", "src\frontend\CodeHub.Portal.Services\CodeHub.Portal.Services.csproj", "{5A58BC92-EC25-49F9-991A-3A2F03E5899F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{3AC7FEA5-756C-45E1-8988-16170C26B898}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeHub.Portal", "src\frontend\CodeHub.Portal\CodeHub.Portal.csproj", "{E6AF518B-3FAC-4D3F-8BF2-7C8411B153CA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeHub.Platform.AzureDevOps", "src\backend\CodeHub.Platform.AzureDevOps\CodeHub.Platform.AzureDevOps.csproj", "{5A4A50EF-186E-4FD0-BB09-BA7A46D6B8C4}"
Expand Down Expand Up @@ -56,6 +54,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeHub.Platform.GitHub", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeHub.Platform.GitLab", "src\backend\CodeHub.Platform.GitLab\CodeHub.Platform.GitLab.csproj", "{7014F74C-506D-4DC3-85DC-DF14EC446FB4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeHub.Platform.GitHub.Tests", "src\backend\CodeHub.Platform.GitHub.Tests\CodeHub.Platform.GitHub.Tests.csproj", "{8BD76230-630B-42A1-AAB3-E3A6E2EAB8A1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeHub.Platform.GitLab.Tests", "src\backend\CodeHub.Platform.GitLab.Tests\CodeHub.Platform.GitLab.Tests.csproj", "{6A915CB9-8153-433B-9DC1-0AFE159486B7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeHub.Shared.Tests", "src\backend\CodeHub.Shared.Tests\CodeHub.Shared.Tests.csproj", "{97CBF632-FA60-42E7-AD2E-7DBD672C7FEA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -102,6 +106,18 @@ Global
{7014F74C-506D-4DC3-85DC-DF14EC446FB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7014F74C-506D-4DC3-85DC-DF14EC446FB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7014F74C-506D-4DC3-85DC-DF14EC446FB4}.Release|Any CPU.Build.0 = Release|Any CPU
{8BD76230-630B-42A1-AAB3-E3A6E2EAB8A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8BD76230-630B-42A1-AAB3-E3A6E2EAB8A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8BD76230-630B-42A1-AAB3-E3A6E2EAB8A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8BD76230-630B-42A1-AAB3-E3A6E2EAB8A1}.Release|Any CPU.Build.0 = Release|Any CPU
{6A915CB9-8153-433B-9DC1-0AFE159486B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6A915CB9-8153-433B-9DC1-0AFE159486B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6A915CB9-8153-433B-9DC1-0AFE159486B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6A915CB9-8153-433B-9DC1-0AFE159486B7}.Release|Any CPU.Build.0 = Release|Any CPU
{97CBF632-FA60-42E7-AD2E-7DBD672C7FEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{97CBF632-FA60-42E7-AD2E-7DBD672C7FEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97CBF632-FA60-42E7-AD2E-7DBD672C7FEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{97CBF632-FA60-42E7-AD2E-7DBD672C7FEA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -110,15 +126,17 @@ Global
{0B59B285-BE8E-48DA-9A9D-AFAF8B4D1C50} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{8D2E2CFA-8324-40CA-8D0D-C1F273363F6E} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{5A58BC92-EC25-49F9-991A-3A2F03E5899F} = {FD7D4EDA-3EEC-4989-B479-563417E6848F}
{3AC7FEA5-756C-45E1-8988-16170C26B898} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{E6AF518B-3FAC-4D3F-8BF2-7C8411B153CA} = {FD7D4EDA-3EEC-4989-B479-563417E6848F}
{5A4A50EF-186E-4FD0-BB09-BA7A46D6B8C4} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{F8B9B097-D031-4E97-8F65-41078E810E3D} = {3AC7FEA5-756C-45E1-8988-16170C26B898}
{794E0C1D-6331-49C2-B835-D02C1FE83D01} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{21C8395D-3B75-46E3-AAAE-396CDE1679DB} = {FD7D4EDA-3EEC-4989-B479-563417E6848F}
{58014886-DBFC-4D3B-9EA3-03B8A228B601} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{4D69214A-521E-413C-8732-A228A50753DD} = {3AC7FEA5-756C-45E1-8988-16170C26B898}
{8AFCA3A8-0CDB-4FF7-B484-803EA74E94D5} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{7014F74C-506D-4DC3-85DC-DF14EC446FB4} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{4D69214A-521E-413C-8732-A228A50753DD} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{F8B9B097-D031-4E97-8F65-41078E810E3D} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{8BD76230-630B-42A1-AAB3-E3A6E2EAB8A1} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{6A915CB9-8153-433B-9DC1-0AFE159486B7} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
{97CBF632-FA60-42E7-AD2E-7DBD672C7FEA} = {6BBAFA79-C6EC-4415-8249-3AF0BF69A8F8}
EndGlobalSection
EndGlobal
15 changes: 4 additions & 11 deletions src/backend/CodeHub.Platform.Azure/Models/AzureSettings.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
namespace CodeHub.Platform.Azure.Models;
using CodeHub.Shared.Models;

internal sealed record AzureSettings
{
public required bool IsEnabled { get; init; }
namespace CodeHub.Platform.Azure.Models;

public static AzureSettings CreateDisabled()
{
return new AzureSettings
{
IsEnabled = false
};
}
internal sealed class AzureSettings : Settings
{
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CodeHub.Platform.Azure.Models;
using CodeHub.Shared.Validation;
using Microsoft.Extensions.Configuration;

namespace CodeHub.Platform.Azure.Validation;
Expand All @@ -7,24 +8,9 @@ internal static class AzureSettingsValidator
{
internal static AzureSettings GetValidSettings(IConfiguration configuration)
{
var settingsSection = configuration.GetSection(nameof(AzureSettings));

if (!settingsSection.Exists())
{
throw new InvalidOperationException("Azure settings section is missing");
}

var isEnabledSection = settingsSection.GetSection(nameof(AzureSettings.IsEnabled));
var isEnabled = settingsSection.GetValue<bool>(nameof(AzureSettings.IsEnabled));

if (!isEnabledSection.Exists() || !isEnabled)
{
return AzureSettings.CreateDisabled();
}

return new AzureSettings
{
IsEnabled = isEnabled
};
return new ValidationBuilder<AzureSettings>(configuration)
.SectionExists(nameof(AzureSettings))
.CheckEnabled(x => x.IsEnabled, nameof(AzureSettings.IsEnabled))
.Build();
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
namespace CodeHub.Platform.AzureDevOps.Models;
using CodeHub.Shared.Models;

internal sealed record AzureDevOpsSettings
{
public required string PersonalAccessToken { get; init; }
public required string Organization { get; init; }
public required bool IsEnabled { get; init; }
namespace CodeHub.Platform.AzureDevOps.Models;

public static AzureDevOpsSettings CreateDisabled()
{
return new AzureDevOpsSettings
{
IsEnabled = false,
PersonalAccessToken = string.Empty,
Organization = string.Empty,
};
}
internal sealed class AzureDevOpsSettings : Settings
{
public string PersonalAccessToken { get; init; } = string.Empty;
public string Organization { get; init; } = string.Empty;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CodeHub.Platform.AzureDevOps.Models;
using CodeHub.Shared.Validation;
using Microsoft.Extensions.Configuration;

namespace CodeHub.Platform.AzureDevOps.Validation;
Expand All @@ -7,39 +8,11 @@ internal static class AzureDevOpsSettingsValidator
{
internal static AzureDevOpsSettings GetValidSettings(IConfiguration configuration)
{
var settingsSection = configuration.GetSection(nameof(AzureDevOpsSettings));

if (!settingsSection.Exists())
{
throw new InvalidOperationException("Azure DevOps settings section is missing.");
}

var isEnabledSection = settingsSection.GetSection(nameof(AzureDevOpsSettings.IsEnabled));
var isEnabled = settingsSection.GetValue<bool>(nameof(AzureDevOpsSettings.IsEnabled));

if (!isEnabledSection.Exists() || !isEnabled)
{
return AzureDevOpsSettings.CreateDisabled();
}

var organization = settingsSection.GetValue<string>(nameof(AzureDevOpsSettings.Organization));
var personalAccessToken = settingsSection.GetValue<string>(nameof(AzureDevOpsSettings.PersonalAccessToken));

if (string.IsNullOrEmpty(organization))
{
throw new InvalidOperationException("Organization configuration is missing");
}

if (string.IsNullOrEmpty(personalAccessToken))
{
throw new InvalidOperationException("Personal Access Token configuration is missing");
}

return new AzureDevOpsSettings
{
Organization = organization,
PersonalAccessToken = personalAccessToken,
IsEnabled = isEnabled
};
return new ValidationBuilder<AzureDevOpsSettings>(configuration)
.SectionExists(nameof(AzureDevOpsSettings))
.CheckEnabled(x => x.IsEnabled, nameof(AzureDevOpsSettings.IsEnabled))
.CheckValue(x => x.Organization, nameof(AzureDevOpsSettings.Organization))
.CheckValue(x => x.PersonalAccessToken, nameof(AzureDevOpsSettings.PersonalAccessToken))
.Build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="coverlet.collector"/>
<PackageReference Include="coverlet.msbuild"/>
<PackageReference Include="Microsoft.Extensions.Configuration"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions"/>
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="xunit"/>
<PackageReference Include="xunit.runner.visualstudio"/>
</ItemGroup>

<ItemGroup>
<Using Include="Xunit"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CodeHub.Platform.GitHub\CodeHub.Platform.GitHub.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using CodeHub.Platform.GitHub.Extensions;
using CodeHub.Platform.GitHub.Services;
using CodeHub.Shared.Services;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace CodeHub.Platform.GitHub.Tests.Extensions;

public sealed class GitHubExtensionsTests
{
[Fact]
public void RegisterGitHubServices_WhenEnabledWithValidSettings_RegistersCorrectServices()
{
// Arrange
var serviceCollection = new ServiceCollection();
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(GetValidGitHubConfiguration(true))
.Build();

// Act
serviceCollection.RegisterGitHub(configuration);

// Assert
Assert.Contains(serviceCollection,
service => service.ServiceType == typeof(IDiscoveryService) &&
service.Lifetime == ServiceLifetime.Singleton &&
service.ImplementationType == typeof(GitHubDiscoveryService));
Assert.Contains(serviceCollection,
service => service.ServiceType == typeof(IGitHubService) &&
service.Lifetime == ServiceLifetime.Singleton &&
service.ImplementationType == typeof(GitHubService));
Assert.Contains(serviceCollection,
service => service.ServiceType == typeof(IGitHubConnectionService) &&
service.Lifetime == ServiceLifetime.Singleton &&
service.ImplementationType == typeof(GitHubConnectionService));
Assert.Contains(serviceCollection,
service => service.ServiceType == typeof(IMemoryCache) &&
service.ImplementationType == typeof(MemoryCache));
}

[Fact]
public void RegisterGitHubServices_WhenDisabledWithValidSettings_DoesNotRegisterServices()
{
// Arrange
var serviceCollection = new ServiceCollection();
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(GetValidGitHubConfiguration(false))
.Build();

// Act
var serviceCountBefore = serviceCollection.Count;
serviceCollection.RegisterGitHub(configuration);

// Assert
Assert.Equal(serviceCountBefore, serviceCollection.Count);
}

[Fact]
public void RegisterGitHubServices_WhenDisabledWithValidSettingsButNotWholeSection_DoesNotRegisterServices()
{
// Arrange
var serviceCollection = new ServiceCollection();
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(GetValidWithoutOtherGitHubConfiguration(false))
.Build();

// Act
var serviceCountBefore = serviceCollection.Count;
serviceCollection.RegisterGitHub(configuration);

// Assert
Assert.Equal(serviceCountBefore, serviceCollection.Count);
}


[Fact]
public void RegisterGitHubServices_WhenEnabledButCalledWithMissingSettings_ThrowsException()
{
// Arrange
var serviceCollection = new ServiceCollection();
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(GetInvalidGitHubSettings(true))
.Build();

// Act + Assert
Assert.Throws<InvalidOperationException>(() => serviceCollection.RegisterGitHub(configuration));
}

private static Dictionary<string, string?> GetValidGitHubConfiguration(bool enabled)
{
return new Dictionary<string, string?>
{
{ "GitHubSettings:AgentName", "TestAgentName" },
{ "GitHubSettings:Token", "TestToken" },
{ "GitHubSettings:IsEnabled", enabled.ToString() }
};
}

private static Dictionary<string, string?> GetValidWithoutOtherGitHubConfiguration(bool enabled)
{
return new Dictionary<string, string?>
{
{ "GitHubSettings:IsEnabled", enabled.ToString() }
};
}

private static Dictionary<string, string?> GetInvalidGitHubSettings(bool enabled)
{
return new Dictionary<string, string?>
{
{ "GitHubSettings:IsEnabled", enabled.ToString() }
};
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Octokit" />
<PackageReference Include="Octokit"/>
<PackageReference Include="System.Text.Json"/>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions"/>
<PackageReference Include="Microsoft.Extensions.Caching.Memory"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CodeHub.Shared\CodeHub.Shared.csproj"/>
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="CodeHub.Platform.GitHub.Tests"/>
</ItemGroup>
</Project>
21 changes: 6 additions & 15 deletions src/backend/CodeHub.Platform.GitHub/Models/GitHubSettings.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
namespace CodeHub.Platform.GitHub.Models;
using CodeHub.Shared.Models;

internal sealed record GitHubSettings
{
public required string AgentName { get; init; }
public required string Token { get; init; }
public required bool IsEnabled { get; init; }
namespace CodeHub.Platform.GitHub.Models;

public static GitHubSettings CreateDisabled()
{
return new GitHubSettings
{
IsEnabled = false,
AgentName = string.Empty,
Token = string.Empty
};
}
internal sealed class GitHubSettings : Settings
{
public string AgentName { get; init; } = string.Empty;
public string Token { get; init; } = string.Empty;
}
Loading

0 comments on commit 78cc0b0

Please sign in to comment.