meilisearch.NET/meilisearch.NET.Tests/MeiliSearchServiceTests.cs
2025-02-25 05:07:55 -05:00

395 lines
14 KiB
C#

using Xunit;
using meilisearch.NET;
using Microsoft.Extensions.Logging;
using meilisearch.NET.Configurations;
using System.Net.Http;
using Moq;
using Meilisearch;
using Microsoft.Extensions.Configuration;
using System.Threading.Tasks;
using System.Collections.Generic;
using meilisearch.NET.Interfaces;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO;
using System.Threading;
using System.Collections.ObjectModel;
namespace meilisearch.NET.Tests;
public class MeiliSearchServiceTests
{
private readonly Mock<HttpClient> _httpClientMock;
private readonly Mock<ILogger<MeiliSearchService>> _loggerMock;
private readonly Mock<MeiliSearchConfiguration> _meiliConfigurationMock;
private readonly MeiliSearchService _meiliSearchService;
public MeiliSearchServiceTests()
{
_httpClientMock = new Mock<HttpClient>();
_loggerMock = new Mock<ILogger<MeiliSearchService>>();
_meiliConfigurationMock = new Mock<MeiliSearchConfiguration>();
_meiliConfigurationMock.Setup(x => x.MeiliPort).Returns(7700);
_meiliSearchService = new MeiliSearchService(_httpClientMock.Object, _loggerMock.Object, _meiliConfigurationMock.Object);
}
[Fact]
public void Test_Service_Initialized()
{
Assert.NotNull(_meiliSearchService);
}
[Fact]
public async Task Test_StartMeilisearch_Success()
{
// Mock the process start
var processMock = new Mock<Process>();
processMock.Setup(p => p.Start()).Returns(true);
// Mock the Process.Start method
Mock<IProcessWrapper> processWrapperMock = new Mock<IProcessWrapper>();
processWrapperMock.Setup(p => p.Start(It.IsAny<ProcessStartInfo>())).Returns(processMock.Object);
// Override the GetMeilisearchBinaryName method
var meiliSearchService = new TestMeiliSearchService(_httpClientMock.Object, _loggerMock.Object, _meiliConfigurationMock.Object, processWrapperMock.Object);
// Call the StartMeilisearch method
await meiliSearchService.StartMeilisearch();
// Assert that the process was started
processWrapperMock.Verify(p => p.Start(It.IsAny<ProcessStartInfo>()), Times.Once);
}
[Fact]
public void Test_Stop_Success()
{
// Mock the process
var processMock = new Mock<Process>();
processMock.Setup(p => p.Kill());
// Set the process to the MeiliSearchService
_meiliSearchService.process = processMock.Object;
// Call the Stop method
_meiliSearchService.Stop();
// Assert that the process was killed
processMock.Verify(p => p.Kill(), Times.Once);
}
[Fact]
public void Test_IsMeilisearchRunning_Success()
{
// Mock the Process.GetProcessesByName method
var processMock = new Mock<Process>();
var processes = new Process[] { processMock.Object };
Mock<IProcessStaticWrapper> processStaticWrapperMock = new Mock<IProcessStaticWrapper>();
processStaticWrapperMock.Setup(p => p.GetProcessesByName(It.IsAny<string>())).Returns(processes);
// Override the GetMeilisearchBinaryName method
var meiliSearchService = new TestMeiliSearchService(_httpClientMock.Object, _loggerMock.Object, _meiliConfigurationMock.Object, null, processStaticWrapperMock.Object);
// Call the IsMeilisearchRunning method
var isRunning = meiliSearchService.IsMeilisearchRunning();
// Assert that the method returns true
Assert.True(isRunning);
}
[Fact]
public async Task Test_CreateIndex_Success()
{
// Arrange
var indexName = "test_index";
var meilisearchClientMock = new Mock<MeilisearchClient>("http://localhost:7700", "masterKey");
var indexMock = new Mock<Meilisearch.Index>(meilisearchClientMock.Object, indexName);
var indexes = new List<IndexResponse> { new IndexResponse { Uid = indexName } };
meilisearchClientMock.Setup(c => c.GetAllIndexesAsync(null, null, It.IsAny<CancellationToken>())).ReturnsAsync(new IndexesResults { Results = new List<IndexResponse>() });
meilisearchClientMock.Setup(c => c.CreateIndexAsync(indexName, null, It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);
meilisearchClientMock.Setup(c => c.GetIndexAsync(indexName, It.IsAny<CancellationToken>())).ReturnsAsync(indexMock.Object);
indexMock.Setup(i => i.UpdateFilterableAttributesAsync(It.IsAny<string[]>(), null, It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);
// Override the MeilisearchClient
var meiliSearchService = new TestMeiliSearchService(_httpClientMock.Object, _loggerMock.Object, _meiliConfigurationMock.Object, null, null, meilisearchClientMock.Object);
// Act
meiliSearchService.CreateIndex<TestDocument>(indexName);
// Assert
meilisearchClientMock.Verify(c => c.CreateIndexAsync(indexName, null, It.IsAny<CancellationToken>()), Times.Once);
}
[Fact]
public async Task Test_DeleteIndex_Success()
{
// Arrange
var indexName = "test_index";
var meilisearchClientMock = new Mock<MeilisearchClient>("http://localhost:7700", "masterKey");
var indexMock = new Mock<Meilisearch.Index>(meilisearchClientMock.Object, indexName);
var indexes = new List<IndexResponse> { new IndexResponse { Uid = indexName } };
meilisearchClientMock.Setup(c => c.GetAllIndexesAsync(null, null, It.IsAny<CancellationToken>())).ReturnsAsync(new IndexesResults { Results = indexes });
meilisearchClientMock.Setup(c => c.DeleteIndexAsync(indexName, null, It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);
// Override the MeilisearchClient
var meiliSearchService = new TestMeiliSearchService(_httpClientMock.Object, _loggerMock.Object, _meiliConfigurationMock.Object, null, null, meilisearchClientMock.Object);
// Act
meiliSearchService.DeleteIndex(indexName);
// Assert
meilisearchClientMock.Verify(c => c.DeleteIndexAsync(indexName, null, It.IsAny<CancellationToken>()), Times.Once);
}
[Fact]
public async Task Test_GetAllIndexes_Success()
{
// Arrange
var indexName = "test_index";
var meilisearchClientMock = new Mock<MeilisearchClient>("http://localhost:7700", "masterKey");
var indexMock = new Mock<Meilisearch.Index>(meilisearchClientMock.Object, indexName);
var indexes = new List<IndexResponse> { new IndexResponse { Uid = indexName } };
meilisearchClientMock.Setup(c => c.GetAllIndexesAsync(null, null, It.IsAny<CancellationToken>())).ReturnsAsync(new IndexesResults { Results = indexes });
// Override the MeilisearchClient
var meiliSearchService = new TestMeiliSearchService(_httpClientMock.Object, _loggerMock.Object, _meiliConfigurationMock.Object, null, null, meilisearchClientMock.Object);
// Act
var result = meiliSearchService.GetAllIndexes();
// Assert
Assert.Contains(indexName, result);
}
[Fact]
public void Test_AddDocument_Success()
{
// Arrange
var indexName = "test_index";
var document = new TestDocument { Id = "1" };
var documentCollectionMock = new Mock<ICollection<KeyValuePair<string, IDocument>>>();
// Override the DocumentCollection
var meiliSearchService = new TestMeiliSearchService(_httpClientMock.Object, _loggerMock.Object, _meiliConfigurationMock.Object, null, null, null, documentCollectionMock.Object);
// Act
meiliSearchService.AddDocument(indexName, document);
// Assert
documentCollectionMock.Verify(c => c.Add(It.IsAny<KeyValuePair<string, IDocument>>()), Times.Once);
}
[Fact]
public void Test_Configuration_Dependencies_Success()
{
// Arrange
var httpClient = _httpClientMock.Object;
var logger = _loggerMock.Object;
var meiliConfiguration = _meiliConfigurationMock.Object;
// Act
var meiliSearchService = new MeiliSearchService(httpClient, logger, meiliConfiguration);
// Assert
Assert.NotNull(meiliSearchService);
}
[Fact]
public void Test_GenerateApiKey_Success()
{
// Arrange
var length = 64;
// Act
var apiKey = MeiliSearchService.GenerateApiKey(length);
// Assert
Assert.NotNull(apiKey);
Assert.Equal(length, apiKey.Length);
}
[Fact]
public void Test_Dispose_Success()
{
// Arrange
var httpClientMock = new Mock<HttpClient>();
var processMock = new Mock<Process>();
// Set the process to the MeiliSearchService
_meiliSearchService.process = processMock.Object;
// Act
_meiliSearchService.Dispose();
// Assert
httpClientMock.Verify(c => c.Dispose(), Times.Never); // HttpClient is disposed in base class
processMock.Verify(p => p.Kill(), Times.Once);
}
}
// Create a wrapper interface for Process.Start
public interface IProcessWrapper
{
Process Start(ProcessStartInfo startInfo);
}
// Create a wrapper class for Process.Start
public class ProcessWrapper : IProcessWrapper
{
public Process Start(ProcessStartInfo startInfo)
{
return Process.Start(startInfo);
}
}
// Create a wrapper interface for Process.GetProcessesByName
public interface IProcessStaticWrapper
{
Process[] GetProcessesByName(string processName);
}
// Create a wrapper class for Process.GetProcessesByName
public class ProcessStaticWrapper : IProcessStaticWrapper
{
public Process[] GetProcessesByName(string processName)
{
return Process.GetProcessesByName(processName);
}
}
// Create a test class to override the GetMeilisearchBinaryName method
public class TestMeiliSearchService : MeiliSearchService
{
private readonly IProcessWrapper _processWrapper;
private readonly IProcessStaticWrapper _processStaticWrapper;
private readonly MeilisearchClient _meilisearchClient;
private readonly ICollection<KeyValuePair<string, IDocument>> _documentCollection;
public TestMeiliSearchService(HttpClient httpClient, ILogger<MeiliSearchService> logger, MeiliSearchConfiguration meiliConfiguration, IProcessWrapper processWrapper = null, IProcessStaticWrapper processStaticWrapper = null, MeilisearchClient meilisearchClient = null, ICollection<KeyValuePair<string, IDocument>> documentCollection = null) : base(httpClient, logger, meiliConfiguration)
{
_processWrapper = processWrapper;
_processStaticWrapper = processStaticWrapper;
_meilisearchClient = meilisearchClient;
_documentCollection = documentCollection;
}
public override async Task StartMeilisearch()
{
var binaryName = GetMeilisearchBinaryName();
var binaryPath = Path.Combine(AppContext.BaseDirectory, binaryName);
if (!File.Exists(binaryPath))
{
Logger.LogError($"Meilisearch binary not found at: {binaryPath}");
throw new FileNotFoundException($"Could not find Meilisearch binary: {binaryName}");
}
// Set execute permissions on Unix-like systems
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
try
{
// Mock the chmod process
//var chmod = Process.Start("chmod", $"+x {binaryPath}");
//chmod?.WaitForExit();
}
catch (Exception ex)
{
Logger.LogWarning($"Failed to set execute permissions on binary: {ex.Message}");
}
}
var host = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? "localhost"
: "127.0.0.1";
var args = "--http-addr " + host + ":" + MeiliConfiguration.MeiliPort
+ " --env development --db-path "
+ Path.Combine(AppContext.BaseDirectory, binaryName);
var processStartInfo = new ProcessStartInfo
{
FileName = binaryPath,
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = false,
RedirectStandardError = false,
CreateNoWindow = false,
};
// Use the mocked Process.Start method
process = _processWrapper.Start(processStartInfo);
await Task.Delay(5000); // Wait for the process to start
Logger.LogInformation($"Started Meilisearch process using binary: {binaryName}");
}
public override string GetMeilisearchBinaryName()
{
return "meilisearch-mock";
}
public override bool IsMeilisearchRunning()
{
// Mock the Process.GetProcessesByName method
var processName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? "meilisearch-windows"
: "meilisearch";
var processes = _processStaticWrapper.GetProcessesByName(processName);
return processes.Any();
}
public override async Task<IndexesResults> GetAllIndexesAsync(CancellationToken cancellationToken = default)
{
return _meilisearchClient.GetAllIndexesAsync(null, null, cancellationToken);
}
public override async Task CreateIndexAsync(string indexName, string primaryKey = null, CancellationToken cancellationToken = default)
{
return _meilisearchClient.CreateIndexAsync(indexName, primaryKey, cancellationToken);
}
public override async Task DeleteIndexAsync(string indexName, CancellationToken cancellationToken = default)
{
return _meilisearchClient.DeleteIndexAsync(indexName, cancellationToken);
}
public override async Task<Index> GetIndexAsync(string indexName, CancellationToken cancellationToken = default)
{
return _meilisearchClient.GetIndexAsync(indexName, cancellationToken);
}
private ILogger<MeiliSearchService> Logger
{
get { return _logger; }
}
private MeiliSearchConfiguration MeiliConfiguration
{
get { return _meiliConfiguration; }
}
private string ApiKey
{
get { return _apiKey; }
}
protected override ObservableCollection<KeyValuePair<string, IDocument>> DocumentCollection
{
get
{
return _documentCollection as ObservableCollection<KeyValuePair<string, IDocument>>;
}
}
protected virtual void Dispose(bool disposing)
{
//base.Dispose(disposing);
}
}
public class TestDocument : IDocument
{
public new string Id { get; set; }
}