using FluentResults; using IdentityShroud.Core.Contracts; using IdentityShroud.Core.EFCore; using IdentityShroud.Core.Model; using IdentityShroud.Core.Security.Keys; using IdentityShroud.Core.Services; using IdentityShroud.Core.Tests.Fixtures; using IdentityShroud.TestUtils.Substitutes; using Microsoft.EntityFrameworkCore; using Shouldly; namespace IdentityShroud.Core.Tests.Services; public class RealmServiceTests : IClassFixture { private readonly DbFixture _dbFixture; private readonly IKeyService _keyService = Substitute.For(); private readonly IDekEncryptionService _dekCryptor = new NullDekEncryptionService(); public RealmServiceTests(DbFixture dbFixture) { _dbFixture = dbFixture; using Db db = dbFixture.CreateDbContext(); if (!db.Database.EnsureCreated()) TruncateTables(db); } private void TruncateTables(Db db) { db.Database.ExecuteSqlRaw("TRUNCATE realm CASCADE;"); } private RealmService CreateSut(Db db) => new(db, _keyService, _dekCryptor, new ClockService()); [Theory] [InlineData(null)] [InlineData("a7c2a39c-3ed9-4790-826e-43bb2e5e480c")] public async Task Create(string? idString) { // Setup Guid? realmId = null; if (idString is not null) realmId = new(idString); Realm? val; await using (var db = _dbFixture.CreateDbContext()) { _keyService.CreateKey(Arg.Any()) .Returns(new CreateKeyResponse(KeyType.AES, new KeyData([21]))); // Act RealmService sut = CreateSut(db); Result response = await sut.Create( new(realmId, "slug", "New realm"), TestContext.Current.CancellationToken); // Verify val = ResultAssert.Success(response); if (realmId.HasValue) Assert.Equal(realmId, val.Id); else Assert.NotEqual(Guid.Empty, val.Id); Assert.Multiple( () => val.Slug.ShouldBe("slug"), () => val.Name.ShouldBe("New realm"), () => val.DataEncryptionKeys.ShouldContain(d => d.Active), () => val.TokenSigningKeys.ShouldContain(d => !d.RevokedAt.HasValue) ); _keyService.Received().CreateKey(Arg.Any()); } await using (var db = _dbFixture.CreateDbContext()) { var dbRecord = await db.Realms .Include(e => e.TokenSigningKeys) .SingleAsync(e => e.Id == val.Id, TestContext.Current.CancellationToken); Assert.Equal(KeyType.AES, dbRecord.TokenSigningKeys[0].KeyType); } } [Theory] [InlineData("slug", null)] [InlineData("foo", "Foo")] public async Task FindBySlug(string slug, string? name) { await using (var setupContext = _dbFixture.CreateDbContext()) { setupContext.Realms.Add(new() { Slug = "foo", Name = "Foo", }); setupContext.Realms.Add(new() { Slug = "bar", Name = "Bar", }); await setupContext.SaveChangesAsync(TestContext.Current.CancellationToken); } await using var actContext = _dbFixture.CreateDbContext(); // Act RealmService sut = CreateSut(actContext); var result = await sut.FindBySlug(slug, TestContext.Current.CancellationToken); // Verify Assert.Equal(name, result?.Name); } [Theory] [InlineData("b0423bba-2411-497b-a5b6-c5adf404b862", true)] [InlineData("65ac9dba-6d43-4fa4-b57f-133ed639fbcb", false)] public async Task FindById(string idString, bool shouldFind) { Guid id = new(idString); await using (var setupContext = _dbFixture.CreateDbContext()) { setupContext.Realms.Add(new() { Id = new("b0423bba-2411-497b-a5b6-c5adf404b862"), Slug = "foo", Name = "Foo", }); setupContext.Realms.Add(new() { Id = new("d4ffc7d0-7b2c-4f02-82b9-a74610435b0d"), Slug = "bar", Name = "Bar", }); await setupContext.SaveChangesAsync(TestContext.Current.CancellationToken); } await using var actContext = _dbFixture.CreateDbContext(); // Act RealmService sut = CreateSut(actContext); Realm? result = await sut.FindById(id, TestContext.Current.CancellationToken); // Verify if (shouldFind) Assert.NotNull(result); else Assert.Null(result); } }