using System; using Xunit; using Newtonsoft.Json.Linq; using FluentAssertions; namespace raven_integration { public class PartCrud { /// /// Full CRUD for a Part, including concurrency violation. /// [Fact] public async Task CRUD() { var token = await Util.GetTokenAsync("BizAdmin"); // CREATE var name = Util.Uniquify("Test Part"); var payload = $$""" {"id":0,"concurrency":0,"name":"{{name}}","active":true,"cost":9.99,"retail":14.99,"description":null,"notes":null,"wiki":null,"customFields":"{}","tags":[],"manufacturerId":null,"manufacturerNumber":null,"wholeSalerId":null,"wholeSalerNumber":null,"alternativeWholeSalerId":null,"alternativeWholeSalerNumber":null,"unitOfMeasure":null,"upc":null} """; ApiResponse a = await Util.PostAsync("part", token, payload); Util.ValidateDataReturnResponseOk(a); long Id = a.ObjectResponse["data"]["id"].Value(); Id.Should().BeGreaterThan(0); a.ObjectResponse["data"]["name"].Value().Should().Be(name); // GET a = await Util.GetAsync($"part/{Id}", token); Util.ValidateDataReturnResponseOk(a); a.ObjectResponse["data"]["name"].Value().Should().Be(name); a.ObjectResponse["data"]["cost"].Value().Should().Be(9.99m); var concurrency = a.ObjectResponse["data"]["concurrency"].Value(); // PUT (update name and cost) var updatedName = Util.Uniquify("Updated Part"); var putPayload = $$""" {"id":{{Id}},"concurrency":{{concurrency}},"name":"{{updatedName}}","active":true,"cost":11.99,"retail":14.99,"description":null,"notes":null,"wiki":null,"customFields":"{}","tags":[],"manufacturerId":null,"manufacturerNumber":null,"wholeSalerId":null,"wholeSalerNumber":null,"alternativeWholeSalerId":null,"alternativeWholeSalerNumber":null,"unitOfMeasure":null,"upc":null} """; a = await Util.PutAsync("part", token, putPayload); Util.ValidateHTTPStatusCode(a, 200); var newConcurrency = a.ObjectResponse["data"]["concurrency"].Value(); newConcurrency.Should().NotBe(concurrency, "concurrency should have been bumped by the update"); // Verify the update was persisted a = await Util.GetAsync($"part/{Id}", token); Util.ValidateDataReturnResponseOk(a); a.ObjectResponse["data"]["name"].Value().Should().Be(updatedName); a.ObjectResponse["data"]["cost"].Value().Should().Be(11.99m); // CONCURRENCY VIOLATION: PUT with stale concurrency should return 409 a = await Util.PutAsync("part", token, putPayload); // putPayload still has old concurrency Util.ValidateConcurrencyError(a); // DELETE a = await Util.DeleteAsync($"part/{Id}", token); Util.ValidateHTTPStatusCode(a, 204); // Confirm deleted a = await Util.GetAsync($"part/{Id}", token); Util.ValidateResponseNotFound(a); } /// /// Verifies that serial numbers can be written to and read back from a Part. /// [Fact] public async Task Serials_RoundTrip() { var token = await Util.GetTokenAsync("BizAdmin"); // CREATE a part to attach serials to var name = Util.Uniquify("Test Part"); var payload = $$""" {"id":0,"concurrency":0,"name":"{{name}}","active":true,"cost":9.99,"retail":14.99,"description":null,"notes":null,"wiki":null,"customFields":"{}","tags":[],"manufacturerId":null,"manufacturerNumber":null,"wholeSalerId":null,"wholeSalerNumber":null,"alternativeWholeSalerId":null,"alternativeWholeSalerNumber":null,"unitOfMeasure":null,"upc":null} """; ApiResponse a = await Util.PostAsync("part", token, payload); Util.ValidateDataReturnResponseOk(a); long Id = a.ObjectResponse["data"]["id"].Value(); Id.Should().BeGreaterThan(0); // GET serials — should be empty initially a = await Util.GetAsync($"part/serials/{Id}", token); Util.ValidateDataReturnResponseOk(a); Util.ValidateHTTPStatusCode(a, 200); ((JArray)a.ObjectResponse["data"]).Count.Should().Be(0); // PUT serials — write three serial numbers var serialsPayload = """["SN-A001","SN-A002","SN-A003"]"""; a = await Util.PutAsync($"part/serials/{Id}", token, serialsPayload); Util.ValidateHTTPStatusCode(a, 200); // GET serials again — verify all three are returned a = await Util.GetAsync($"part/serials/{Id}", token); Util.ValidateDataReturnResponseOk(a); Util.ValidateHTTPStatusCode(a, 200); var serials = (JArray)a.ObjectResponse["data"]; serials.Count.Should().Be(3); serials.Values().Should().Contain("SN-A001"); serials.Values().Should().Contain("SN-A002"); serials.Values().Should().Contain("SN-A003"); // Clean up: clear serials before deleting (parts with serials cannot be deleted) a = await Util.PutAsync($"part/serials/{Id}", token, "[]"); Util.ValidateHTTPStatusCode(a, 200); a = await Util.DeleteAsync($"part/{Id}", token); Util.ValidateHTTPStatusCode(a, 204); } }//eoc }//eons