mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-27 12:41:06 -04:00
Operators want to release a few loose drawings without first building a set for them. The release record gains a `drawings` list (drawingId, quantity, joined drawingName) and nest items with no `setId` carry those drawings' quantities, so grouping, merging and undo stay one path. Schema v11 adds release_drawings and rebuilds release_nest_items with a nullable setId (SQLite cannot drop NOT NULL), keeping every row. The query gains `drawingId=`; a server from before this refuses that unknown parameter with 400, which the client turns into ServerTooOldException. SetReleaseSession asks before building anything, because the release is posted only after its nests are saved and an old server would refuse it then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
145 lines
6.4 KiB
C#
145 lines
6.4 KiB
C#
using System.Net;
|
|
using OpenNest.Data;
|
|
|
|
namespace OpenNest.Tests.Data;
|
|
|
|
public class RemoteDrawingSetStoreTests
|
|
{
|
|
private static RemoteDrawingSetStore Store(StubHandler handler) =>
|
|
new(new HttpClient(handler), "http://server:8090/base/");
|
|
|
|
private static DrawingSetRecord Set(Guid? drawing = null) => new()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Name = "Synthetic kit",
|
|
Members = new[] { new DrawingSetMember { DrawingId = drawing ?? Guid.NewGuid(), Quantity = 2 } },
|
|
};
|
|
|
|
[Fact]
|
|
public async Task QueryAsync_SendsEscapedFilters()
|
|
{
|
|
var handler = new StubHandler(_ => StubHandler.Json(new { items = Array.Empty<object>(), total = 0, offset = 5, limit = 5 }));
|
|
using var store = Store(handler);
|
|
|
|
await store.QueryAsync(new DrawingSetQuery
|
|
{
|
|
Search = " 50%_ ",
|
|
Customer = "A&B",
|
|
IncludeArchived = true,
|
|
Sort = DrawingSetSortField.Customer,
|
|
Descending = false,
|
|
Offset = 5,
|
|
Limit = 5,
|
|
});
|
|
|
|
Assert.Equal(
|
|
"http://server:8090/base/api/sets/query?search=50%25_&sort=customer&order=asc&offset=5&limit=5&customer=A%26B&includeArchived=true",
|
|
handler.Requests.Single().RequestUri!.AbsoluteUri);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task OldServer_RaisesServerTooOld()
|
|
{
|
|
using var store = Store(new StubHandler(_ => new HttpResponseMessage(HttpStatusCode.NotFound)));
|
|
|
|
var query = await Assert.ThrowsAsync<ServerTooOldException>(() => store.QueryAsync(new DrawingSetQuery()));
|
|
await Assert.ThrowsAsync<ServerTooOldException>(() => store.GetAsync(Guid.NewGuid()));
|
|
var releases = await Assert.ThrowsAsync<ServerTooOldException>(() => store.QueryReleasesAsync(nestId: Guid.NewGuid()));
|
|
await Assert.ThrowsAsync<ServerTooOldException>(() => store.GetReleaseAsync(Guid.NewGuid()));
|
|
Assert.Contains("several drawing sets", releases.Message);
|
|
Assert.Contains("drawing sets", query.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ServerWithoutDrawingsOnTheirOwn_RefusesTheDrawingFilter_AndTheSupportCheck()
|
|
{
|
|
// A server from before drawings on their own refuses the unknown query parameter.
|
|
var handler = new StubHandler(request => request.RequestUri!.Query.Contains("drawingId=")
|
|
? new HttpResponseMessage(HttpStatusCode.BadRequest)
|
|
: StubHandler.Json(new { items = Array.Empty<object>(), total = 0, offset = 0, limit = 100 }));
|
|
using var store = Store(handler);
|
|
|
|
var check = await Assert.ThrowsAsync<ServerTooOldException>(() => store.EnsureDrawingReleasesSupportedAsync());
|
|
await Assert.ThrowsAsync<ServerTooOldException>(() => store.QueryReleasesAsync(drawingId: Guid.NewGuid()));
|
|
await store.QueryReleasesAsync(setId: Guid.NewGuid());
|
|
|
|
Assert.Contains("releasing library drawings on their own", check.Message);
|
|
Assert.Contains("api/releases/query?drawingId=", handler.Requests[0].RequestUri!.AbsoluteUri);
|
|
using var missing = Store(new StubHandler(_ => new HttpResponseMessage(HttpStatusCode.NotFound)));
|
|
await Assert.ThrowsAsync<ServerTooOldException>(() => missing.EnsureDrawingReleasesSupportedAsync());
|
|
using var current = Store(new StubHandler(_ => StubHandler.Json(new { items = Array.Empty<object>(), total = 0, offset = 0, limit = 1 })));
|
|
await current.EnsureDrawingReleasesSupportedAsync();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UnknownSet_OnASetAwareServer_IsNull()
|
|
{
|
|
var handler = new StubHandler(request => request.RequestUri!.AbsolutePath.EndsWith("/api/sets/query")
|
|
? StubHandler.Json(new { items = Array.Empty<object>(), total = 0, offset = 0, limit = 1 })
|
|
: new HttpResponseMessage(HttpStatusCode.NotFound));
|
|
using var store = Store(handler);
|
|
|
|
Assert.Null(await store.GetAsync(Guid.NewGuid()));
|
|
Assert.EndsWith("api/sets/query?limit=1", handler.Requests[1].RequestUri!.AbsoluteUri);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task InvalidSet_IsRefusedBeforeSending()
|
|
{
|
|
var handler = new StubHandler(_ => throw new InvalidOperationException("Nothing may be sent."));
|
|
using var store = Store(handler);
|
|
var duplicate = Set();
|
|
duplicate.Members = duplicate.Members.Concat(duplicate.Members).ToList();
|
|
|
|
await Assert.ThrowsAsync<ArgumentException>(() => store.CreateAsync(duplicate));
|
|
await Assert.ThrowsAsync<ArgumentException>(() => store.PostReleaseAsync(new SetReleaseRecord
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Sets = new[] { new SetReleaseLine { SetId = Guid.NewGuid(), SetRevision = 1, Quantity = 0 } },
|
|
}));
|
|
await Assert.ThrowsAsync<ArgumentException>(() => store.UndoReleaseAsync(Guid.NewGuid(),
|
|
new SetReleaseUndoRequest { SkipNests = new[] { Guid.Empty } }));
|
|
Assert.Empty(handler.Requests);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_SendsMembersWithoutJoinedDrawingsAndChecksTheReply()
|
|
{
|
|
var drawing = Guid.NewGuid();
|
|
var set = Set(drawing);
|
|
set.Members[0].Drawing = new DrawingLibraryRecord { Id = drawing, Name = "Synthetic joined" };
|
|
var handler = new StubHandler(_ => StubHandler.Json(new
|
|
{
|
|
id = set.Id,
|
|
revision = 1,
|
|
name = set.Name,
|
|
members = new[] { new { drawingId = drawing, quantity = 3 } },
|
|
}));
|
|
using var store = Store(handler);
|
|
|
|
await Assert.ThrowsAsync<IOException>(() => store.CreateAsync(set));
|
|
Assert.DoesNotContain("Synthetic joined", handler.RequestBodies.Single());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task PostReleaseAsync_ReplyForOtherNests_IsRefused()
|
|
{
|
|
var setId = Guid.NewGuid();
|
|
var release = new SetReleaseRecord
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
Sets = new[] { new SetReleaseLine { SetId = setId, SetRevision = 1, Quantity = 1 } },
|
|
Nests = new[] { new SetReleaseNest { NestId = Guid.NewGuid(), RevisionAfter = 1, MaterialName = "Synthetic Steel" } },
|
|
};
|
|
var handler = new StubHandler(_ => StubHandler.Json(new
|
|
{
|
|
id = release.Id,
|
|
sets = new[] { new { setId, setRevision = 1, quantity = 1 } },
|
|
nests = new[] { new { nestId = Guid.NewGuid(), kind = "created", revisionAfter = 1 } },
|
|
}));
|
|
using var store = Store(handler);
|
|
|
|
await Assert.ThrowsAsync<IOException>(() => store.PostReleaseAsync(release));
|
|
}
|
|
}
|