230 lines
7.4 KiB
C#
230 lines
7.4 KiB
C#
using ChatBot.Models.Configuration;
|
|
using ChatBot.Services;
|
|
using ChatBot.Tests.TestUtilities;
|
|
using FluentAssertions;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using Moq;
|
|
|
|
namespace ChatBot.Tests.Services;
|
|
|
|
public class ModelServiceTests : UnitTestBase
|
|
{
|
|
private readonly Mock<ILogger<ModelService>> _loggerMock;
|
|
private readonly Mock<IOptions<OllamaSettings>> _optionsMock;
|
|
private readonly ModelService _modelService;
|
|
|
|
public ModelServiceTests()
|
|
{
|
|
_loggerMock = TestDataBuilder.Mocks.CreateLoggerMock<ModelService>();
|
|
var ollamaSettings = TestDataBuilder.Configurations.CreateOllamaSettings();
|
|
_optionsMock = TestDataBuilder.Mocks.CreateOptionsMock(ollamaSettings);
|
|
|
|
_modelService = new ModelService(_loggerMock.Object, _optionsMock.Object);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetCurrentModel_ShouldReturnDefaultModel()
|
|
{
|
|
// Act
|
|
var result = _modelService.GetCurrentModel();
|
|
|
|
// Assert
|
|
result.Should().Be("llama3.2");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_ShouldLogModelInformation()
|
|
{
|
|
// Act
|
|
var act = async () => await _modelService.InitializeAsync();
|
|
|
|
// Assert
|
|
await act.Should()
|
|
.NotThrowAsync("InitializeAsync should complete without throwing exceptions");
|
|
|
|
// Verify that logging was called (at least once for model information)
|
|
_loggerMock.Verify(
|
|
x =>
|
|
x.Log(
|
|
LogLevel.Information,
|
|
It.IsAny<EventId>(),
|
|
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("model")),
|
|
It.IsAny<Exception>(),
|
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
|
),
|
|
Times.AtLeastOnce,
|
|
"Should log model information"
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetCurrentModel_ShouldReturnCustomModel_WhenDifferentModelIsConfigured()
|
|
{
|
|
// Arrange
|
|
var customSettings = new OllamaSettings
|
|
{
|
|
DefaultModel = "custom-model-name",
|
|
Url = "http://custom-server:8080",
|
|
};
|
|
var customOptionsMock = TestDataBuilder.Mocks.CreateOptionsMock(customSettings);
|
|
var customService = new ModelService(_loggerMock.Object, customOptionsMock.Object);
|
|
|
|
// Act
|
|
var result = customService.GetCurrentModel();
|
|
|
|
// Assert
|
|
result.Should().Be("custom-model-name");
|
|
}
|
|
|
|
[Fact]
|
|
public void GetCurrentModel_ShouldReturnEmptyString_WhenDefaultModelIsEmpty()
|
|
{
|
|
// Arrange
|
|
var emptyModelSettings = new OllamaSettings
|
|
{
|
|
DefaultModel = string.Empty,
|
|
Url = "http://localhost:11434",
|
|
};
|
|
var emptyOptionsMock = TestDataBuilder.Mocks.CreateOptionsMock(emptyModelSettings);
|
|
var emptyService = new ModelService(_loggerMock.Object, emptyOptionsMock.Object);
|
|
|
|
// Act
|
|
var result = emptyService.GetCurrentModel();
|
|
|
|
// Assert
|
|
result.Should().Be(string.Empty);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetCurrentModel_ShouldReturnNull_WhenDefaultModelIsNull()
|
|
{
|
|
// Arrange
|
|
var nullModelSettings = new OllamaSettings
|
|
{
|
|
DefaultModel = null!,
|
|
Url = "http://localhost:11434",
|
|
};
|
|
var nullOptionsMock = TestDataBuilder.Mocks.CreateOptionsMock(nullModelSettings);
|
|
var nullService = new ModelService(_loggerMock.Object, nullOptionsMock.Object);
|
|
|
|
// Act
|
|
var result = nullService.GetCurrentModel();
|
|
|
|
// Assert
|
|
result.Should().BeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void GetCurrentModel_ShouldReturnModelWithSpecialCharacters_WhenModelNameContainsSpecialChars()
|
|
{
|
|
// Arrange
|
|
var specialCharSettings = new OllamaSettings
|
|
{
|
|
DefaultModel = "model-with-special_chars.123",
|
|
Url = "http://localhost:11434",
|
|
};
|
|
var specialOptionsMock = TestDataBuilder.Mocks.CreateOptionsMock(specialCharSettings);
|
|
var specialService = new ModelService(_loggerMock.Object, specialOptionsMock.Object);
|
|
|
|
// Act
|
|
var result = specialService.GetCurrentModel();
|
|
|
|
// Assert
|
|
result.Should().Be("model-with-special_chars.123");
|
|
}
|
|
|
|
[Fact]
|
|
public void GetCurrentModel_ShouldReturnLongModelName_WhenModelNameIsVeryLong()
|
|
{
|
|
// Arrange
|
|
var longModelName = new string('a', 1000); // Very long model name
|
|
var longModelSettings = new OllamaSettings
|
|
{
|
|
DefaultModel = longModelName,
|
|
Url = "http://localhost:11434",
|
|
};
|
|
var longOptionsMock = TestDataBuilder.Mocks.CreateOptionsMock(longModelSettings);
|
|
var longService = new ModelService(_loggerMock.Object, longOptionsMock.Object);
|
|
|
|
// Act
|
|
var result = longService.GetCurrentModel();
|
|
|
|
// Assert
|
|
result.Should().Be(longModelName);
|
|
result.Should().HaveLength(1000);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_ShouldLogCorrectModel_WhenDifferentModelIsConfigured()
|
|
{
|
|
// Arrange
|
|
var customSettings = new OllamaSettings
|
|
{
|
|
DefaultModel = "custom-llama-model",
|
|
Url = "http://custom-server:8080",
|
|
};
|
|
var customOptionsMock = TestDataBuilder.Mocks.CreateOptionsMock(customSettings);
|
|
var customService = new ModelService(_loggerMock.Object, customOptionsMock.Object);
|
|
|
|
// Act
|
|
await customService.InitializeAsync();
|
|
|
|
// Assert
|
|
_loggerMock.Verify(
|
|
x =>
|
|
x.Log(
|
|
LogLevel.Information,
|
|
It.IsAny<EventId>(),
|
|
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("custom-llama-model")),
|
|
It.IsAny<Exception>(),
|
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
|
),
|
|
Times.Once,
|
|
"Should log the correct custom model name"
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InitializeAsync_ShouldLogEmptyModel_WhenModelIsEmpty()
|
|
{
|
|
// Arrange
|
|
var emptyModelSettings = new OllamaSettings
|
|
{
|
|
DefaultModel = string.Empty,
|
|
Url = "http://localhost:11434",
|
|
};
|
|
var emptyOptionsMock = TestDataBuilder.Mocks.CreateOptionsMock(emptyModelSettings);
|
|
var emptyService = new ModelService(_loggerMock.Object, emptyOptionsMock.Object);
|
|
|
|
// Act
|
|
await emptyService.InitializeAsync();
|
|
|
|
// Assert
|
|
_loggerMock.Verify(
|
|
x =>
|
|
x.Log(
|
|
LogLevel.Information,
|
|
It.IsAny<EventId>(),
|
|
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("Using model:")),
|
|
It.IsAny<Exception>(),
|
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()
|
|
),
|
|
Times.Once,
|
|
"Should log even when model is empty"
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_ShouldHandleNullOllamaSettings()
|
|
{
|
|
// Arrange
|
|
var nullSettings = new OllamaSettings { DefaultModel = null!, Url = null! };
|
|
var nullOptionsMock = TestDataBuilder.Mocks.CreateOptionsMock(nullSettings);
|
|
|
|
// Act & Assert
|
|
var act = () => new ModelService(_loggerMock.Object, nullOptionsMock.Object);
|
|
act.Should().NotThrow("Constructor should handle null settings gracefully");
|
|
}
|
|
}
|