using ColaFlow.Modules.Mcp.Domain.Entities; using ColaFlow.Modules.Mcp.Domain.ValueObjects; using FluentAssertions; using Xunit; namespace ColaFlow.Modules.Mcp.Tests.Domain; public class McpApiKeyTests { [Fact] public void Create_ShouldGenerateValidApiKey() { // Arrange var name = "Test API Key"; var tenantId = Guid.NewGuid(); var userId = Guid.NewGuid(); var permissions = ApiKeyPermissions.ReadOnly(); // Act var (apiKey, plainKey) = McpApiKey.Create(name, tenantId, userId, permissions); // Assert apiKey.Should().NotBeNull(); apiKey.Name.Should().Be(name); apiKey.TenantId.Should().Be(tenantId); apiKey.UserId.Should().Be(userId); apiKey.Status.Should().Be(ApiKeyStatus.Active); plainKey.Should().StartWith("cola_"); plainKey.Length.Should().Be(41); // "cola_" (5) + 36 random chars apiKey.KeyPrefix.Should().Be(plainKey.Substring(0, 12)); } [Fact] public void Create_ShouldHashApiKey() { // Arrange var name = "Test API Key"; var tenantId = Guid.NewGuid(); var userId = Guid.NewGuid(); var permissions = ApiKeyPermissions.ReadOnly(); // Act var (apiKey, plainKey) = McpApiKey.Create(name, tenantId, userId, permissions); // Assert apiKey.KeyHash.Should().NotBeNullOrEmpty(); apiKey.KeyHash.Should().NotBe(plainKey); // Hash should not equal plain key apiKey.VerifyKey(plainKey).Should().BeTrue(); // But should verify correctly } [Fact] public void Create_WithInvalidName_ShouldThrowArgumentException() { // Arrange var tenantId = Guid.NewGuid(); var userId = Guid.NewGuid(); var permissions = ApiKeyPermissions.ReadOnly(); // Act & Assert Assert.Throws(() => McpApiKey.Create("", tenantId, userId, permissions)); } [Fact] public void Create_WithInvalidTenantId_ShouldThrowArgumentException() { // Arrange var userId = Guid.NewGuid(); var permissions = ApiKeyPermissions.ReadOnly(); // Act & Assert Assert.Throws(() => McpApiKey.Create("Test", Guid.Empty, userId, permissions)); } [Fact] public void VerifyKey_WithCorrectKey_ShouldReturnTrue() { // Arrange var (apiKey, plainKey) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly()); // Act var result = apiKey.VerifyKey(plainKey); // Assert result.Should().BeTrue(); } [Fact] public void VerifyKey_WithIncorrectKey_ShouldReturnFalse() { // Arrange var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly()); // Act var result = apiKey.VerifyKey("cola_wrongkey123456789012345678901234"); // Assert result.Should().BeFalse(); } [Fact] public void Revoke_ShouldMarkAsRevoked() { // Arrange var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly()); var revokedBy = Guid.NewGuid(); // Act apiKey.Revoke(revokedBy); // Assert apiKey.Status.Should().Be(ApiKeyStatus.Revoked); apiKey.RevokedAt.Should().NotBeNull(); apiKey.RevokedBy.Should().Be(revokedBy); } [Fact] public void Revoke_WhenAlreadyRevoked_ShouldThrowInvalidOperationException() { // Arrange var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly()); apiKey.Revoke(Guid.NewGuid()); // Act & Assert Assert.Throws(() => apiKey.Revoke(Guid.NewGuid())); } [Fact] public void IsExpired_WhenNotExpired_ShouldReturnFalse() { // Arrange var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly(), expirationDays: 90); // Act var result = apiKey.IsExpired(); // Assert result.Should().BeFalse(); } [Fact] public void IsValid_WhenActiveAndNotExpired_ShouldReturnTrue() { // Arrange var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly()); // Act var result = apiKey.IsValid(); // Assert result.Should().BeTrue(); } [Fact] public void IsValid_WhenRevoked_ShouldReturnFalse() { // Arrange var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly()); apiKey.Revoke(Guid.NewGuid()); // Act var result = apiKey.IsValid(); // Assert result.Should().BeFalse(); } [Fact] public void RecordUsage_ShouldIncrementUsageCount() { // Arrange var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly()); var initialCount = apiKey.UsageCount; // Act apiKey.RecordUsage(); // Assert apiKey.UsageCount.Should().Be(initialCount + 1); apiKey.LastUsedAt.Should().NotBeNull(); } [Fact] public void IsIpAllowed_WithNoWhitelist_ShouldReturnTrue() { // Arrange var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), ApiKeyPermissions.ReadOnly()); // Act var result = apiKey.IsIpAllowed("192.168.1.1"); // Assert result.Should().BeTrue(); } [Fact] public void IsIpAllowed_WithWhitelist_ShouldValidateCorrectly() { // Arrange var permissions = ApiKeyPermissions.ReadOnly(); var ipWhitelist = new List { "192.168.1.1", "10.0.0.1" }; var (apiKey, _) = McpApiKey.Create("Test", Guid.NewGuid(), Guid.NewGuid(), permissions, ipWhitelist: ipWhitelist); // Act & Assert apiKey.IsIpAllowed("192.168.1.1").Should().BeTrue(); apiKey.IsIpAllowed("10.0.0.1").Should().BeTrue(); apiKey.IsIpAllowed("172.16.0.1").Should().BeFalse(); } }