Files
ChatBot/ChatBot.Tests/Services/ModelServiceTests.cs
Leonid Pershin 1647fe19d3
Some checks failed
SonarQube / Build and analyze (push) Failing after 2m56s
Unit Tests / Run Tests (push) Failing after 2m28s
add more tests
2025-10-20 07:02:12 +03:00

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");
}
}