Skip to content

Commit 596e948

Browse files
authored
Merge pull request #22 from Black-Cockpit/feature/add_net9_net10_keycloak_26_support
🆗 Fixed code smells and added organization test coverage
2 parents 8534f77 + d03392a commit 596e948

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+907
-226
lines changed

.fossa.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ version: 1
33
analysis:
44
# Paths to exclude from license/security analysis (relative to repo root)
55
ignorePaths:
6-
- "NETCore.Keycloak.Client.Tests/**"
7-
6+
- "NETCore.Keycloak.Client.Tests/**"
87
# Specific package locators to ignore (use exact locator shown by FOSSA)
98
ignorePackages:
10-
- "pip+ansible$13.2.0"
11-
- "pip+ansible-core$2.17.7"
12-
- "nuget+Microsoft.NET.Test.Sdk$17.6.0"
9+
- "pip+ansible$13.2.0"
10+
- "pip+ansible-core$2.17.7"
11+
- "nuget+Microsoft.NET.Test.Sdk$17.6.0"
1312

1413
# Notes:
1514
# - Commit and push this file and then trigger a new FOSSA scan so the server-side

.github/workflows/branch_naming_policy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99
- reopened
1010
jobs:
1111
branch-naming-policy:
12-
runs-on: ubuntu-latest
12+
runs-on: ubuntu-24.04
1313
steps:
1414
- name: Checkout
1515
uses: actions/checkout@v3
Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Build test and analyze
22
on:
3-
push:
3+
push:
44
branches:
55
- master
66
pull_request:
@@ -15,48 +15,54 @@ on:
1515
jobs:
1616
# Build test and analyze source code
1717
build_test_analyze:
18-
runs-on: ubuntu-22.04
18+
runs-on: ubuntu-24.04
1919
strategy:
2020
matrix:
21-
java-version: [ 21 ]
21+
java-version: [21]
2222
steps:
2323
- name: Checkout Repository
2424
uses: actions/checkout@v4
25-
25+
2626
# Setup OpenJDK
2727
- name: Setup OpenJDK
2828
uses: actions/setup-java@v3
2929
with:
30-
distribution: 'adopt'
30+
distribution: "adopt"
3131
java-version: ${{ matrix.java-version }}
3232

33+
# Setup Python
34+
- name: Setup Python
35+
uses: actions/setup-python@v5
36+
with:
37+
python-version: '3.12'
38+
3339
# Install all required .NET SDK versions
34-
- name: Install .NET SDKs (6.0, 7.0, 8.0)
40+
- name: Install .NET SDKs (8.0, 9.0 and 10.0)
3541
uses: actions/setup-dotnet@v1
3642
with:
3743
dotnet-version: |
38-
6.0.x
39-
7.0.x
4044
8.0.x
41-
45+
9.0.x
46+
10.0.x
47+
4248
# Install dependencies
4349
- name: Install dependencies
4450
run: |
45-
sudo apt install -y make python3-pip python3-rpm python3-psycopg2
46-
pip install 'python-keycloak==3.3.0' --user
51+
sudo apt install -y make python3-rpm
4752
dotnet tool install --global dotnet-sonarscanner
4853
dotnet tool install --global Cake.Tool
49-
dotnet tool install --global JetBrains.dotCover.GlobalTool
50-
54+
dotnet tool install --global JetBrains.dotCover.CommandLineTools
55+
5156
# Build test and analyze the project
5257
- name: Build test and analyze the project
5358
if: success()
5459
env:
5560
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
61+
VIRTUAL_ENV_DIR: keycloak.venv
5662
run: |
5763
# Copy Licence
5864
cp LICENSE NETCore.Keycloak.Client/
59-
60-
# Build, test and analyze project with keycloak version 20
61-
cd NETCore.Keycloak.Client.Tests
62-
dotnet cake build_test_analyse.cake --kc_major_version=20 --sonar_token=${SONAR_TOKEN}
65+
66+
# Build, test and analyze project with keycloak version 26
67+
cd NETCore.Keycloak.Client.Tests
68+
dotnet cake build_test_analyse.cake --kc_major_version=26 --sonar_token=${SONAR_TOKEN}

