Make PriceCharting runs auditable and pin matches by product id
Two changes aimed at the first real run, since the integration cannot be exercised here without a subscription. Refresh now reports which product each game matched — name, console and the source's id — next to the prices, and dry-run surfaces it before anything is written. This is the failure that would otherwise go unnoticed: a lookup for the DS "Chrono Trigger" resolving to the SNES original returns entirely plausible numbers for the wrong game, and nothing in a bare price would say so. The matched id is then stored on the game, and later refreshes look it up directly instead of repeating the title search. Cheaper, and stable — a search that drifts to a different edition next month cannot silently re-price something that was already matched correctly. IPriceProvider takes an optional sourceId so this stays provider-agnostic. eBay ignores it, having no stable per-product identifier in Browse. 139 backend tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -367,3 +367,60 @@ public class PriceEndpointTests(LudosApiFactory factory) : IClassFixture<LudosAp
|
||||
decimal? ValueLoose, decimal? ValueCib, decimal? ValueNew);
|
||||
private record PagePayload(List<GamePayload> Items, int Total);
|
||||
}
|
||||
|
||||
public class PriceChartingMatchTests
|
||||
{
|
||||
[Fact]
|
||||
public void The_matched_product_and_console_are_reported()
|
||||
{
|
||||
using var document = JsonDocument.Parse("""
|
||||
{
|
||||
"status": "success",
|
||||
"id": "6910",
|
||||
"product-name": "Chrono Trigger",
|
||||
"console-name": "Super Nintendo",
|
||||
"loose-price": 12800, "cib-price": 65000, "new-price": 1200000
|
||||
}
|
||||
""");
|
||||
|
||||
var estimate = PriceChartingProvider.Parse(document);
|
||||
|
||||
// Without this a dry run cannot tell a DS entry that resolved to the
|
||||
// SNES original from one that resolved correctly.
|
||||
Assert.Equal("Chrono Trigger", estimate.MatchedName);
|
||||
Assert.Equal("Super Nintendo", estimate.MatchedConsole);
|
||||
Assert.Equal("6910", estimate.SourceId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_numeric_id_is_read_as_a_string()
|
||||
{
|
||||
using var document = JsonDocument.Parse("""
|
||||
{ "status": "success", "id": 6910, "loose-price": 100 }
|
||||
""");
|
||||
|
||||
Assert.Equal("6910", PriceChartingProvider.Parse(document).SourceId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Missing_match_metadata_is_not_fatal()
|
||||
{
|
||||
using var document = JsonDocument.Parse("""{ "loose-price": 12800 }""");
|
||||
|
||||
var estimate = PriceChartingProvider.Parse(document);
|
||||
|
||||
Assert.Equal(128.00m, estimate.Loose);
|
||||
Assert.Null(estimate.MatchedName);
|
||||
Assert.Null(estimate.SourceId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_stored_id_is_preferred_over_a_title_search()
|
||||
{
|
||||
// Documents the intent of the lookup switch: with an id, the query is an
|
||||
// exact product fetch, so a drifting title search cannot re-price a
|
||||
// different edition on a later run.
|
||||
Assert.Equal("super nintendo Chrono Trigger",
|
||||
PriceChartingProvider.BuildQuery("Chrono Trigger", "SNES"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user