bugfix: don't JSON.parse empty 200/201 bodies (0.19.1)
$(addMangaToCollection crashed when the backend returned 201/200 with no body — the shared client only short-circuited 204. Now any empty body returns undefined.) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2
backend/Cargo.lock
generated
2
backend/Cargo.lock
generated
@@ -1033,7 +1033,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mangalord"
|
name = "mangalord"
|
||||||
version = "0.19.0"
|
version = "0.19.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argon2",
|
"argon2",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mangalord"
|
name = "mangalord"
|
||||||
version = "0.19.0"
|
version = "0.19.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mangalord-frontend",
|
"name": "mangalord-frontend",
|
||||||
"version": "0.19.0",
|
"version": "0.19.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, it, expect, vi, beforeEach, afterEach, type MockInstance } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, afterEach, type MockInstance } from 'vitest';
|
||||||
import { ApiError } from './client';
|
import { ApiError, request } from './client';
|
||||||
import { getManga } from './mangas';
|
import { getManga } from './mangas';
|
||||||
|
|
||||||
describe('request error envelope parsing', () => {
|
describe('request error envelope parsing', () => {
|
||||||
@@ -48,6 +48,20 @@ describe('request error envelope parsing', () => {
|
|||||||
expect(err.code).toBe('http_error');
|
expect(err.code).toBe('http_error');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('treats empty 200/201 bodies as undefined (no JSON.parse crash)', async () => {
|
||||||
|
// Regression: addMangaToCollection is typed `void` and the
|
||||||
|
// backend returns 201 (created) / 200 (already there) with
|
||||||
|
// no body. Without the empty-body short-circuit, `res.json()`
|
||||||
|
// would throw `JSON.parse: unexpected end of data`.
|
||||||
|
fetchSpy.mockResolvedValueOnce(new Response(null, { status: 201 }));
|
||||||
|
const created = await request<void>('/v1/whatever', { method: 'POST' });
|
||||||
|
expect(created).toBeUndefined();
|
||||||
|
|
||||||
|
fetchSpy.mockResolvedValueOnce(new Response(null, { status: 200 }));
|
||||||
|
const ok200 = await request<void>('/v1/whatever', { method: 'POST' });
|
||||||
|
expect(ok200).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
it('falls back to http_error code when JSON has no error envelope', async () => {
|
it('falls back to http_error code when JSON has no error envelope', async () => {
|
||||||
fetchSpy.mockResolvedValueOnce(
|
fetchSpy.mockResolvedValueOnce(
|
||||||
new Response(JSON.stringify({ message: 'oops' }), {
|
new Response(JSON.stringify({ message: 'oops' }), {
|
||||||
|
|||||||
@@ -56,10 +56,18 @@ export async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
|||||||
}
|
}
|
||||||
throw new ApiError(res.status, code, message);
|
throw new ApiError(res.status, code, message);
|
||||||
}
|
}
|
||||||
|
// Any empty body (not just 204) returns undefined — the manga-add
|
||||||
|
// endpoint, for instance, signals create-vs-already-present via
|
||||||
|
// 201/200 with no body, and callers typed `request<void>` would
|
||||||
|
// otherwise blow up on `res.json()` parsing an empty string.
|
||||||
if (res.status === 204) {
|
if (res.status === 204) {
|
||||||
return undefined as T;
|
return undefined as T;
|
||||||
}
|
}
|
||||||
return (await res.json()) as T;
|
const text = await res.text();
|
||||||
|
if (!text) {
|
||||||
|
return undefined as T;
|
||||||
|
}
|
||||||
|
return JSON.parse(text) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Manga = {
|
export type Manga = {
|
||||||
|
|||||||
Reference in New Issue
Block a user