.github/workflows/deploy.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
dotnet-version: [8.0.x]
14+
dotnet-version: [10.0.x]
1515
steps:
1616
- name: Checkout Repository
1717
uses: actions/checkout@v4
18-
18+
1919
# Setup .NET SDK
2020
- name: Set up .NET SDK ${{ matrix.dotnet-version }}
2121
uses: actions/setup-dotnet@v1
2222
with:
2323
dotnet-version: ${{ matrix.dotnet-version }}
24-
24+
2525
# Setup cake tool
2626
- name: Setup cake tool
2727
run: dotnet tool install --global Cake.Tool
@@ -32,10 +32,10 @@ jobs:
3232
run: |
3333
# Copy Licence
3434
cp LICENSE NETCore.Keycloak.Client/
35-
35+
3636
# Build project
3737
dotnet cake build.cake --target=build
38-
38+
3939
# Deploy nuget package
4040
- name: Deploy nuget package
4141
if: success()
@@ -44,7 +44,6 @@ jobs:
4444
run: |
4545
# Extract nuget package version
4646
NUGET_PKG_VERSION=$(cat NETCore.Keycloak.Client/NETCore.Keycloak.Client.csproj | grep "PackageVersion" | awk -F '>' '{print $2}' | awk -F '<' '{print $1}')
47-
47+
4848
# Deploy package
4949
dotnet nuget push NETCore.Keycloak.Client/bin/Release/Keycloak.NETCore.Client.${NUGET_PKG_VERSION}.nupkg --api-key $NUGET_API_KEY --source https://api.nuget.org/v3/index.json
50-

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ persister
44
Test
55
/persister
66
.vscode
7+
.claude
78
Scripting
89
docker
910
kubernetes
@@ -37,6 +38,9 @@ out
3738
**/out
3839
**/containers/**
3940
**/Assets/*.json
41+
**/*.dcvr
42+
**/dotCover.Output.xml
43+
**/dotCover.Output.html
4044

4145
**/NETCore.Keycloak.Client/LICENSE
4246

@@ -198,7 +202,6 @@ nCrunchTemp_*
198202
*.mm.*
199203
AutoTest.Net/
200204

201-
202205
# local files
203206
compose.yml
204207

NETCore.Keycloak.Client.Tests/Abstraction/KcTestingModule.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using NETCore.Keycloak.Client.Models.Common;
1111
using NETCore.Keycloak.Client.Models.Groups;
1212
using NETCore.Keycloak.Client.Models.KcEnum;
13+
using NETCore.Keycloak.Client.Models.Organizations;
1314
using NETCore.Keycloak.Client.Models.Roles;
1415
using NETCore.Keycloak.Client.Models.Tokens;
1516
using NETCore.Keycloak.Client.Models.Users;
@@ -495,6 +496,62 @@ protected async Task<KcGroup> CreateAndGetGroupAsync(string context)
495496
return listGroupsResponse.Response.First();
496497
}
497498

499+
/// <summary>
500+
/// Creates a new organization in the Keycloak realm and retrieves its details.
501+
/// This method generates a mock organization, creates it via the Keycloak Admin API,
502+
/// then retrieves the created organization by listing organizations with a matching name filter.
503+
/// </summary>
504+
/// <param name="context">The context name used for managing environment variables.</param>
505+
/// <returns>A task representing the asynchronous operation, with a result of <see cref="KcOrganization"/>.</returns>
506+
protected async Task<KcOrganization> CreateAndGetOrganizationAsync(string context)
507+
{
508+
// Retrieve an access token for the realm admin to perform the organization creation.
509+
var accessToken = await GetRealmAdminTokenAsync(context).ConfigureAwait(false);
510+
Assert.IsNotNull(accessToken);
511+
512+
// Create a faker instance for generating random data.
513+
var faker = new Faker();
514+
515+
// Generate a mock organization.
516+
var kcOrganization = KcOrganizationMocks.Generate(faker);
517+
518+
// Execute the operation to create the organization.
519+
var createOrganizationResponse = await KeycloakRestClient.Organizations.CreateAsync(
520+
TestEnvironment.TestingRealm.Name,
521+
accessToken.AccessToken,
522+
kcOrganization).ConfigureAwait(false);
523+
524+
// Validate the response from the organization creation operation.
525+
Assert.IsNotNull(createOrganizationResponse);
526+
Assert.IsFalse(createOrganizationResponse.IsError);
527+
528+
// Validate the monitoring metrics for the successful organization creation request.
529+
KcCommonAssertion.AssertResponseMonitoringMetrics(createOrganizationResponse.MonitoringMetrics,
530+
HttpStatusCode.Created, HttpMethod.Post);
531+
532+
// Execute the operation to list organizations matching the specified filter criteria.
533+
var listOrganizationsResponse = await KeycloakRestClient.Organizations
534+
.ListAsync(TestEnvironment.TestingRealm.Name, accessToken.AccessToken, new KcOrganizationFilter
535+
{
536+
Exact = true,
537+
Search = kcOrganization.Name
538+
}).ConfigureAwait(false);
539+
540+
// Validate the response from the organization listing operation.
541+
Assert.IsNotNull(listOrganizationsResponse);
542+
Assert.IsFalse(listOrganizationsResponse.IsError);
543+
Assert.IsNotNull(listOrganizationsResponse.Response);
544+
545+
// Ensure that the test organization is included in the results.
546+
Assert.IsTrue(listOrganizationsResponse.Response.Any(org => org.Name == kcOrganization.Name));
547+
548+
// Validate the monitoring metrics for the successful organization listing request.
549+
KcCommonAssertion.AssertResponseMonitoringMetrics(listOrganizationsResponse.MonitoringMetrics,
550+
HttpStatusCode.OK, HttpMethod.Get);
551+
552+
return listOrganizationsResponse.Response.First();
553+
}
554+
498555
/// <summary>
499556
/// Loads the test environment configuration from the `Assets/testing_environment.json` file.
500557
/// The loaded configuration is deserialized into the <see cref="KcTestEnvironment"/> object.
@@ -508,4 +565,19 @@ protected void LoadConfiguration()
508565

509566
Assert.IsNotNull(TestEnvironment, "The test environment configuration must not be null.");
510567
}
568+
569+
/// <summary>
570+
/// Gets the major version of the Keycloak server from the test environment configuration.
571+
/// </summary>
572+
/// <returns>The major version number, or 0 if the version is not set.</returns>
573+
protected int GetKcMajorVersion()
574+
{
575+
if ( string.IsNullOrWhiteSpace(TestEnvironment?.KcVersion) )
576+
{
577+
return 0;
578+
}
579+
580+
var parts = TestEnvironment.KcVersion.Split('.');
581+
return int.TryParse(parts[0], out var major) ? major : 0;
582+
}
511583
}

NETCore.Keycloak.Client.Tests/Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ CURRENT_MK := $(lastword $(MAKEFILE_LIST))
1616

1717
# Configuration for the virtual environment
1818
# Virtual environment directory name
19-
VIRTUAL_ENV_DIR := "keycloak.venv"
19+
VIRTUAL_ENV_DIR := keycloak.venv
20+
export VIRTUAL_ENV_DIR
2021

2122
# Directory context for the project
2223
CONF_DIR_CONTEXT := "."
@@ -81,7 +82,7 @@ prepare_keycloak_25_environment: check_virtual_env stop
8182
@pushd ${CONF_DIR_CONTEXT}/ && source ${VIRTUAL_ENV_DIR}/bin/activate && ansible-playbook ansible/provision_keycloak.yml -e "stack_state=present" -e "kc_version=25.0.6"
8283

