Skip to content

fix(sharing): extract producers array from wrapped API response in fetchDrinkData - #286

Merged
richardthe3rd merged 3 commits into
mainfrom
claude/add-og-tags-Eo0rY
May 17, 2026
Merged

fix(sharing): extract producers array from wrapped API response in fetchDrinkData#286
richardthe3rd merged 3 commits into
mainfrom
claude/add-og-tags-Eo0rY

Conversation

@richardthe3rd

Copy link
Copy Markdown
Owner

The data API returns {"producers": [...], "timestamp": ...} but
fetchDrinkData was returning the whole object. findDrink then called
for...of on a plain object, which throws TypeError: not iterable —
silently falling back to the unmodified SPA for all crawlers.

Fix: extract data.producers when the response is an object, fall back
to treating the response as an array for forward-compatibility.

Add 5 missing unit tests for fetchDrinkData covering the wrapped format,
bare-array format, 404 responses, missing producers key, and URL construction.

https://claude.ai/code/session_014n8NH3HkZQUkGGjK2geKYg

…tchDrinkData

The data API returns {"producers": [...], "timestamp": ...} but
fetchDrinkData was returning the whole object. findDrink then called
for...of on a plain object, which throws TypeError: not iterable —
silently falling back to the unmodified SPA for all crawlers.

Fix: extract data.producers when the response is an object, fall back
to treating the response as an array for forward-compatibility.

Add 5 missing unit tests for fetchDrinkData covering the wrapped format,
bare-array format, 404 responses, missing producers key, and URL construction.

https://claude.ai/code/session_014n8NH3HkZQUkGGjK2geKYg
Copilot AI review requested due to automatic review settings May 17, 2026 15:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Cloudflare Pages “drink preview” sharing for crawlers by making fetchDrinkData return an iterable producers array when the data API wraps the response, preventing findDrink from throwing and falling back to the SPA for crawlers.

Changes:

  • Update fetchDrinkData to unwrap { producers: [...] } responses while still supporting bare-array responses.
  • Add unit tests covering wrapped vs bare-array responses, non-OK responses, missing producers, and URL construction.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
functions/_lib/drink-preview.js Unwraps the API response so fetchDrinkData yields producer arrays for findDrink.
functions/test/drink-preview.test.js Adds vitest coverage for fetchDrinkData response shapes and URL construction.

Comment thread functions/_lib/drink-preview.js Outdated
if (!response.ok) return null;
return response.json();
const data = await response.json();
return Array.isArray(data) ? data : (data.producers ?? null);
Comment thread functions/test/drink-preview.test.js Outdated
Comment on lines +199 to +241
vi.restoreAllMocks();
});

it('returns producers array from wrapped API response', async () => {
const producers = [{ id: 'adnams', name: 'Adnams', products: [] }];
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ producers, timestamp: '2025-01-01' }),
});
const result = await fetchDrinkData('cbf2025', 'beer');
expect(result).toEqual(producers);
});

it('returns array directly when API response is already an array', async () => {
const producers = [{ id: 'adnams', name: 'Adnams', products: [] }];
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve(producers),
});
const result = await fetchDrinkData('cbf2025', 'beer');
expect(result).toEqual(producers);
});

it('returns null when response is not ok', async () => {
global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 404 });
const result = await fetchDrinkData('cbf2025', 'beer');
expect(result).toBeNull();
});

it('returns null when wrapped response has no producers key', async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ timestamp: '2025-01-01' }),
});
const result = await fetchDrinkData('cbf2025', 'beer');
expect(result).toBeNull();
});

it('fetches the correct URL for a given festival and category', async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ producers: [] }),
});
…ction

Tests the full onRequest pipeline that was previously untested:
- Non-crawler passthrough (ASSETS.fetch called, no data fetch, no OG tags)
- Crawler OG injection (og:title, og:url, correct festival/category URL)
- Fallback paths (non-ok API, network error, drink not found)

Uses a minimal MockHTMLRewriter that appends to </head> without requiring
the Cloudflare Workers runtime or any new dependencies.

https://claude.ai/code/session_014n8NH3HkZQUkGGjK2geKYg
@github-actions

github-actions Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

LCOV of commit 8acc8db during CI #216

Summary coverage rate:
  lines......: 77.7% (2482 of 3195 lines)
  functions..: no data found
  branches...: no data found

Files changed coverage rate: n/a

@codecov

codecov Bot commented May 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…ock leak

fetchDrinkData now guards against data.producers being non-array and
data being null, returning null in both cases rather than returning an
uninitable value that would make findDrink throw.

Test hygiene: replace global.fetch/HTMLRewriter direct assignment with
vi.stubGlobal/vi.unstubAllGlobals so stubs are properly restored between
test files without relying on vi.restoreAllMocks (which doesn't restore
globalThis properties set by direct assignment).

Two new fetchDrinkData cases: null JSON body and non-array producers field.

https://claude.ai/code/session_014n8NH3HkZQUkGGjK2geKYg

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@richardthe3rd

Copy link
Copy Markdown
Owner Author

@copilot re-review

Copilot AI commented May 17, 2026

Copy link
Copy Markdown
Contributor

@copilot re-review

Re-reviewed: the previously flagged issues are addressed in 8acc8db. fetchDrinkData now only returns arrays (including safe handling for null / non-array producers), test fetch mocking now uses vi.stubGlobal with vi.unstubAllGlobals, and ./bin/mise run check passes.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • unofficial-builds.nodejs.org
    • Triggering command: /home/REDACTED/work/cambridge-beer-festival-app/cambridge-beer-festival-app/.mise/mise-2026.5.8 ./bin/mise run check (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@richardthe3rd
richardthe3rd merged commit bfa5208 into main May 17, 2026
15 checks passed
@richardthe3rd
richardthe3rd deleted the claude/add-og-tags-Eo0rY branch May 17, 2026 16:12
This was referenced May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants