All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- Added Role to AuthResponse DTO and all auth endpoints - Fixed null config handling in MistralConnector, TtsService, VoskService, MistralService - Fixed BaseAddress setup in MistralConnector to work without API key - Reverted seed data to use hardcoded bcrypt hashes (compatible with PasswordHasher) - Added integration tests for StoryController - Added unit tests for MistralConnector - Updated frontend AuthResponse type to include role Fixes admin redirect to /, story generation null reference, and Docker build failures. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
257 lines
7.9 KiB
C#
257 lines
7.9 KiB
C#
using System;
|
|
using System.Net.Http;
|
|
using System.Threading.Tasks;
|
|
using GermanApp.Infrastructure.Configuration;
|
|
using GermanApp.Infrastructure.Services;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using Moq;
|
|
|
|
namespace GermanApp.Tests.Unit.Infrastructure.Services;
|
|
|
|
/// <summary>
|
|
/// Unit tests for MistralConnector.
|
|
/// Tests configuration, BaseAddress setup, and error handling.
|
|
/// </summary>
|
|
[TestClass]
|
|
public class MistralConnectorTests
|
|
{
|
|
private HttpClient _httpClient;
|
|
private Mock<ILogger<MistralConnector>> _loggerMock;
|
|
private IMemoryCache _cache;
|
|
|
|
[TestInitialize]
|
|
public void TestInitialize()
|
|
{
|
|
_httpClient = new HttpClient();
|
|
_loggerMock = new Mock<ILogger<MistralConnector>>();
|
|
_cache = new MemoryCache(new MemoryCacheOptions());
|
|
}
|
|
|
|
[TestCleanup]
|
|
public void TestCleanup()
|
|
{
|
|
_httpClient?.Dispose();
|
|
}
|
|
|
|
// ============================================
|
|
// Configuration Tests
|
|
// ============================================
|
|
|
|
[TestMethod]
|
|
public void Constructor_SetsBaseAddress_WhenValidConfigProvided()
|
|
{
|
|
// Arrange
|
|
var config = new MistralConfig
|
|
{
|
|
ApiKey = "test-api-key",
|
|
BaseUrl = "https://api.mistral.ai/v1/"
|
|
};
|
|
|
|
// Act
|
|
var connector = new MistralConnector(_httpClient, config, _loggerMock.Object, _cache);
|
|
|
|
// Assert
|
|
Assert.IsNotNull(_httpClient.BaseAddress);
|
|
Assert.AreEqual(new Uri("https://api.mistral.ai/v1/"), _httpClient.BaseAddress);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Constructor_SetsBaseAddress_WhenApiKeyIsPlaceholder()
|
|
{
|
|
// Arrange
|
|
var config = new MistralConfig
|
|
{
|
|
ApiKey = "your-mistral-api-key-here", // Placeholder key
|
|
BaseUrl = "https://api.mistral.ai/v1/"
|
|
};
|
|
|
|
// Act
|
|
var connector = new MistralConnector(_httpClient, config, _loggerMock.Object, _cache);
|
|
|
|
// Assert
|
|
// Even with a placeholder API key, BaseAddress should be set
|
|
Assert.IsNotNull(_httpClient.BaseAddress);
|
|
Assert.AreEqual(new Uri("https://api.mistral.ai/v1/"), _httpClient.BaseAddress);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Constructor_SetsBaseAddress_WhenApiKeyIsEmpty()
|
|
{
|
|
// Arrange
|
|
var config = new MistralConfig
|
|
{
|
|
ApiKey = string.Empty, // Empty API key
|
|
BaseUrl = "https://api.mistral.ai/v1/"
|
|
};
|
|
|
|
// Act
|
|
var connector = new MistralConnector(_httpClient, config, _loggerMock.Object, _cache);
|
|
|
|
// Assert
|
|
// Even without an API key, BaseAddress should be set for testing
|
|
Assert.IsNotNull(_httpClient.BaseAddress);
|
|
Assert.AreEqual(new Uri("https://api.mistral.ai/v1/"), _httpClient.BaseAddress);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Constructor_UsesDefaultConfig_WhenNullConfigProvided()
|
|
{
|
|
// Arrange & Act
|
|
var connector = new MistralConnector(_httpClient, null!, _loggerMock.Object, _cache);
|
|
|
|
// Assert
|
|
// Should use default BaseUrl from MistralConfig
|
|
Assert.IsNotNull(_httpClient.BaseAddress);
|
|
Assert.AreEqual(new Uri("https://api.mistral.ai/v1/"), _httpClient.BaseAddress);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Constructor_SetsTimeout_WhenValidConfigProvided()
|
|
{
|
|
// Arrange
|
|
var config = new MistralConfig
|
|
{
|
|
ApiKey = "test-api-key",
|
|
BaseUrl = "https://api.mistral.ai/v1/",
|
|
TimeoutSeconds = 60
|
|
};
|
|
|
|
// Act
|
|
var connector = new MistralConnector(_httpClient, config, _loggerMock.Object, _cache);
|
|
|
|
// Assert
|
|
Assert.AreEqual(TimeSpan.FromSeconds(60), _httpClient.Timeout);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Constructor_AddsAuthHeader_WhenApiKeyIsValid()
|
|
{
|
|
// Arrange
|
|
var config = new MistralConfig
|
|
{
|
|
ApiKey = "test-api-key",
|
|
BaseUrl = "https://api.mistral.ai/v1/"
|
|
};
|
|
|
|
// Act
|
|
var connector = new MistralConnector(_httpClient, config, _loggerMock.Object, _cache);
|
|
|
|
// Assert
|
|
Assert.IsTrue(_httpClient.DefaultRequestHeaders.Contains("Authorization"));
|
|
var authHeader = _httpClient.DefaultRequestHeaders.GetValues("Authorization").FirstOrDefault();
|
|
Assert.AreEqual("Bearer test-api-key", authHeader);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Constructor_DoesNotAddAuthHeader_WhenApiKeyIsPlaceholder()
|
|
{
|
|
// Arrange
|
|
var config = new MistralConfig
|
|
{
|
|
ApiKey = "your-mistral-api-key-here", // Placeholder
|
|
BaseUrl = "https://api.mistral.ai/v1/"
|
|
};
|
|
|
|
// Act
|
|
var connector = new MistralConnector(_httpClient, config, _loggerMock.Object, _cache);
|
|
|
|
// Assert
|
|
// With placeholder key, auth header should still be added (it's valid non-whitespace)
|
|
Assert.IsTrue(_httpClient.DefaultRequestHeaders.Contains("Authorization"));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Constructor_DoesNotAddAuthHeader_WhenApiKeyIsEmpty()
|
|
{
|
|
// Arrange
|
|
var config = new MistralConfig
|
|
{
|
|
ApiKey = string.Empty,
|
|
BaseUrl = "https://api.mistral.ai/v1/"
|
|
};
|
|
|
|
// Act
|
|
var connector = new MistralConnector(_httpClient, config, _loggerMock.Object, _cache);
|
|
|
|
// Assert
|
|
// With empty API key, auth header should NOT be added
|
|
Assert.IsFalse(_httpClient.DefaultRequestHeaders.Contains("Authorization"));
|
|
}
|
|
|
|
// ============================================
|
|
// Error Handling Tests
|
|
// ============================================
|
|
|
|
[TestMethod]
|
|
public void Constructor_LogsError_WhenBaseUrlIsInvalid()
|
|
{
|
|
// Arrange
|
|
var config = new MistralConfig
|
|
{
|
|
ApiKey = "test-api-key",
|
|
BaseUrl = "not-a-valid-url"
|
|
};
|
|
|
|
// Act
|
|
var connector = new MistralConnector(_httpClient, config, _loggerMock.Object, _cache);
|
|
|
|
// Assert
|
|
// Should have logged an error about invalid BaseUrl
|
|
_loggerMock.Verify(l => l.Log(
|
|
LogLevel.Error,
|
|
It.IsAny<EventId>(),
|
|
It.Is<It.IsAnyType>((v, t) => v.ToString().Contains("invalid or missing BaseUrl")),
|
|
It.IsAny<Exception>(),
|
|
It.Is<Func<It.IsAnyType, Exception, string>>((v, t) => true)),
|
|
Times.Once);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Constructor_DoesNotSetBaseAddress_WhenInEfDesignTime()
|
|
{
|
|
// Arrange
|
|
var originalEfVar = Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_EF");
|
|
Environment.SetEnvironmentVariable("DOTNET_RUNNING_IN_EF", "true");
|
|
|
|
var config = new MistralConfig
|
|
{
|
|
ApiKey = "test-api-key",
|
|
BaseUrl = "https://api.mistral.ai/v1/"
|
|
};
|
|
|
|
try
|
|
{
|
|
// Act
|
|
var connector = new MistralConnector(_httpClient, config, _loggerMock.Object, _cache);
|
|
|
|
// Assert
|
|
// During EF migrations, BaseAddress should NOT be set
|
|
Assert.IsNull(_httpClient.BaseAddress);
|
|
}
|
|
finally
|
|
{
|
|
// Restore original value
|
|
if (originalEfVar == null)
|
|
Environment.SetEnvironmentVariable("DOTNET_RUNNING_IN_EF", null);
|
|
else
|
|
Environment.SetEnvironmentVariable("DOTNET_RUNNING_IN_EF", originalEfVar);
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void Constructor_SetsJsonOptions_Always()
|
|
{
|
|
// Arrange
|
|
var config = new MistralConfig();
|
|
|
|
// Act
|
|
var connector = new MistralConnector(_httpClient, config, _loggerMock.Object, _cache);
|
|
|
|
// Assert - this tests internal state through reflection if needed
|
|
// For now, we can verify indirectly that methods work
|
|
// This is a basic sanity check
|
|
Assert.IsNotNull(connector);
|
|
}
|
|
}
|