mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-14 02:15:07 -05:00
Finding a part on the nest server meant scrolling 40k drawings: the library could only match an exact thickness or customer and knew nothing about a drawing's size or holes. The library browser gets a Filter dialog with inclusive ranges, and the query API takes them. The server never parses packages and stays off IO, so the server worker now measures each package beside its nest jobs (bounding sides, net area, closed cutouts inside the outline) and stores the summary keyed by package hash (schema v19). Identical geometry is measured once, an unreadable package is recorded once instead of retried forever, and size or hole limits match only measured drawings. Thickness ranges compare in inches across units and keep the library's rule that a drawing saved without a thickness suits any. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
137 lines
6.0 KiB
C#
137 lines
6.0 KiB
C#
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using Microsoft.Data.Sqlite;
|
|
using OpenNest.Data;
|
|
using static OpenNest.Server.Tests.DrawingLibraryFixtures;
|
|
using static OpenNest.Server.Tests.DrawingSetFixtures;
|
|
using static OpenNest.Server.Tests.TestDatabase;
|
|
|
|
namespace OpenNest.Server.Tests;
|
|
|
|
public sealed class MaterialListTests
|
|
{
|
|
[Fact]
|
|
public void V9Upgrade_StartsEmptyAndLeavesStoredMaterialsAlone()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
var nest = Guid.NewGuid();
|
|
var drawing = Record();
|
|
using (var database = new NestDatabase(factory.DatabasePath, pooling: false))
|
|
{
|
|
database.Insert(nest, Nest("A", " Stainless ", "304"), [1]);
|
|
database.InsertDrawing(drawing.Id, drawing, Package());
|
|
}
|
|
Execute(factory.DatabasePath, "DROP TABLE material_grades; DROP TABLE materials; PRAGMA user_version=9;");
|
|
|
|
using var upgraded = new NestDatabase(factory.DatabasePath, pooling: false);
|
|
|
|
Assert.Equal(19L, Scalar(factory.DatabasePath, "PRAGMA user_version"));
|
|
Assert.Empty(upgraded.ListMaterials());
|
|
Assert.Equal((" Stainless ", "304"), (upgraded.Get(nest)!.Material, upgraded.Get(nest)!.MaterialGrade));
|
|
Assert.Equal(drawing.MaterialName, upgraded.GetDrawing(drawing.Id)!.MaterialName);
|
|
}
|
|
|
|
[Fact]
|
|
public void Add_IsIdempotentByKeyAndKeepsTheFirstSpelling()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var database = new NestDatabase(factory.DatabasePath, pooling: false);
|
|
|
|
var (created, createdAdded) = database.AddMaterial(new MaterialListAddition { Name = " Aluminum 5000 " });
|
|
var (again, againAdded) = database.AddMaterial(new MaterialListAddition { Name = "ALUMINUM 5000" });
|
|
var (graded, gradeAdded) = database.AddMaterial(new MaterialListAddition { Name = "aluminum 5000", Grade = "5052" });
|
|
var (_, sameGradeAdded) = database.AddMaterial(new MaterialListAddition { Name = "Aluminum 5000", Grade = " 5052" });
|
|
var (both, bothAdded) = database.AddMaterial(new MaterialListAddition { Name = "Copper", Grade = "C110" });
|
|
|
|
Assert.Equal((true, false, true, false, true), (createdAdded, againAdded, gradeAdded, sameGradeAdded, bothAdded));
|
|
Assert.Equal("Aluminum 5000", created.Name);
|
|
Assert.Equal(("Aluminum 5000", 0), (again.Name, again.Grades.Count));
|
|
Assert.Equal(("Aluminum 5000", "5052"), (graded.Name, Assert.Single(graded.Grades)));
|
|
Assert.Equal(("Copper", "C110"), (both.Name, Assert.Single(both.Grades)));
|
|
Assert.Equal(new[] { "Aluminum 5000", "Copper" }, database.ListMaterials().Select(m => m.Name));
|
|
}
|
|
|
|
[Fact]
|
|
public void Add_SameGradeUnderTwoMaterials_ListsItUnderEach()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var database = new NestDatabase(factory.DatabasePath, pooling: false);
|
|
|
|
database.AddMaterial(new MaterialListAddition { Name = "Stainless", Grade = "Grade A" });
|
|
var (_, added) = database.AddMaterial(new MaterialListAddition { Name = "Mild Steel", Grade = "Grade A" });
|
|
|
|
Assert.True(added);
|
|
Assert.All(database.ListMaterials(), m => Assert.Equal("Grade A", Assert.Single(m.Grades)));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
public void Add_BlankName_IsRefusedWithNoChange(string? name)
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var database = new NestDatabase(factory.DatabasePath, pooling: false);
|
|
|
|
Assert.Throws<ArgumentException>(() => database.AddMaterial(new MaterialListAddition { Name = name, Grade = "304" }));
|
|
Assert.Empty(database.ListMaterials());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Api_PostReturnsCreatedThenOkAndGetListsTheMaterial()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var client = factory.CreateClient();
|
|
|
|
using var first = await client.PostAsync("/api/materials", Json("""{"name":"Stainless","grade":"304"}"""));
|
|
using var repeat = await client.PostAsync("/api/materials", Json("""{"name":"stainless","grade":"304"}"""));
|
|
|
|
Assert.Equal(HttpStatusCode.Created, first.StatusCode);
|
|
Assert.Equal(HttpStatusCode.OK, repeat.StatusCode);
|
|
var entry = (await repeat.Content.ReadFromJsonAsync<MaterialListEntry>(Options))!;
|
|
Assert.Equal(("Stainless", "304"), (entry.Name, Assert.Single(entry.Grades)));
|
|
var listed = Assert.Single((await client.GetFromJsonAsync<List<MaterialListEntry>>("/api/materials", Options))!);
|
|
Assert.Equal(("Stainless", "304"), (listed.Name, Assert.Single(listed.Grades)));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("""{"grade":"304"}""")]
|
|
[InlineData("""{"name":" "}""")]
|
|
[InlineData("""{"name":"Stainless","density":0.29}""")]
|
|
[InlineData("""{"name":"Stainless","grade":7}""")]
|
|
[InlineData("""not json""")]
|
|
[InlineData("""null""")]
|
|
public async Task Api_RejectedAddition_IsBadRequestWithNoChange(string body)
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var client = factory.CreateClient();
|
|
|
|
using var response = await client.PostAsync("/api/materials", Json(body));
|
|
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
Assert.Empty((await client.GetFromJsonAsync<List<MaterialListEntry>>("/api/materials", Options))!);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Api_OverlongName_IsBadRequest()
|
|
{
|
|
using var factory = new ServerFactory();
|
|
using var client = factory.CreateClient();
|
|
var name = new string('x', DrawingLibraryRules.MaxTextLength + 1);
|
|
|
|
using var response = await client.PostAsync("/api/materials", Json($$"""{"name":"{{name}}"}"""));
|
|
|
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
|
}
|
|
|
|
private static NestRecord Nest(string name, string material, string grade) => new()
|
|
{
|
|
Name = name,
|
|
Material = material,
|
|
MaterialGrade = grade,
|
|
Thickness = 0.25,
|
|
DateCreated = new DateTime(2026, 1, 1),
|
|
DateModified = new DateTime(2026, 1, 2),
|
|
};
|
|
}
|