mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-27 03:51:05 -04:00
GET /api/nests/{id}/sheets reads the stored archive's nest.json and returns
the sheet sizes it needs (grouped, width the shorter side, quantities summed)
and each drawing's required and nested counts, skipping the trailing empty
plate as plateCount does. MaterialLedger builds material holds from it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
150 lines
5.6 KiB
C#
150 lines
5.6 KiB
C#
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using OpenNest.Data;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.Server.Tests;
|
|
|
|
/// <summary>
|
|
/// <c>GET /api/nests/{id}/sheets</c> reads sheet sizes and part counts from the stored archive,
|
|
/// skipping the same trailing new-plate slot the record's plate count skips.
|
|
/// </summary>
|
|
public sealed class NestSheetsTests
|
|
{
|
|
private static readonly JsonSerializerOptions Options = new(JsonSerializerDefaults.Web)
|
|
{ Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() } };
|
|
|
|
[Fact]
|
|
public async Task SinglePlate_ReturnsItsSizeAndParts()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var client = factory.CreateClient();
|
|
var id = await Create(client, NestApiTests.SyntheticNest());
|
|
|
|
using var response = await client.GetAsync($"/api/nests/{id}/sheets");
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var sheets = (await response.Content.ReadFromJsonAsync<NestSheets>(Options))!;
|
|
Assert.Equal((id, 1), (sheets.NestId, sheets.Revision));
|
|
Assert.Equal([(20.0, 30.0, 1)], Sizes(sheets));
|
|
Assert.Equal([("Synthetic rectangle", 1, 1)], Parts(sheets));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PlateQuantity_MultipliesSheetsAndNestedParts()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var client = factory.CreateClient();
|
|
var nest = NestApiTests.SyntheticNest();
|
|
var plate = nest.Plates.Single();
|
|
plate.Quantity = 3;
|
|
plate.Parts.Add(new Part(nest.Drawings.Single(), new Vector(10, 3)));
|
|
|
|
var sheets = await Sheets(client, nest);
|
|
|
|
Assert.Equal([(20.0, 30.0, 3)], Sizes(sheets));
|
|
Assert.Equal([("Synthetic rectangle", 1, 6)], Parts(sheets));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task TrailingEmptyPlate_IsNotASheet()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var client = factory.CreateClient();
|
|
var nest = NestApiTests.SyntheticNest();
|
|
nest.Plates.Add(new Plate(new Size(48, 96)) { Quantity = 1 });
|
|
|
|
var sheets = await Sheets(client, nest);
|
|
|
|
Assert.Equal([(20.0, 30.0, 1)], Sizes(sheets));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SameSizePlates_GroupEvenWhenStoredRotated()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var client = factory.CreateClient();
|
|
var nest = NestApiTests.SyntheticNest();
|
|
var drawing = nest.Drawings.Single();
|
|
var rotated = new Plate(new Size(30, 20)) { Quantity = 2 };
|
|
rotated.Parts.Add(new Part(drawing, new Vector(2, 3)));
|
|
nest.Plates.Add(rotated);
|
|
var larger = new Plate(new Size(60, 10.125)) { Quantity = 1 };
|
|
larger.Parts.Add(new Part(drawing, new Vector(2, 3)));
|
|
nest.Plates.Add(larger);
|
|
|
|
var sheets = await Sheets(client, nest);
|
|
|
|
Assert.Equal([(10.125, 60.0, 1), (20.0, 30.0, 3)], Sizes(sheets));
|
|
Assert.Equal([("Synthetic rectangle", 1, 4)], Parts(sheets));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DrawingWithNothingNested_IsListedWithZero()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var client = factory.CreateClient();
|
|
var nest = NestApiTests.SyntheticNest();
|
|
var unplaced = new Drawing("Another rectangle", nest.Drawings.Single().Program);
|
|
unplaced.Quantity.Required = 5;
|
|
nest.Drawings.Add(unplaced);
|
|
|
|
var sheets = await Sheets(client, nest);
|
|
|
|
Assert.Equal([("Another rectangle", 5, 0), ("Synthetic rectangle", 1, 1)], Parts(sheets));
|
|
}
|
|
|
|
// NestWriter never writes a part without its drawing; NestReader drops one if it finds it.
|
|
[Fact]
|
|
public async Task PartWithMissingDrawing_IsSkipped()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var client = factory.CreateClient();
|
|
var archive = TestNests.Zip("nest.json", """
|
|
{"drawings":[{"id":1,"name":"Kept","quantity":{"required":2}}],
|
|
"plates":[{"id":1,"size":{"width":48,"length":96},"quantity":2,
|
|
"parts":[{"drawingId":1},{"drawingId":7}]}]}
|
|
""");
|
|
var id = await Create(client, archive);
|
|
|
|
var sheets = (await client.GetFromJsonAsync<NestSheets>($"/api/nests/{id}/sheets", Options))!;
|
|
|
|
Assert.Equal([(48.0, 96.0, 2)], Sizes(sheets));
|
|
Assert.Equal([("Kept", 2, 2)], Parts(sheets));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UnknownNest_Returns404()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var client = factory.CreateClient();
|
|
|
|
using var response = await client.GetAsync($"/api/nests/{Guid.NewGuid()}/sheets");
|
|
|
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
|
}
|
|
|
|
private static (double, double, int)[] Sizes(NestSheets sheets) =>
|
|
sheets.Sheets.Select(s => (s.Width, s.Length, s.Quantity)).ToArray();
|
|
|
|
private static (string, int, int)[] Parts(NestSheets sheets) =>
|
|
sheets.Parts.Select(p => (p.Name, p.Required, p.Nested)).ToArray();
|
|
|
|
private static async Task<NestSheets> Sheets(HttpClient client, Nest nest)
|
|
{
|
|
var id = await Create(client, nest);
|
|
return (await client.GetFromJsonAsync<NestSheets>($"/api/nests/{id}/sheets", Options))!;
|
|
}
|
|
|
|
private static Task<Guid> Create(HttpClient client, Nest nest) => Create(client, NestApiTests.Archive(nest));
|
|
|
|
private static async Task<Guid> Create(HttpClient client, byte[] archive)
|
|
{
|
|
using var created = await client.PostAsync("/api/nests",
|
|
NestRevisionTests.Body(new NestRecord { Name = "Sheets" }, archive));
|
|
Assert.Equal(HttpStatusCode.OK, created.StatusCode);
|
|
return (await created.Content.ReadFromJsonAsync<NestRecord>(Options))!.Id;
|
|
}
|
|
}
|