229 lines
8.2 KiB
C#
229 lines
8.2 KiB
C#
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using DongfangHydro.Dashboard.Api.Data;
|
|
using DongfangHydro.Dashboard.Api.Contracts;
|
|
using DongfangHydro.Dashboard.Api.Realtime;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Mvc.Testing;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using Xunit;
|
|
|
|
namespace DongfangHydro.Dashboard.Tests;
|
|
|
|
public sealed class DashboardApiTests(DashboardApiFactory factory)
|
|
: IClassFixture<DashboardApiFactory>
|
|
{
|
|
[Fact]
|
|
public async Task Orders_SearchesSeededWaterConservancyProductNames()
|
|
{
|
|
using var client = factory.CreateClient();
|
|
|
|
var response = await client.GetAsync("/api/orders?keyword=门架&page=1&pageSize=20");
|
|
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
Assert.True(body.GetProperty("total").GetInt32() >= 1);
|
|
Assert.Contains(
|
|
body.GetProperty("items").EnumerateArray(),
|
|
item => item.GetProperty("productName").GetString()!.Contains("门架", StringComparison.Ordinal));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task OrderTree_ReturnsTheCompleteSeededHierarchy()
|
|
{
|
|
using var client = factory.CreateClient();
|
|
var orders = await client.GetFromJsonAsync<JsonElement>("/api/orders?page=1&pageSize=20");
|
|
var orderId = orders.GetProperty("items")[0].GetProperty("id").GetString();
|
|
|
|
var root = await client.GetFromJsonAsync<JsonElement>($"/api/orders/{orderId}/tree");
|
|
|
|
Assert.Equal("order", root.GetProperty("nodeType").GetString());
|
|
Assert.Equal(66, CountNodes(root));
|
|
Assert.Equal(1, CountNodes(root, "order"));
|
|
Assert.Equal(5, CountNodes(root, "part"));
|
|
Assert.Equal(15, CountNodes(root, "component"));
|
|
Assert.Equal(45, CountNodes(root, "process"));
|
|
Assert.False(string.IsNullOrWhiteSpace(FindFirstProcess(root).GetProperty("version").GetString()));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateProgress_RecalculatesTheOrderAndReturnsTheUpdatedNode()
|
|
{
|
|
using var client = factory.CreateClient();
|
|
factory.UpdateQueue.Reset();
|
|
var orders = await client.GetFromJsonAsync<JsonElement>("/api/orders?page=1&pageSize=20");
|
|
var orderId = orders.GetProperty("items")[0].GetProperty("id").GetString();
|
|
var root = await client.GetFromJsonAsync<JsonElement>($"/api/orders/{orderId}/tree");
|
|
var process = FindFirstProcess(root);
|
|
|
|
var response = await client.PatchAsJsonAsync(
|
|
$"/api/nodes/{process.GetProperty("id").GetString()}/progress",
|
|
new
|
|
{
|
|
completedQty = process.GetProperty("requiredQty").GetInt32(),
|
|
expectedVersion = process.GetProperty("version").GetString(),
|
|
status = "done",
|
|
riskLevel = "normal",
|
|
delayDays = 0,
|
|
delayReason = string.Empty,
|
|
});
|
|
var updated = await response.Content.ReadFromJsonAsync<JsonElement>();
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
Assert.Equal(100, updated.GetProperty("node").GetProperty("progress").GetInt32());
|
|
Assert.Equal("done", updated.GetProperty("node").GetProperty("status").GetString());
|
|
Assert.Equal(orderId, updated.GetProperty("order").GetProperty("id").GetString());
|
|
Assert.Equal(1, factory.UpdateQueue.EnqueueCount);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateProgress_RejectsARequestWithoutCompletedQuantity()
|
|
{
|
|
using var client = factory.CreateClient();
|
|
var process = await GetFirstProcessAsync(client);
|
|
|
|
var response = await client.PatchAsJsonAsync(
|
|
$"/api/nodes/{process.GetProperty("id").GetString()}/progress",
|
|
new { status = "in_progress" });
|
|
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateProgress_RejectsDoneStatusBeforeThePlannedQuantityIsComplete()
|
|
{
|
|
using var client = factory.CreateClient();
|
|
var process = await GetFirstProcessAsync(client);
|
|
var requiredQty = process.GetProperty("requiredQty").GetInt32();
|
|
|
|
var response = await client.PatchAsJsonAsync(
|
|
$"/api/nodes/{process.GetProperty("id").GetString()}/progress",
|
|
new
|
|
{
|
|
completedQty = requiredQty - 1,
|
|
expectedVersion = process.GetProperty("version").GetString(),
|
|
status = "done",
|
|
riskLevel = "normal",
|
|
});
|
|
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateProgress_RequiresTheClientsExpectedVersion()
|
|
{
|
|
using var client = factory.CreateClient();
|
|
var process = await GetFirstProcessAsync(client);
|
|
|
|
var response = await client.PatchAsJsonAsync(
|
|
$"/api/nodes/{process.GetProperty("id").GetString()}/progress",
|
|
new
|
|
{
|
|
completedQty = process.GetProperty("completedQty").GetInt32(),
|
|
status = process.GetProperty("status").GetString(),
|
|
riskLevel = process.GetProperty("riskLevel").GetString(),
|
|
});
|
|
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
}
|
|
|
|
private static int CountNodes(JsonElement node)
|
|
{
|
|
var count = 1;
|
|
if (node.TryGetProperty("children", out var children))
|
|
{
|
|
count += children.EnumerateArray().Sum(CountNodes);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
private static int CountNodes(JsonElement node, string nodeType)
|
|
{
|
|
var count = node.GetProperty("nodeType").GetString() == nodeType ? 1 : 0;
|
|
if (node.TryGetProperty("children", out var children))
|
|
{
|
|
count += children.EnumerateArray().Sum(child => CountNodes(child, nodeType));
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
private static async Task<JsonElement> GetFirstProcessAsync(HttpClient client)
|
|
{
|
|
var orders = await client.GetFromJsonAsync<JsonElement>("/api/orders?page=1&pageSize=20");
|
|
var orderId = orders.GetProperty("items")[0].GetProperty("id").GetString();
|
|
var root = await client.GetFromJsonAsync<JsonElement>($"/api/orders/{orderId}/tree");
|
|
return FindFirstProcess(root);
|
|
}
|
|
|
|
private static JsonElement FindFirstProcess(JsonElement node)
|
|
{
|
|
if (node.GetProperty("nodeType").GetString() == "process")
|
|
{
|
|
return node;
|
|
}
|
|
|
|
foreach (var child in node.GetProperty("children").EnumerateArray())
|
|
{
|
|
var match = FindFirstProcess(child);
|
|
if (match.ValueKind != JsonValueKind.Undefined)
|
|
{
|
|
return match;
|
|
}
|
|
}
|
|
|
|
return default;
|
|
}
|
|
}
|
|
|
|
public sealed class DashboardApiFactory : WebApplicationFactory<Program>
|
|
{
|
|
private readonly string databaseName = $"dashboard-tests-{Guid.NewGuid():N}";
|
|
|
|
public RecordingDashboardUpdateQueue UpdateQueue { get; } = new();
|
|
|
|
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
|
{
|
|
builder.UseEnvironment("Testing");
|
|
builder.ConfigureAppConfiguration((_, configuration) =>
|
|
{
|
|
configuration.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["Database:AutoMigrate"] = "true",
|
|
["Database:Seed"] = "true",
|
|
});
|
|
});
|
|
builder.ConfigureServices(services =>
|
|
{
|
|
services.RemoveAll<DbContextOptions<DashboardDbContext>>();
|
|
services.AddDbContext<DashboardDbContext>(options =>
|
|
options.UseInMemoryDatabase(databaseName));
|
|
services.RemoveAll<IDashboardUpdateQueue>();
|
|
services.AddSingleton<IDashboardUpdateQueue>(UpdateQueue);
|
|
});
|
|
}
|
|
}
|
|
|
|
public sealed class RecordingDashboardUpdateQueue : IDashboardUpdateQueue
|
|
{
|
|
public int EnqueueCount { get; private set; }
|
|
|
|
public ValueTask EnqueueAsync(
|
|
UpdateNodeProgressResultDto update,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
EnqueueCount++;
|
|
return ValueTask.CompletedTask;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
EnqueueCount = 0;
|
|
}
|
|
}
|