fix: stop protecting stale, unverified pc-builder.io catalog prices
Bug found: "Gigabyte GAMING OC GeForce RTX 5070 Ti 16 GB Video Card" kept showing a stale price of 431.17 even after pc-builder.io itself no longer had a fixed offer for it (its "Add to Build" HTML only had a generic Amazon search link, no .price-box element at all, confirmed by fetching the live page directly). Root cause: scrapeComponentType's "never lose a better price" rule (added to make ScrapedPartsController#compare's persisted merchant prices durable across catalog refreshes) was too broad. It treated ANY existing price as worth protecting whenever the incoming catalog price was null or higher - including a price that had only ever come from a *previous* pc-builder.io scrape itself (priceSource: null), with no independent verification. Since pc-builder.io's own listings change constantly (a GPU can lose its priced Amazon link if the referenced ASIN goes out of stock), that logic effectively froze old pc-builder.io prices forever even after the site removed them. Fix: extracted the decision into shouldKeepExistingPrice(), which now only protects a price when priceSource is a real merchant name (set by updatePriceIfCheaper after a live comparison confirmed it on the merchant's own page) - never when priceSource is null (an unverified, previous pc-builder.io catalog price). A plain re-scrape can always correct/clear an unverified price, matching whatever pc-builder.io currently shows. 4 new unit tests for shouldKeepExistingPrice covering: unverified pc-builder.io price gets overwritten (the regression case), verified merchant price is protected from worse/missing catalog data, verified merchant price is still replaced by a genuinely cheaper catalog price, and no-existing-part returns false. Also re-ran `node ace scrape:parts` to apply the fix to already-cached data with stale unverified prices (affected ~1500 rows across the catalog before the fix, mostly likely unaffected in practice but no longer silently trusted going forward).
This commit is contained in:
1 parent
a1f6ff419a
commit
21d56a5da4
2 files changed
+66
-10
No files matched your search
@@ -7,6 +7,7 @@ import {
|
||||
isCategoryStale,
|
||||
ensureFreshCategory,
|
||||
updatePriceIfCheaper,
|
||||
shouldKeepExistingPrice,
|
||||
CACHE_TTL_HOURS,
|
||||
} from '#services/part_scraper_service'
|
||||
|
||||
@@ -215,3 +216,38 @@ test.group('Best-known-price persistence', () => {
|
||||
assert.equal(part.priceSource, 'Grosbill')
|
||||
})
|
||||
})
|
||||
|
||||
test.group('Catalog refresh price protection (shouldKeepExistingPrice)', () => {
|
||||
test('an unverified pc-builder.io price is NOT protected and gets overwritten', ({ assert }) => {
|
||||
// Regression test: a GPU had price=431.17 from a previous pc-builder.io
|
||||
// catalog scrape (priceSource: null, never independently verified).
|
||||
// pc-builder.io later stopped showing a fixed price for it (only a
|
||||
// generic Amazon search link). The stale 431.17 must NOT be kept.
|
||||
const existing = { price: 431.17, priceSource: null }
|
||||
|
||||
assert.isFalse(shouldKeepExistingPrice(existing, null))
|
||||
assert.isFalse(shouldKeepExistingPrice(existing, 999))
|
||||
})
|
||||
|
||||
test('a verified merchant price IS protected from a worse/missing catalog price', ({
|
||||
assert,
|
||||
}) => {
|
||||
const existing = { price: 350, priceSource: 'LDLC' }
|
||||
|
||||
assert.isTrue(shouldKeepExistingPrice(existing, null))
|
||||
assert.isTrue(shouldKeepExistingPrice(existing, 500))
|
||||
})
|
||||
|
||||
test('a verified merchant price is replaced if the catalog price is actually cheaper', ({
|
||||
assert,
|
||||
}) => {
|
||||
const existing = { price: 350, priceSource: 'LDLC' }
|
||||
|
||||
assert.isFalse(shouldKeepExistingPrice(existing, 200))
|
||||
})
|
||||
|
||||
test('no existing part means nothing to keep', ({ assert }) => {
|
||||
assert.isFalse(shouldKeepExistingPrice(null, 100))
|
||||
assert.isFalse(shouldKeepExistingPrice(null, null))
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user