8384
prepare_keycloak_26_environment: check_virtual_env stop
84-
@pushd ${CONF_DIR_CONTEXT}/ && source ${VIRTUAL_ENV_DIR}/bin/activate && ansible-playbook ansible/provision_keycloak.yml -e "stack_state=present" -e "kc_version=26.0.8"
85+
@pushd ${CONF_DIR_CONTEXT}/ && source ${VIRTUAL_ENV_DIR}/bin/activate && ansible-playbook ansible/provision_keycloak.yml -e "stack_state=present" -e "kc_version=26.5.6"
8586

8687
# -----------------------------------
8788
# Target: stop
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using Bogus;
2+
using NETCore.Keycloak.Client.Models.Organizations;
3+
4+
namespace NETCore.Keycloak.Client.Tests.MockData;
5+
6+
/// <summary>
7+
/// Provides mock data generation for Keycloak organization objects used in testing scenarios.
8+
/// This class leverages the Faker library to generate realistic data for testing Keycloak organization configurations.
9+
/// </summary>
10+
public static class KcOrganizationMocks
11+
{
12+
/// <summary>
13+
/// Generates a mock <see cref="KcOrganization"/> instance with randomized test data.
14+
/// </summary>
15+
/// <param name="faker">The <see cref="Faker"/> instance used to generate randomized data.</param>
16+
/// <returns>A new <see cref="KcOrganization"/> instance populated with test data.</returns>
17+
/// <exception cref="AssertFailedException">
18+
/// Thrown if the provided <see cref="Faker"/> instance is null.
19+
/// </exception>
20+
public static KcOrganization Generate(Faker faker)
21+
{
22+
// Ensure the Faker instance is not null before generating data.
23+
Assert.IsNotNull(faker);
24+
25+
// Generate a unique name for the organization.
26+
var orgName = Guid.NewGuid().ToString().Replace("-", string.Empty, StringComparison.Ordinal);
27+
28+
// Create an organization with randomized details.
29+
return new KcOrganization
30+
{
31+
Name = orgName,
32+
Alias = orgName,
33+
Enabled = true,
34+
Description = faker.Lorem.Sentence(),
35+
RedirectUrl = faker.Internet.Url(),
36+
Attributes = new Dictionary<string, List<string>>
37+
{
38+
{ "test_attr", ["value1"] }
39+
},
40+
Domains =
41+
[
42+
new KcOrganizationDomain
43+
{
44+
Name = $"{orgName}.{faker.Internet.DomainSuffix()}",
45+
Verified = false
46+
}
47+
]
48+
};
49+
}
50+
}

NETCore.Keycloak.Client.Tests/Models/KcTestEnvironment.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ namespace NETCore.Keycloak.Client.Tests.Models;
1010
/// </summary>
1111
public class KcTestEnvironment
1212
{
13+
/// <summary>
14+
/// Gets or sets the Keycloak server version.
15+
/// </summary>
16+
[JsonProperty("kc_version")]
17+
public string KcVersion { get; set; }
18+
1319
/// <summary>
1420
/// Gets or sets the base URL of the Keycloak server.
1521
/// </summary>

NETCore.Keycloak.Client.Tests/Modules/KcAuthentication/KcAuthenticationExtensionTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public void ShouldRegisterClaimsTransformation()
8888
Assert.IsNotNull(claimsTransformer, "IClaimsTransformation should be registered.");
8989

9090
// Verify that the registered IClaimsTransformation is of type KcRolesClaimsTransformer.
91-
Assert.IsInstanceOfType(claimsTransformer, typeof(KcRolesClaimsTransformer),
91+
Assert.IsInstanceOfType<KcRolesClaimsTransformer>(claimsTransformer,
9292
"Claims transformer should be of type KcRolesClaimsTransformer.");
9393
}
9494

0 commit comments

Comments
 (0)