diff --git a/.idea/.idea.blocktrust.CredentialWorkflow/.idea/dataSources.xml b/.idea/.idea.blocktrust.CredentialWorkflow/.idea/dataSources.xml new file mode 100644 index 0000000..04da6d6 --- /dev/null +++ b/.idea/.idea.blocktrust.CredentialWorkflow/.idea/dataSources.xml @@ -0,0 +1,13 @@ + + + + + postgresql + true + true + org.postgresql.Driver + jdbc:postgresql://localhost/WorkflowDatabase?password=Post%400DB&user=postgres + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/Blocktrust.CredentialWorkflow.Core.Tests/Blocktrust.CredentialWorkflow.Core.Tests.csproj b/Blocktrust.CredentialWorkflow.Core.Tests/Blocktrust.CredentialWorkflow.Core.Tests.csproj index c146e01..5f225c1 100644 --- a/Blocktrust.CredentialWorkflow.Core.Tests/Blocktrust.CredentialWorkflow.Core.Tests.csproj +++ b/Blocktrust.CredentialWorkflow.Core.Tests/Blocktrust.CredentialWorkflow.Core.Tests.csproj @@ -7,6 +7,7 @@ false true + 25eb5ae8-7347-4b40-b985-c958a0668b00 diff --git a/Blocktrust.CredentialWorkflow.Core.Tests/Commands/IssuingKeyTests/CreateIssuingKeyHandlerTests.cs b/Blocktrust.CredentialWorkflow.Core.Tests/Commands/IssuingKeyTests/CreateIssuingKeyHandlerTests.cs new file mode 100644 index 0000000..f629af4 --- /dev/null +++ b/Blocktrust.CredentialWorkflow.Core.Tests/Commands/IssuingKeyTests/CreateIssuingKeyHandlerTests.cs @@ -0,0 +1,188 @@ +using Blocktrust.CredentialWorkflow.Core.Commands.Tenant.CreateIssuingKey; +using Blocktrust.CredentialWorkflow.Core.Commands.Tenant.CreateTenant; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +// using Blocktrust.CredentialWorkflow.Core.Commands.IssuingKey; +// using Blocktrust.CredentialWorkflow.Core.Commands.Tenant; + +namespace Blocktrust.CredentialWorkflow.Core.Tests.Commands.IssuingKeyTests; + +public class CreateIssuingKeyHandlerTests : TestSetup +{ + private readonly CreateIssuingKeyHandler _handler; + private readonly CreateTenantHandler _createTenantHandler; + private readonly DataContext _dataContext; + + public CreateIssuingKeyHandlerTests(TransactionalTestDatabaseFixture fixture) : base(fixture) + { + _dataContext = fixture.CreateContext(); + _handler = new CreateIssuingKeyHandler(_dataContext); + _createTenantHandler = new CreateTenantHandler(_dataContext); + } + + [Fact] + public async Task Handle_ValidRequest_ShouldSucceed() + { + // Arrange + var tenantResult = await _createTenantHandler.Handle(new CreateTenantRequest("TestTenant"), CancellationToken.None); + tenantResult.IsSuccess.Should().BeTrue(); + var tenantId = tenantResult.Value; + + var request = new CreateIssuingKeyRequest( + tenantId, + "TestKey", + "did:prism:test123", + "secp256k1", + "publicKeyTest", + "privateKeyTest"); + + // Act + var result = await _handler.Handle(request, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().NotBeNull(); + result.Value.Name.Should().Be("TestKey"); + result.Value.Did.Should().Be("did:prism:test123"); + result.Value.KeyType.Should().Be("secp256k1"); + result.Value.PublicKey.Should().Be("publicKeyTest"); + result.Value.PrivateKey.Should().Be("privateKeyTest"); + + // Verify database state + var issuingKey = await _dataContext.IssuingKeys + .FirstOrDefaultAsync(i => i.TenantEntityId == tenantId); + issuingKey.Should().NotBeNull(); + issuingKey!.Name.Should().Be("TestKey"); + issuingKey.Did.Should().Be("did:prism:test123"); + issuingKey.KeyType.Should().Be("secp256k1"); + issuingKey.PublicKey.Should().Be("publicKeyTest"); + issuingKey.PrivateKey.Should().Be("privateKeyTest"); + } + + [Fact] + public async Task Handle_NonExistentTenant_ShouldFail() + { + // Arrange + var request = new CreateIssuingKeyRequest( + Guid.NewGuid(), + "TestKey", + "did:prism:test123", + "secp256k1", + "publicKeyTest", + "privateKeyTest"); + + // Act + var result = await _handler.Handle(request, CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle() + .Which.Message.Should().Be("The tenant does not exist in the database. The IssuingKey cannot be created."); + } + + [Theory] + [InlineData("", "did:prism:test", "secp256k1", "pub", "priv")] + [InlineData("TestKey", "", "secp256k1", "pub", "priv")] + [InlineData("TestKey", "did:prism:test", "", "pub", "priv")] + [InlineData("TestKey", "did:prism:test", "secp256k1", "", "priv")] + [InlineData("TestKey", "did:prism:test", "secp256k1", "pub", "")] + public async Task Handle_WithEmptyValues_ShouldCreateSuccessfully( + string name, string did, string keyType, string publicKey, string privateKey) + { + // Arrange + var tenantResult = await _createTenantHandler.Handle(new CreateTenantRequest("TestTenant"), CancellationToken.None); + tenantResult.IsSuccess.Should().BeTrue(); + var tenantId = tenantResult.Value; + + var request = new CreateIssuingKeyRequest( + tenantId, + name, + did, + keyType, + publicKey, + privateKey); + + // Act + var result = await _handler.Handle(request, CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().NotBeNull(); + result.Value.Name.Should().Be(name); + result.Value.Did.Should().Be(did); + result.Value.KeyType.Should().Be(keyType); + result.Value.PublicKey.Should().Be(publicKey); + result.Value.PrivateKey.Should().Be(privateKey); + } + + [Fact] + public async Task Handle_MultipleKeysForSameTenant_ShouldSucceed() + { + // Arrange + var tenantResult = await _createTenantHandler.Handle(new CreateTenantRequest("TestTenant"), CancellationToken.None); + tenantResult.IsSuccess.Should().BeTrue(); + var tenantId = tenantResult.Value; + + var request1 = new CreateIssuingKeyRequest( + tenantId, + "TestKey1", + "did:prism:test1", + "secp256k1", + "publicKey1", + "privateKey1"); + + var request2 = new CreateIssuingKeyRequest( + tenantId, + "TestKey2", + "did:prism:test2", + "secp256k1", + "publicKey2", + "privateKey2"); + + // Act + var result1 = await _handler.Handle(request1, CancellationToken.None); + var result2 = await _handler.Handle(request2, CancellationToken.None); + + // Assert + result1.IsSuccess.Should().BeTrue(); + result2.IsSuccess.Should().BeTrue(); + + var issuingKeys = await _dataContext.IssuingKeys + .Where(i => i.TenantEntityId == tenantId) + .ToListAsync(); + + issuingKeys.Should().HaveCount(2); + issuingKeys.Should().Contain(k => k.Name == "TestKey1" && k.Did == "did:prism:test1"); + issuingKeys.Should().Contain(k => k.Name == "TestKey2" && k.Did == "did:prism:test2"); + } + + // [Fact] + // public async Task Handle_WithLongValues_ShouldSucceed() + // { + // // Arrange + // var tenantResult = await _createTenantHandler.Handle(new CreateTenantRequest("TestTenant"), CancellationToken.None); + // tenantResult.IsSuccess.Should().BeTrue(); + // var tenantId = tenantResult.Value; + // + // var longString = new string('x', 1000); + // var request = new CreateIssuingKeyRequest( + // tenantId, + // longString, + // longString, + // longString, + // longString, + // longString); + // + // // Act + // var result = await _handler.Handle(request, CancellationToken.None); + // + // // Assert + // result.IsSuccess.Should().BeTrue(); + // result.Value.Should().NotBeNull(); + // result.Value.Name.Should().Be(longString); + // result.Value.Did.Should().Be(longString); + // result.Value.KeyType.Should().Be(longString); + // result.Value.PublicKey.Should().Be(longString); + // result.Value.PrivateKey.Should().Be(longString); + // } +} \ No newline at end of file diff --git a/Blocktrust.CredentialWorkflow.Core.Tests/Commands/IssuingKeyTests/GetIssuingKeyByIdHandlerTests.cs b/Blocktrust.CredentialWorkflow.Core.Tests/Commands/IssuingKeyTests/GetIssuingKeyByIdHandlerTests.cs new file mode 100644 index 0000000..fef3e6b --- /dev/null +++ b/Blocktrust.CredentialWorkflow.Core.Tests/Commands/IssuingKeyTests/GetIssuingKeyByIdHandlerTests.cs @@ -0,0 +1,183 @@ +using Blocktrust.CredentialWorkflow.Core.Commands.Tenant.CreateIssuingKey; +using Blocktrust.CredentialWorkflow.Core.Commands.Tenant.CreateTenant; +using Blocktrust.CredentialWorkflow.Core.Commands.Tenant.GetIssungKeyById; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; + + +namespace Blocktrust.CredentialWorkflow.Core.Tests.Commands.IssuingKeyTests; + +public class GetIssuingKeyByIdHandlerTests : TestSetup +{ + private readonly GetIssuingKeyByIdHandler _handler; + private readonly CreateIssuingKeyHandler _createIssuingKeyHandler; + private readonly CreateTenantHandler _createTenantHandler; + private readonly DataContext _dataContext; + + public GetIssuingKeyByIdHandlerTests(TransactionalTestDatabaseFixture fixture) : base(fixture) + { + _dataContext = fixture.CreateContext(); + _handler = new GetIssuingKeyByIdHandler(_dataContext); + _createIssuingKeyHandler = new CreateIssuingKeyHandler(_dataContext); + _createTenantHandler = new CreateTenantHandler(_dataContext); + } + + [Fact] + public async Task Handle_NonExistentKeyId_ShouldReturnFailure() + { + // Arrange + var nonExistentKeyId = Guid.NewGuid(); + var request = new GetIssuingKeyByIdRequest(nonExistentKeyId); + + // Act + var result = await _handler.Handle(request, CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle() + .Which.Message.Should().Be("Issuing key not found."); + + var keyInDb = await _dataContext.IssuingKeys.FindAsync(nonExistentKeyId); + keyInDb.Should().BeNull(); + } + + [Fact] + public async Task Handle_MultipleKeysForSameTenant_ShouldReturnCorrectKey() + { + // Arrange + var tenantResult = await _createTenantHandler.Handle( + new CreateTenantRequest("TestTenant"), + CancellationToken.None); + tenantResult.IsSuccess.Should().BeTrue(); + var tenantId = tenantResult.Value; + + // Create multiple keys for the same tenant + var key1Result = await _createIssuingKeyHandler.Handle( + new CreateIssuingKeyRequest( + tenantId, + "TestKey1", + "did:prism:test1", + "secp256k1", + "publicKey1", + "privateKey1"), + CancellationToken.None); + + var key2Result = await _createIssuingKeyHandler.Handle( + new CreateIssuingKeyRequest( + tenantId, + "TestKey2", + "did:prism:test2", + "secp256k1", + "publicKey2", + "privateKey2"), + CancellationToken.None); + + var key3Result = await _createIssuingKeyHandler.Handle( + new CreateIssuingKeyRequest( + tenantId, + "TestKey3", + "did:prism:test3", + "secp256k1", + "publicKey3", + "privateKey3"), + CancellationToken.None); + + // Verify all keys were created successfully + key1Result.IsSuccess.Should().BeTrue(); + key2Result.IsSuccess.Should().BeTrue(); + key3Result.IsSuccess.Should().BeTrue(); + + // Act - retrieve the second key + var result = await _handler.Handle( + new GetIssuingKeyByIdRequest(key2Result.Value.IssuingKeyId), + CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().NotBeNull(); + result.Value.IssuingKeyId.Should().Be(key2Result.Value.IssuingKeyId); + result.Value.Name.Should().Be("TestKey2"); + result.Value.Did.Should().Be("did:prism:test2"); + + // Verify all keys exist in database + var keysInDb = await _dataContext.IssuingKeys + .Where(k => k.TenantEntityId == tenantId) + .ToListAsync(); + keysInDb.Should().HaveCount(3); + } + + [Fact] + public async Task Handle_KeyDataIntegrity_ShouldPreserveAllFields() + { + // Arrange + var tenantResult = await _createTenantHandler.Handle( + new CreateTenantRequest("TestTenant"), + CancellationToken.None); + tenantResult.IsSuccess.Should().BeTrue(); + + // Create a key with specific test data + var complexData = new + { + Name = "Complex Key Name with Spaces and Special Chars !@#$", + Did = "did:prism:test:with:multiple:colons:and:special:chars:!@#$", + KeyType = "secp256k1WithSpecialParams!@#$", + PublicKey = new string('A', 1000), // Long public key + PrivateKey = new string('B', 1000) // Long private key + }; + + var createKeyResult = await _createIssuingKeyHandler.Handle( + new CreateIssuingKeyRequest( + tenantResult.Value, + complexData.Name, + complexData.Did, + complexData.KeyType, + complexData.PublicKey, + complexData.PrivateKey), + CancellationToken.None); + createKeyResult.IsSuccess.Should().BeTrue(); + + // Act + var result = await _handler.Handle( + new GetIssuingKeyByIdRequest(createKeyResult.Value.IssuingKeyId), + CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().NotBeNull(); + + // Verify all fields maintain exact data integrity + result.Value.Name.Should().Be(complexData.Name); + result.Value.Did.Should().Be(complexData.Did); + result.Value.KeyType.Should().Be(complexData.KeyType); + result.Value.PublicKey.Should().Be(complexData.PublicKey); + result.Value.PrivateKey.Should().Be(complexData.PrivateKey); + + // Verify length and content of long fields + result.Value.PublicKey.Length.Should().Be(1000); + result.Value.PrivateKey.Length.Should().Be(1000); + + // Verify data integrity in database + var keyInDb = await _dataContext.IssuingKeys.FindAsync(result.Value.IssuingKeyId); + keyInDb.Should().NotBeNull(); + keyInDb!.Name.Should().Be(complexData.Name); + keyInDb.Did.Should().Be(complexData.Did); + keyInDb.KeyType.Should().Be(complexData.KeyType); + keyInDb.PublicKey.Should().Be(complexData.PublicKey); + keyInDb.PrivateKey.Should().Be(complexData.PrivateKey); + } + + [Fact] + public async Task Handle_EmptyGuid_ShouldReturnFailure() + { + // Arrange + var request = new GetIssuingKeyByIdRequest(Guid.Empty); + + // Act + var result = await _handler.Handle(request, CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle() + .Which.Message.Should().Be("Issuing key not found."); + } +} \ No newline at end of file diff --git a/Blocktrust.CredentialWorkflow.Core.Tests/Commands/IssuingKeyTests/GetPrivateIssuingKeyByDidHandlerTests.cs b/Blocktrust.CredentialWorkflow.Core.Tests/Commands/IssuingKeyTests/GetPrivateIssuingKeyByDidHandlerTests.cs new file mode 100644 index 0000000..1b5fd6e --- /dev/null +++ b/Blocktrust.CredentialWorkflow.Core.Tests/Commands/IssuingKeyTests/GetPrivateIssuingKeyByDidHandlerTests.cs @@ -0,0 +1,194 @@ +using Blocktrust.CredentialWorkflow.Core.Commands.Tenant.CreateIssuingKey; +using Blocktrust.CredentialWorkflow.Core.Commands.Tenant.CreateTenant; +using Blocktrust.CredentialWorkflow.Core.Commands.Tenant.GetPrivateIssuingKeyByDid; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; + +namespace Blocktrust.CredentialWorkflow.Core.Tests.Commands.IssuingKeyTests; + +public class GetPrivateIssuingKeyByDidHandlerTests : TestSetup +{ + private readonly GetPrivateIssuingKeyByDidHandler _handler; + private readonly CreateIssuingKeyHandler _createIssuingKeyHandler; + private readonly CreateTenantHandler _createTenantHandler; + private readonly DataContext _dataContext; + + public GetPrivateIssuingKeyByDidHandlerTests(TransactionalTestDatabaseFixture fixture) : base(fixture) + { + _dataContext = fixture.CreateContext(); + _handler = new GetPrivateIssuingKeyByDidHandler(_dataContext); + _createIssuingKeyHandler = new CreateIssuingKeyHandler(_dataContext); + _createTenantHandler = new CreateTenantHandler(_dataContext); + } + + [Fact] + public async Task Handle_NonExistentDid_ShouldReturnFailure() + { + // Arrange + var nonExistentDid = "did:prism:nonexistent123"; + var request = new GetPrivateIssuingKeyByDidRequest(nonExistentDid); + + // Act + var result = await _handler.Handle(request, CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle() + .Which.Message.Should().Be("Issuing key not found for the provided DID."); + + var keyInDb = await _dataContext.IssuingKeys + .FirstOrDefaultAsync(k => k.Did == nonExistentDid); + keyInDb.Should().BeNull(); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData("not-a-did")] + [InlineData("did:")] + [InlineData("did:invalidformat")] + [InlineData("prism:123")] + public async Task Handle_InvalidDidFormat_ShouldReturnFailure(string invalidDid) + { + // Arrange + var request = new GetPrivateIssuingKeyByDidRequest(invalidDid); + + // Act + var result = await _handler.Handle(request, CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle() + .Which.Message.Should().Be("Issuing key not found for the provided DID."); + } + + [Fact] + public async Task Handle_MultipleDidKeys_ShouldReturnCorrectPrivateKey() + { + // Arrange + var tenantResult = await _createTenantHandler.Handle( + new CreateTenantRequest("TestTenant"), + CancellationToken.None); + tenantResult.IsSuccess.Should().BeTrue(); + + // Create multiple keys with different DIDs + var testData = new[] + { + ("TestKey1", "did:prism:test1", "privateKey1"), + ("TestKey2", "did:prism:test2", "privateKey2"), + ("TestKey3", "did:prism:test3", "privateKey3") + }; + + foreach (var (name, did, privateKey) in testData) + { + var createResult = await _createIssuingKeyHandler.Handle( + new CreateIssuingKeyRequest( + tenantResult.Value, + name, + did, + "secp256k1", + "publicKey", + privateKey), + CancellationToken.None); + createResult.IsSuccess.Should().BeTrue(); + } + + // Verify each private key can be retrieved correctly + foreach (var (_, did, expectedPrivateKey) in testData) + { + // Act + var result = await _handler.Handle( + new GetPrivateIssuingKeyByDidRequest(did), + CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().Be(expectedPrivateKey); + } + + // Verify database state + var keysInDb = await _dataContext.IssuingKeys + .Where(k => k.TenantEntityId == tenantResult.Value) + .ToListAsync(); + keysInDb.Should().HaveCount(3); + } + + [Fact] + public async Task Handle_DuplicateDidKeys_ShouldReturnFirstMatch() + { + // Arrange + var tenantResult = await _createTenantHandler.Handle( + new CreateTenantRequest("TestTenant"), + CancellationToken.None); + tenantResult.IsSuccess.Should().BeTrue(); + + // Create multiple keys with same DID but different private keys + var sameDid = "did:prism:duplicate"; + var keys = new[] + { + ("Key1", "privateKey1"), + ("Key2", "privateKey2") + }; + + foreach (var (name, privateKey) in keys) + { + var createResult = await _createIssuingKeyHandler.Handle( + new CreateIssuingKeyRequest( + tenantResult.Value, + name, + sameDid, + "secp256k1", + "publicKey", + privateKey), + CancellationToken.None); + createResult.IsSuccess.Should().BeTrue(); + } + + // Act + var result = await _handler.Handle( + new GetPrivateIssuingKeyByDidRequest(sameDid), + CancellationToken.None); + + // Assert + result.IsSuccess.Should().BeTrue(); + result.Value.Should().Be("privateKey1"); // Should return first matching key + + // Verify both keys exist in database + var keysInDb = await _dataContext.IssuingKeys + .Where(k => k.Did == sameDid) + .ToListAsync(); + keysInDb.Should().HaveCount(2); + } + + [Fact] + public async Task Handle_CaseSensitiveDid_ShouldNotMatchDifferentCase() + { + // Arrange + var tenantResult = await _createTenantHandler.Handle( + new CreateTenantRequest("TestTenant"), + CancellationToken.None); + tenantResult.IsSuccess.Should().BeTrue(); + + var did = "did:prism:TEST123"; + var createResult = await _createIssuingKeyHandler.Handle( + new CreateIssuingKeyRequest( + tenantResult.Value, + "TestKey", + did, + "secp256k1", + "publicKey", + "privateKey"), + CancellationToken.None); + createResult.IsSuccess.Should().BeTrue(); + + // Act + var result = await _handler.Handle( + new GetPrivateIssuingKeyByDidRequest(did.ToLower()), + CancellationToken.None); + + // Assert + result.IsFailed.Should().BeTrue(); + result.Errors.Should().ContainSingle() + .Which.Message.Should().Be("Issuing key not found for the provided DID."); + } +} \ No newline at end of file diff --git a/Blocktrust.CredentialWorkflow.Core.Tests/TransactionalTestDatabaseFixture.cs b/Blocktrust.CredentialWorkflow.Core.Tests/TransactionalTestDatabaseFixture.cs index a2a425b..77d36fc 100644 --- a/Blocktrust.CredentialWorkflow.Core.Tests/TransactionalTestDatabaseFixture.cs +++ b/Blocktrust.CredentialWorkflow.Core.Tests/TransactionalTestDatabaseFixture.cs @@ -6,7 +6,7 @@ public class TransactionalTestDatabaseFixture { - private const string ConnectionString = @"Host=10.10.20.103; Database=CredentialWorkflowTests; Username=postgres; Password=postgres"; + private const string ConnectionString = @"Host=localhost; Database=CredentialWorkflowTests; Username=postgres; Password=Post@0DB"; public DataContext CreateContext() => new DataContext( diff --git a/Blocktrust.CredentialWorkflow.Web/appsettings.json b/Blocktrust.CredentialWorkflow.Web/appsettings.json index e786451..8caafc1 100644 --- a/Blocktrust.CredentialWorkflow.Web/appsettings.json +++ b/Blocktrust.CredentialWorkflow.Web/appsettings.json @@ -4,7 +4,7 @@ "SendGridFromEmail": "system@blocktrust.dev" }, "ConnectionStrings": { - "DefaultConnection": "Host=10.10.20.103; Database=WorkflowDatabase; Username=postgres; Password=postgres" + "DefaultConnection": "Host=localhost; Database=WorkflowDatabase; Username=postgres; Password=Post@0DB" }, "CredentialSettings": { "DefaultIssuerDid": "did:prism:your_default_issuer",