zenstack/packages/clients/tanstack-query/test/react/json-null-serialization.test.tsx
Yiming Cao 22e0fd40a9
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
feat(tanstack-query): add useTransaction hook for sequential transactions (#2637)
2026-05-03 18:33:15 -07:00

137 lines
5.1 KiB
TypeScript

/**
* @vitest-environment happy-dom
*/
import { act, renderHook, waitFor } from '@testing-library/react';
import { deserialize, serialize } from '@zenstackhq/client-helpers/fetch';
import nock from 'nock';
import { describe, expect, it } from 'vitest';
import { AnyNull, DbNull, JsonNull, useClientQueries } from '../../src/react';
import { schema } from '../schemas/basic/schema-lite';
import { BASE_URL, createWrapper, registerCleanup } from './helpers';
registerCleanup();
describe('JSON null value serialization', () => {
it('encodes DbNull in query filter and includes serialization metadata in URL', async () => {
const { wrapper } = createWrapper();
let capturedUri = '';
nock(BASE_URL)
.get(/.*/)
.reply(200, function (uri) {
capturedUri = uri;
return { data: [] };
});
const { result } = renderHook(
() => useClientQueries(schema).user.useFindMany({ where: { name: DbNull } } as any),
{ wrapper },
);
await waitFor(() => expect(result.current.isSuccess).toBe(true));
const url = new URL(capturedUri, BASE_URL);
expect(url.searchParams.has('meta')).toBe(true);
const q = JSON.parse(decodeURIComponent(url.searchParams.get('q')!));
const meta = JSON.parse(decodeURIComponent(url.searchParams.get('meta')!));
const reconstructed = deserialize(q, meta.serialization) as any;
expect(reconstructed.where.name.__brand).toBe('DbNull');
});
it('encodes JsonNull in query filter and includes serialization metadata in URL', async () => {
const { wrapper } = createWrapper();
let capturedUri = '';
nock(BASE_URL)
.get(/.*/)
.reply(200, function (uri) {
capturedUri = uri;
return { data: [] };
});
const { result } = renderHook(
() => useClientQueries(schema).user.useFindMany({ where: { name: JsonNull } } as any),
{ wrapper },
);
await waitFor(() => expect(result.current.isSuccess).toBe(true));
const url = new URL(capturedUri, BASE_URL);
expect(url.searchParams.has('meta')).toBe(true);
const q = JSON.parse(decodeURIComponent(url.searchParams.get('q')!));
const meta = JSON.parse(decodeURIComponent(url.searchParams.get('meta')!));
const reconstructed = deserialize(q, meta.serialization) as any;
expect(reconstructed.where.name.__brand).toBe('JsonNull');
});
it('encodes AnyNull in query filter and includes serialization metadata in URL', async () => {
const { wrapper } = createWrapper();
let capturedUri = '';
nock(BASE_URL)
.get(/.*/)
.reply(200, function (uri) {
capturedUri = uri;
return { data: [] };
});
const { result } = renderHook(
() => useClientQueries(schema).user.useFindMany({ where: { name: AnyNull } } as any),
{ wrapper },
);
await waitFor(() => expect(result.current.isSuccess).toBe(true));
const url = new URL(capturedUri, BASE_URL);
expect(url.searchParams.has('meta')).toBe(true);
const q = JSON.parse(decodeURIComponent(url.searchParams.get('q')!));
const meta = JSON.parse(decodeURIComponent(url.searchParams.get('meta')!));
const reconstructed = deserialize(q, meta.serialization) as any;
expect(reconstructed.where.name.__brand).toBe('AnyNull');
});
it('encodes DbNull in mutation body with serialization metadata', async () => {
const { wrapper } = createWrapper();
let capturedBody: any;
nock(BASE_URL)
.post(/.*/)
.reply(200, function (_uri, body) {
capturedBody = body;
return { data: { id: '1', name: null } };
});
const { result } = renderHook(() => useClientQueries(schema).user.useCreate(), { wrapper });
act(() => result.current.mutate({ data: { email: 'test@example.com', name: DbNull } } as any));
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(capturedBody.meta?.serialization).toBeDefined();
const reconstructed = deserialize({ data: capturedBody.data }, capturedBody.meta.serialization) as any;
expect(reconstructed.data.name.__brand).toBe('DbNull');
});
it('deserializes null sentinels in server response back to branded instances', async () => {
const { wrapper } = createWrapper();
const responseData = { id: '1', email: 'test@example.com', name: DbNull };
const { data: serializedData, meta: serializedMeta } = serialize(responseData);
nock(BASE_URL)
.get(/.*/)
.reply(200, { data: serializedData, meta: { serialization: serializedMeta } });
const { result } = renderHook(() => useClientQueries(schema).user.useFindUnique({ where: { id: '1' } }), {
wrapper,
});
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect((result.current.data as any).name.__brand).toBe('DbNull');
});
});