using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Junction.Domain;
using Junction.Domain.Models;
using Junction.Domain.Persistence;
using Moq;
using Xunit;
namespace Junction.Tests.Unit
{
///
/// Shape/contract tests for using a Moq mock.
/// No real database: these assert the async signatures and Result shapes only.
///
public class MachineRepositoryContractTests
{
private static readonly OperationError SampleError =
new OperationError("repo.error", "repository", "boom");
private static Machine SampleMachine(Guid id) =>
new Machine(id, "Mill", "mtconnect", null, TimeSpan.FromSeconds(2));
private static MachineSnapshot SampleSnapshot(Guid machineId) =>
new MachineSnapshot(machineId, DateTimeOffset.UtcNow, ConnectionState.Connected, null);
[Fact]
public async Task GetAllAsync_Ok_WrapsReadOnlyListOfMachines()
{
var machines = new List { SampleMachine(Guid.NewGuid()) };
var mock = new Mock();
mock.Setup(r => r.GetAllAsync(It.IsAny()))
.ReturnsAsync(Result>.Ok(machines));
Result> result = await mock.Object.GetAllAsync(CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.False(result.WasCancelled);
Assert.Single(result.Value);
}
[Fact]
public async Task GetAllAsync_Fail_HasErrorsNoValue()
{
var mock = new Mock();
mock.Setup(r => r.GetAllAsync(It.IsAny()))
.ReturnsAsync(Result>.Fail(SampleError));
var result = await mock.Object.GetAllAsync(CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.Single(result.Errors);
Assert.Equal("repo.error", result.Errors[0].Code);
}
[Fact]
public async Task GetByIdAsync_Ok_WrapsMachine()
{
var id = Guid.NewGuid();
var mock = new Mock();
mock.Setup(r => r.GetByIdAsync(id, It.IsAny()))
.ReturnsAsync(Result.Ok(SampleMachine(id)));
var result = await mock.Object.GetByIdAsync(id, CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal(id, result.Value.Id);
}
[Fact]
public async Task GetByIdAsync_Fail_NotFoundShape()
{
var mock = new Mock();
mock.Setup(r => r.GetByIdAsync(It.IsAny(), It.IsAny()))
.ReturnsAsync(Result.Fail(SampleError));
var result = await mock.Object.GetByIdAsync(Guid.NewGuid(), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.NotEmpty(result.Errors);
}
[Fact]
public async Task GetByIdAsync_Cancelled_SetsWasCancelled()
{
var mock = new Mock();
mock.Setup(r => r.GetByIdAsync(It.IsAny(), It.IsAny()))
.ReturnsAsync(Result.Cancelled());
var result = await mock.Object.GetByIdAsync(Guid.NewGuid(), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.True(result.WasCancelled);
}
[Fact]
public async Task UpsertAsync_ReturnsNonGenericResult_Ok()
{
var mock = new Mock();
mock.Setup(r => r.UpsertAsync(It.IsAny(), It.IsAny()))
.ReturnsAsync(Result.Ok());
Result result = await mock.Object.UpsertAsync(SampleMachine(Guid.NewGuid()), CancellationToken.None);
Assert.IsType(result);
Assert.True(result.IsSuccess);
}
[Fact]
public async Task UpsertAsync_Fail_CarriesErrors()
{
var mock = new Mock();
mock.Setup(r => r.UpsertAsync(It.IsAny(), It.IsAny()))
.ReturnsAsync(Result.Fail(SampleError));
var result = await mock.Object.UpsertAsync(SampleMachine(Guid.NewGuid()), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.Single(result.Errors);
}
[Fact]
public async Task DeleteAsync_ReturnsNonGenericResult_Ok()
{
var mock = new Mock();
mock.Setup(r => r.DeleteAsync(It.IsAny(), It.IsAny()))
.ReturnsAsync(Result.Ok());
Result result = await mock.Object.DeleteAsync(Guid.NewGuid(), CancellationToken.None);
Assert.IsType(result);
Assert.True(result.IsSuccess);
}
[Fact]
public async Task DeleteAsync_Cancelled_SetsWasCancelled()
{
var mock = new Mock();
mock.Setup(r => r.DeleteAsync(It.IsAny(), It.IsAny()))
.ReturnsAsync(Result.Cancelled());
var result = await mock.Object.DeleteAsync(Guid.NewGuid(), CancellationToken.None);
Assert.True(result.WasCancelled);
Assert.False(result.IsSuccess);
}
[Fact]
public async Task SaveSnapshotAsync_ReturnsNonGenericResult_Ok()
{
var machineId = Guid.NewGuid();
var mock = new Mock();
mock.Setup(r => r.SaveSnapshotAsync(It.IsAny(), It.IsAny()))
.ReturnsAsync(Result.Ok());
Result result = await mock.Object.SaveSnapshotAsync(SampleSnapshot(machineId), CancellationToken.None);
Assert.IsType(result);
Assert.True(result.IsSuccess);
}
[Fact]
public async Task SaveSnapshotAsync_Fail_CarriesErrors()
{
var mock = new Mock();
mock.Setup(r => r.SaveSnapshotAsync(It.IsAny(), It.IsAny()))
.ReturnsAsync(Result.Fail(SampleError));
var result = await mock.Object.SaveSnapshotAsync(SampleSnapshot(Guid.NewGuid()), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.NotEmpty(result.Errors);
}
[Fact]
public async Task GetLatestSnapshotAsync_Ok_WrapsSnapshot()
{
var machineId = Guid.NewGuid();
var mock = new Mock();
mock.Setup(r => r.GetLatestSnapshotAsync(machineId, It.IsAny()))
.ReturnsAsync(Result.Ok(SampleSnapshot(machineId)));
Result result = await mock.Object.GetLatestSnapshotAsync(machineId, CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal(machineId, result.Value.MachineId);
Assert.Equal(ConnectionState.Connected, result.Value.ConnectionState);
}
[Fact]
public async Task GetLatestSnapshotAsync_Fail_NoneStored()
{
var mock = new Mock();
mock.Setup(r => r.GetLatestSnapshotAsync(It.IsAny(), It.IsAny()))
.ReturnsAsync(Result.Fail(SampleError));
var result = await mock.Object.GetLatestSnapshotAsync(Guid.NewGuid(), CancellationToken.None);
Assert.False(result.IsSuccess);
Assert.Single(result.Errors);
}
[Fact]
public async Task AllMethods_HonorPassedCancellationToken()
{
using var cts = new CancellationTokenSource();
var token = cts.Token;
var mock = new Mock();
mock.Setup(r => r.GetAllAsync(token))
.ReturnsAsync(Result>.Ok(new List()));
mock.Setup(r => r.GetByIdAsync(It.IsAny(), token))
.ReturnsAsync(Result.Ok(SampleMachine(Guid.NewGuid())));
mock.Setup(r => r.UpsertAsync(It.IsAny(), token)).ReturnsAsync(Result.Ok());
mock.Setup(r => r.DeleteAsync(It.IsAny(), token)).ReturnsAsync(Result.Ok());
mock.Setup(r => r.SaveSnapshotAsync(It.IsAny(), token)).ReturnsAsync(Result.Ok());
mock.Setup(r => r.GetLatestSnapshotAsync(It.IsAny(), token))
.ReturnsAsync(Result.Ok(SampleSnapshot(Guid.NewGuid())));
await mock.Object.GetAllAsync(token);
await mock.Object.GetByIdAsync(Guid.NewGuid(), token);
await mock.Object.UpsertAsync(SampleMachine(Guid.NewGuid()), token);
await mock.Object.DeleteAsync(Guid.NewGuid(), token);
await mock.Object.SaveSnapshotAsync(SampleSnapshot(Guid.NewGuid()), token);
await mock.Object.GetLatestSnapshotAsync(Guid.NewGuid(), token);
mock.Verify(r => r.GetAllAsync(token), Times.Once);
mock.Verify(r => r.GetByIdAsync(It.IsAny(), token), Times.Once);
mock.Verify(r => r.UpsertAsync(It.IsAny(), token), Times.Once);
mock.Verify(r => r.DeleteAsync(It.IsAny(), token), Times.Once);
mock.Verify(r => r.SaveSnapshotAsync(It.IsAny(), token), Times.Once);
mock.Verify(r => r.GetLatestSnapshotAsync(It.IsAny(), token), Times.Once);
}
}
}