Thread book_role through the odds route grouping
The route regroups flat props into lines[] and was dropping the role tag, so the widened feed reached the browser untagged. That is not cosmetic: on a live prop, PrizePicks prices both sides at even money (+100/+100) while BetMGM has +450/-750. Rendered side by side without a tag, the pick'em row reads as a dramatically better price when it is a different product entirely -- exactly the confusion the three-way split exists to prevent. Consumers gate on book_role !== 'dfs' before treating a row as a market price. The ?book= filter now accepts any DISPLAY book, since shopping a real book against an exchange is the point of the widening. Grading still only ever consumes MODEL_BOOKS. One superseded integration test updated to a stronger pair: an unknown book still 400s, and a newly-visible one no longer does. Gates: 4,028 tests / 322 suites green; next build exit 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
This commit is contained in:
@@ -194,11 +194,23 @@ describe('GET /api/odds/nba', () => {
|
||||
expect(res.body.error).toContain('Invalid stat_type');
|
||||
});
|
||||
|
||||
it('returns 400 for invalid book', async () => {
|
||||
const res = await request(app).get('/api/odds/nba?book=bovada').expect(400);
|
||||
// SUPERSEDED 2026-08-01 (Order Zero). `bovada` used to be an invalid filter
|
||||
// value because we discarded it. It is now a DISPLAY book — the whole point
|
||||
// of the widening is that a user can shop a real book against an exchange —
|
||||
// so the property that replaces this is that a genuinely unknown book still
|
||||
// 400s, while a newly-visible one no longer does.
|
||||
it('returns 400 for a book that does not exist', async () => {
|
||||
const res = await request(app).get('/api/odds/nba?book=not_a_real_book').expect(400);
|
||||
expect(res.body.error).toContain('Invalid book');
|
||||
});
|
||||
|
||||
it('accepts a newly-visible DISPLAY book as a filter value', async () => {
|
||||
const { DISPLAY_BOOKS } = require('../../src/config/bookRoles');
|
||||
expect(DISPLAY_BOOKS.has('bovada')).toBe(true);
|
||||
const res = await request(app).get('/api/odds/nba?book=bovada');
|
||||
expect(res.status).not.toBe(400); // no longer rejected at validation
|
||||
});
|
||||
|
||||
it('returns stale cache data when API fails', async () => {
|
||||
mockRedis.get
|
||||
.mockResolvedValueOnce(null) // cache miss
|
||||
|
||||
Reference in New Issue
Block a user