using NUnit.Framework; using System; using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Text.Json.Serialization; namespace SimpleIdp.Tests.Configuration; public class ConfigurationTests { private const string TestConfigPath = "test-config.json"; [TearDown] public void Cleanup() { if (File.Exists(TestConfigPath)) File.Delete(TestConfigPath); } [Test] public void Deserialize_ValidConfig_ReturnsConfig() { var config = new IdpConfig( Api: new ApiConfig("api", "api-audience"), Scope: new ScopeConfig("scope", "description", new List()), Client: new ClientConfig("client-id", "client-name", new List { "http://redirect" }), Users: new List() ); var json = SerializeConfig(config); File.WriteAllText(TestConfigPath, json); var result = LoadConfigFromFile(TestConfigPath); Assert.IsNotNull(result); Assert.That(result.Api.Name, Is.EqualTo(config.Api.Name)); Assert.That(result.Scope.Name, Is.EqualTo(config.Scope.Name)); Assert.That(result.Client.ClientId, Is.EqualTo(config.Client.ClientId)); } [Test] public void Deserialize_WithClaimMappers_ParsesClaimMappersCorrectly() { var claimMappers = new List { new ClaimMapperConfig("claim1", "source1", "string"), new ClaimMapperConfig("claim2", "source2", "int", true) }; var config = new IdpConfig( Api: new ApiConfig("api", "audience"), Scope: new ScopeConfig("scope", "desc", claimMappers), Client: new ClientConfig("client", "name", new List()), Users: new List() ); var json = SerializeConfig(config); File.WriteAllText(TestConfigPath, json); var result = LoadConfigFromFile(TestConfigPath); Assert.IsNotNull(result.Scope.ClaimMappers); Assert.That(result.Scope.ClaimMappers.Count, Is.EqualTo(2)); Assert.That(result.Scope.ClaimMappers[0].TargetClaimPath, Is.EqualTo("claim1")); Assert.That(result.Scope.ClaimMappers[0].SourceUserAttribute, Is.EqualTo("source1")); Assert.That(result.Scope.ClaimMappers[0].TokenClaimJsonType, Is.EqualTo("string")); Assert.That(result.Scope.ClaimMappers[1].TargetClaimPath, Is.EqualTo("claim2")); Assert.IsTrue(result.Scope.ClaimMappers[1].IsMultiValued); } [Test] public void Deserialize_WithUsers_ParsesUsersCorrectly() { var users = new List { new UserConfig( "user1", "pass1", "User One", "user1@test.com", "User", true, new List { "admin" }, new Dictionary { { "role", "admin" } }, new List() ) }; var config = new IdpConfig( Api: new ApiConfig("api", "audience"), Scope: new ScopeConfig("scope", "desc", new List()), Client: new ClientConfig("client", "name", new List()), Users: users ); var json = SerializeConfig(config); File.WriteAllText(TestConfigPath, json); var result = LoadConfigFromFile(TestConfigPath); Assert.IsNotNull(result.Users); Assert.That(result.Users.Count, Is.EqualTo(1)); Assert.That(result.Users[0].Login, Is.EqualTo("user1")); Assert.That(result.Users[0].Password, Is.EqualTo("pass1")); Assert.That(result.Users[0].Name, Is.EqualTo("User One")); Assert.That(result.Users[0].Email, Is.EqualTo("user1@test.com")); Assert.IsTrue(result.Users[0].EmailVerified); Assert.That(result.Users[0].Roles.Count, Is.EqualTo(1)); Assert.That(result.Users[0].Roles[0], Is.EqualTo("admin")); } [Test] public void Deserialize_WithConsents_ParsesConsentsCorrectly() { var consents = new List { new ConsentConfig("realm1", "client1", "scope1") }; var users = new List { new UserConfig( "user1", "pass1", "User One", "user1@test.com", "User", true, new List(), new Dictionary(), consents ) }; var config = new IdpConfig( Api: new ApiConfig("api", "audience"), Scope: new ScopeConfig("scope", "desc", new List()), Client: new ClientConfig("client", "name", new List()), Users: users ); var json = SerializeConfig(config); File.WriteAllText(TestConfigPath, json); var result = LoadConfigFromFile(TestConfigPath); Assert.IsNotNull(result.Users[0].Consents); Assert.That(result.Users[0].Consents.Count, Is.EqualTo(1)); Assert.That(result.Users[0].Consents[0].Realm, Is.EqualTo("realm1")); Assert.That(result.Users[0].Consents[0].ClientId, Is.EqualTo("client1")); Assert.That(result.Users[0].Consents[0].Scope, Is.EqualTo("scope1")); } [Test] public void Deserialize_CaseInsensitive_ParsesCorrectly() { var json = """ { "api": { "name": "test-api", "audience": "test-audience" }, "scope": { "name": "test-scope", "description": "test-desc", "claimmappers": [] }, "client": { "clientid": "test-client", "name": "test client", "redirecturis": ["http://test.com"], "ispublic": false }, "users": [] } """; File.WriteAllText(TestConfigPath, json); var result = LoadConfigFromFile(TestConfigPath); Assert.IsNotNull(result); Assert.That(result.Api.Name, Is.EqualTo("test-api")); Assert.That(result.Client.ClientId, Is.EqualTo("test-client")); Assert.IsFalse(result.Client.IsPublic); } [Test] public void Deserialize_MissingApiConfig_ReturnsNullApi() { var json = """ { "scope": { "name": "scope", "description": "desc", "claimmappers": [] }, "client": { "clientid": "client", "name": "name", "redirecturis": [] }, "users": [] } """; File.WriteAllText(TestConfigPath, json); var result = LoadConfigFromFile(TestConfigPath); Assert.IsNull(result.Api); } [Test] public void Deserialize_MissingScopeConfig_ReturnsNullScope() { var json = """ { "api": { "name": "api", "audience": "audience" }, "client": { "clientid": "client", "name": "name", "redirecturis": [] }, "users": [] } """; File.WriteAllText(TestConfigPath, json); var result = LoadConfigFromFile(TestConfigPath); Assert.IsNull(result.Scope); } [Test] public void Deserialize_MissingClientConfig_ReturnsNullClient() { var json = """ { "api": { "name": "api", "audience": "audience" }, "scope": { "name": "scope", "description": "desc", "claimmappers": [] }, "users": [] } """; File.WriteAllText(TestConfigPath, json); var result = LoadConfigFromFile(TestConfigPath); Assert.IsNull(result.Client); } [Test] public void Deserialize_EmptyUsersList_ParsesSuccessfully() { var config = new IdpConfig( Api: new ApiConfig("api", "audience"), Scope: new ScopeConfig("scope", "desc", new List()), Client: new ClientConfig("client", "name", new List()), Users: new List() ); var json = SerializeConfig(config); File.WriteAllText(TestConfigPath, json); var result = LoadConfigFromFile(TestConfigPath); Assert.IsNotNull(result.Users); Assert.That(result.Users, Is.Empty); } [Test] public void Deserialize_MultipleUsers_ParsesAllUsers() { var users = new List { new UserConfig("user1", "pass1", "User 1", "user1@test.com", "User", true, new List(), new Dictionary(), new List()), new UserConfig("user2", "pass2", "User 2", "user2@test.com", "User", false, new List(), new Dictionary(), new List()), new UserConfig("user3", "pass3", "User 3", "user3@test.com", "User", true, new List(), new Dictionary(), new List()) }; var config = new IdpConfig( Api: new ApiConfig("api", "audience"), Scope: new ScopeConfig("scope", "desc", new List()), Client: new ClientConfig("client", "name", new List()), Users: users ); var json = SerializeConfig(config); File.WriteAllText(TestConfigPath, json); var result = LoadConfigFromFile(TestConfigPath); Assert.That(result.Users.Count, Is.EqualTo(3)); } [Test] public void Deserialize_ClientWithMultipleRedirectUris_ParsesAllUris() { var redirectUris = new List { "http://redirect1.com", "http://redirect2.com", "https://redirect3.com" }; var config = new IdpConfig( Api: new ApiConfig("api", "audience"), Scope: new ScopeConfig("scope", "desc", new List()), Client: new ClientConfig("client", "name", redirectUris), Users: new List() ); var json = SerializeConfig(config); File.WriteAllText(TestConfigPath, json); var result = LoadConfigFromFile(TestConfigPath); Assert.That(result.Client.RedirectUris.Count, Is.EqualTo(3)); Assert.That(result.Client.RedirectUris, Contains.Item("http://redirect1.com")); Assert.That(result.Client.RedirectUris, Contains.Item("http://redirect2.com")); Assert.That(result.Client.RedirectUris, Contains.Item("https://redirect3.com")); } private string SerializeConfig(IdpConfig config) { var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true }; return JsonSerializer.Serialize(config, options); } private IdpConfig LoadConfigFromFile(string path) { var json = File.ReadAllText(path); var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; return JsonSerializer.Deserialize(json, options) ?? throw new InvalidOperationException("Failed to deserialize config"); } } public record IdpConfig( ApiConfig Api, ScopeConfig Scope, ClientConfig Client, List Users); public record ApiConfig( string Name, string Audience); public record ScopeConfig( string Name, string Description, List ClaimMappers); public record ClaimMapperConfig( string TargetClaimPath, string SourceUserAttribute, string TokenClaimJsonType, bool IsMultiValued = false); public record ClientConfig( string ClientId, string Name, List RedirectUris, bool IsPublic = true); public record UserConfig( string Login, string Password, string Name, string Email, string Firstname, bool EmailVerified, List Roles, Dictionary Claims, List Consents); public record ConsentConfig( string Realm, string ClientId, string Scope);