From 8963bb53d9e6e5b5b997ec43edc7c6d3120366f4 Mon Sep 17 00:00:00 2001 From: mitchav66 Date: Wed, 15 Jul 2026 19:53:28 -0500 Subject: [PATCH 1/4] Persist exact listed price for GE offers Updated the comment for listedPrice to clarify its purpose and added the @SerializedName annotation. --- src/main/java/com/flippingutilities/model/OfferEvent.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/flippingutilities/model/OfferEvent.java b/src/main/java/com/flippingutilities/model/OfferEvent.java index 2afe02e..2a95f87 100644 --- a/src/main/java/com/flippingutilities/model/OfferEvent.java +++ b/src/main/java/com/flippingutilities/model/OfferEvent.java @@ -93,8 +93,12 @@ public class OfferEvent //Used in theGeHistoryTabOfferPanel and RecipeFlipPanel private transient String itemName; - //used in the live slot view to show what price something was listed at - private transient int listedPrice; + // Exact limit price posted in the GE. This is distinct from `price`, which + // is the average execution price of filled units. Persisting it lets other + // local tools and a restarted client recover the price of an unfilled live + // offer; older account files simply deserialize it as 0. + @SerializedName("lp") + private int listedPrice; private transient int spent; /** From da8e085631bc9735eff740a7fd2d64f42583a78b Mon Sep 17 00:00:00 2001 From: mitchav66 Date: Wed, 15 Jul 2026 19:54:11 -0500 Subject: [PATCH 2/4] Test listed-price serialization --- .../OfferEventSerializationTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/test/java/com/flippingutilities/OfferEventSerializationTest.java diff --git a/src/test/java/com/flippingutilities/OfferEventSerializationTest.java b/src/test/java/com/flippingutilities/OfferEventSerializationTest.java new file mode 100644 index 0000000..cb74ac4 --- /dev/null +++ b/src/test/java/com/flippingutilities/OfferEventSerializationTest.java @@ -0,0 +1,37 @@ +package com.flippingutilities; + +import com.flippingutilities.model.OfferEvent; +import com.google.gson.Gson; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class OfferEventSerializationTest +{ + private final Gson gson = new Gson(); + + @Test + public void listedPriceIsPersistedSeparatelyFromExecutionPrice() + { + OfferEvent offer = new OfferEvent(); + offer.setPrice(91); + offer.setListedPrice(94); + + String json = gson.toJson(offer); + assertTrue(json.contains("\"p\":91")); + assertTrue(json.contains("\"lp\":94")); + + OfferEvent restored = gson.fromJson(json, OfferEvent.class); + assertEquals(91, restored.getPreTaxPrice()); + assertEquals(94, restored.getListedPrice()); + } + + @Test + public void oldAccountFilesDefaultListedPriceToUnknown() + { + OfferEvent restored = gson.fromJson("{\"p\":91}", OfferEvent.class); + assertEquals(91, restored.getPreTaxPrice()); + assertEquals(0, restored.getListedPrice()); + } +} From a7da818989c1459303f5c38cf07476c066bf3907 Mon Sep 17 00:00:00 2001 From: Mitchel Date: Wed, 22 Jul 2026 13:12:29 -0500 Subject: [PATCH 3/4] Assert the serialization round trip structurally Parse the serialized offer into a JsonObject instead of substring-matching the raw string. `json.contains("\"lp\":94")` is sensitive to formatting, cannot distinguish a top-level key from a nested one, and can be satisfied by a longer key that happens to end in the one under test. Add the equal-price case, which a substring assertion cannot check at all: when the listed and execution prices are both 91 there is a single distinct value in the document, so `contains` passes even if only one of the two keys was written. Co-Authored-By: Claude Opus 4.8 --- .../OfferEventSerializationTest.java | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/flippingutilities/OfferEventSerializationTest.java b/src/test/java/com/flippingutilities/OfferEventSerializationTest.java index cb74ac4..c54d6b8 100644 --- a/src/test/java/com/flippingutilities/OfferEventSerializationTest.java +++ b/src/test/java/com/flippingutilities/OfferEventSerializationTest.java @@ -2,15 +2,30 @@ import com.flippingutilities.model.OfferEvent; import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class OfferEventSerializationTest { private final Gson gson = new Gson(); + /** + * Parsed structurally rather than matched as a substring. `json.contains("\"p\":91")` + * asserts nothing about the document's shape: it is sensitive to formatting, it cannot + * tell a top-level key from one nested somewhere else, and it can be satisfied by a + * longer key that happens to end in the one being looked for. Reading the field off a + * parsed object tests what the assertion is actually meant to claim. + */ + private JsonObject serialize(OfferEvent offer) + { + return new JsonParser().parse(gson.toJson(offer)).getAsJsonObject(); + } + @Test public void listedPriceIsPersistedSeparatelyFromExecutionPrice() { @@ -18,19 +33,45 @@ public void listedPriceIsPersistedSeparatelyFromExecutionPrice() offer.setPrice(91); offer.setListedPrice(94); - String json = gson.toJson(offer); - assertTrue(json.contains("\"p\":91")); - assertTrue(json.contains("\"lp\":94")); + JsonObject json = serialize(offer); + assertTrue(json.has("p")); + assertTrue(json.has("lp")); + assertEquals(91, json.get("p").getAsInt()); + assertEquals(94, json.get("lp").getAsInt()); OfferEvent restored = gson.fromJson(json, OfferEvent.class); assertEquals(91, restored.getPreTaxPrice()); assertEquals(94, restored.getListedPrice()); } + /** + * The equal-price case is the one a substring assertion cannot distinguish at all: + * with both fields at 91 there is only one distinct value in the document, so a + * `contains` check passes even if only one of the two keys was written. + */ + @Test + public void listedPriceIsWrittenEvenWhenItEqualsExecutionPrice() + { + OfferEvent offer = new OfferEvent(); + offer.setPrice(91); + offer.setListedPrice(91); + + JsonObject json = serialize(offer); + assertEquals(91, json.get("p").getAsInt()); + assertEquals(91, json.get("lp").getAsInt()); + + OfferEvent restored = gson.fromJson(json, OfferEvent.class); + assertEquals(91, restored.getPreTaxPrice()); + assertEquals(91, restored.getListedPrice()); + } + @Test public void oldAccountFilesDefaultListedPriceToUnknown() { - OfferEvent restored = gson.fromJson("{\"p\":91}", OfferEvent.class); + JsonObject legacy = new JsonParser().parse("{\"p\":91}").getAsJsonObject(); + assertFalse(legacy.has("lp")); + + OfferEvent restored = gson.fromJson(legacy, OfferEvent.class); assertEquals(91, restored.getPreTaxPrice()); assertEquals(0, restored.getListedPrice()); } From b83b289bf2f20d27aaf3290197fafa40ce235a4c Mon Sep 17 00:00:00 2001 From: Mitchel Date: Wed, 22 Jul 2026 15:10:53 -0500 Subject: [PATCH 4/4] Document the remaining serialization test cases Javadoc on the round-trip and backward-compatibility cases, stating what each one guarantees rather than restating the assertions. Co-Authored-By: Claude Opus 4.8 --- .../flippingutilities/OfferEventSerializationTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/com/flippingutilities/OfferEventSerializationTest.java b/src/test/java/com/flippingutilities/OfferEventSerializationTest.java index c54d6b8..03be0f7 100644 --- a/src/test/java/com/flippingutilities/OfferEventSerializationTest.java +++ b/src/test/java/com/flippingutilities/OfferEventSerializationTest.java @@ -26,6 +26,11 @@ private JsonObject serialize(OfferEvent offer) return new JsonParser().parse(gson.toJson(offer)).getAsJsonObject(); } + /** + * The core guarantee: the two prices survive a round trip as separate fields. `p` remains + * the average execution price and `lp` carries the exact price the offer was listed at, so + * a live offer that has filled nothing still persists a usable price. + */ @Test public void listedPriceIsPersistedSeparatelyFromExecutionPrice() { @@ -65,6 +70,11 @@ public void listedPriceIsWrittenEvenWhenItEqualsExecutionPrice() assertEquals(91, restored.getListedPrice()); } + /** + * Backward compatibility: account files written before `lp` existed simply omit the key, + * and must keep deserializing. The field reads 0, which is the sentinel for "no listed + * price recorded" — the same value an offer carried under the old transient behaviour. + */ @Test public void oldAccountFilesDefaultListedPriceToUnknown() {