Skip to content

Commit c11047a

Browse files
committed
test(json-api): add repeated hasMany patch regression
1 parent 2ab1fd0 commit c11047a

1 file changed

Lines changed: 195 additions & 1 deletion

File tree

tests/json-api/tests/integration/cache/mutation-request-test.ts

Lines changed: 195 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { Type } from '@warp-drive/core/types/symbols';
66
import { module, test, todo } from '@warp-drive/diagnostic';
77
import type { TestContext } from '@warp-drive/diagnostic/-types';
88
import { MockServerHandler } from '@warp-drive/holodeck';
9-
import { POST } from '@warp-drive/holodeck/mock';
9+
import { PATCH, POST } from '@warp-drive/holodeck/mock';
1010
import { JSONAPICache } from '@warp-drive/json-api';
1111
import { buildBaseURL } from '@warp-drive/utilities';
1212

@@ -22,6 +22,14 @@ interface ExistingUser {
2222
id: string;
2323
firstName: string;
2424
lastName: string;
25+
pets: ExistingPet[];
26+
}
27+
28+
interface ExistingPet {
29+
[Type]: 'pet';
30+
id: string;
31+
name: string;
32+
owner: ExistingUser | null;
2533
}
2634

2735
interface CustomContext extends TestContext {
@@ -39,6 +47,24 @@ module<CustomContext>('mutation-request', function (hooks) {
3947
fields: [
4048
{ name: 'firstName', kind: 'field' },
4149
{ name: 'lastName', kind: 'field' },
50+
{
51+
name: 'pets',
52+
kind: 'hasMany',
53+
type: 'pet',
54+
options: { inverse: 'owner', async: false, linksMode: true },
55+
},
56+
],
57+
}),
58+
withDefaults({
59+
type: 'pet',
60+
fields: [
61+
{ name: 'name', kind: 'field' },
62+
{
63+
name: 'owner',
64+
kind: 'belongsTo',
65+
type: 'user',
66+
options: { inverse: 'pets', async: false, linksMode: true },
67+
},
4268
],
4369
}),
4470
],
@@ -108,6 +134,174 @@ module<CustomContext>('mutation-request', function (hooks) {
108134
assert.equal(user2?.id, 'id2', 'second record has correct id');
109135
});
110136

137+
test<CustomContext>('update hasMany with repeated patch', async function (assert) {
138+
const { store } = this;
139+
const url = buildBaseURL({ resourcePath: 'api/user/1' });
140+
141+
store.push({
142+
data: [
143+
{
144+
type: 'user',
145+
id: '1',
146+
attributes: { firstName: 'Chris', lastName: 'Thoburn' },
147+
relationships: {
148+
pets: {
149+
links: {
150+
self: '/api/user/1/relationships/pets',
151+
related: '/api/user/1/pets',
152+
},
153+
data: [{ type: 'pet', id: '1' }],
154+
},
155+
},
156+
},
157+
{
158+
type: 'user',
159+
id: '2',
160+
attributes: { firstName: 'Tom', lastName: 'Dale' },
161+
relationships: {
162+
pets: {
163+
links: {
164+
self: '/api/user/2/relationships/pets',
165+
related: '/api/user/2/pets',
166+
},
167+
data: [{ type: 'pet', id: '2' }],
168+
},
169+
},
170+
},
171+
],
172+
included: [
173+
{
174+
type: 'pet',
175+
id: '1',
176+
attributes: { name: 'Rey' },
177+
relationships: {
178+
owner: {
179+
links: {
180+
self: '/api/pet/1/relationships/owner',
181+
related: '/api/pet/1/owner',
182+
},
183+
data: { type: 'user', id: '1' },
184+
},
185+
},
186+
},
187+
{
188+
type: 'pet',
189+
id: '2',
190+
attributes: { name: 'Pixel' },
191+
relationships: {
192+
owner: {
193+
links: {
194+
self: '/api/pet/2/relationships/owner',
195+
related: '/api/pet/2/owner',
196+
},
197+
data: { type: 'user', id: '2' },
198+
},
199+
},
200+
},
201+
],
202+
});
203+
204+
const pet1 = store.peekRecord<ExistingPet>('pet', '1');
205+
const pet2 = store.peekRecord<ExistingPet>('pet', '2');
206+
const user1 = store.peekRecord<ExistingUser>('user', '1');
207+
const user2 = store.peekRecord<ExistingUser>('user', '2');
208+
209+
const lid = recordIdentifierFor(user1);
210+
211+
const patchUser1 = async (petIds: string[]) => {
212+
const reqBody = JSON.stringify({
213+
data: {
214+
type: 'user',
215+
id: '1',
216+
relationships: {
217+
pets: {
218+
data: petIds.map((id) => ({ type: 'pet', id })),
219+
},
220+
},
221+
},
222+
});
223+
224+
await PATCH(
225+
this,
226+
'/api/user/1',
227+
() => {
228+
return {
229+
data: {
230+
type: 'user',
231+
id: '1',
232+
attributes: { firstName: 'Chris', lastName: 'Thoburn' },
233+
relationships: {
234+
pets: {
235+
links: {
236+
self: '/api/user/1/relationships/pets',
237+
related: '/api/user/1/pets',
238+
},
239+
data: petIds.map((id) => ({ type: 'pet', id })),
240+
},
241+
},
242+
},
243+
};
244+
},
245+
{
246+
body: reqBody,
247+
}
248+
);
249+
250+
await this.store.request(
251+
withReactiveResponse<ExistingUser>({
252+
op: 'updateRecord',
253+
url,
254+
method: 'PATCH',
255+
body: reqBody,
256+
records: [lid],
257+
})
258+
);
259+
};
260+
261+
assert.equal(pet1?.owner?.id, '1', 'first pet starts on the first user');
262+
assert.equal(pet2?.owner?.id, '2', 'second pet starts on the second user');
263+
assert.deepEqual(
264+
user1?.pets.map((value) => value.id),
265+
['1'],
266+
'first user starts with the first pet'
267+
);
268+
assert.deepEqual(
269+
user2?.pets.map((value) => value.id),
270+
['2'],
271+
'second user starts with the second pet'
272+
);
273+
274+
await patchUser1(['1', '2']);
275+
276+
assert.equal(pet1?.owner?.id, '1', 'first pet stays on the first user after the first patch');
277+
assert.equal(pet2?.owner?.id, '1', 'second pet moves to the first user after the first patch');
278+
assert.deepEqual(
279+
user1?.pets.map((value) => value.id),
280+
['1', '2'],
281+
'first user has both pets after the first patch'
282+
);
283+
assert.deepEqual(
284+
user2?.pets.map((value) => value.id),
285+
[],
286+
'second user pets are cleared after the first patch'
287+
);
288+
289+
await patchUser1(['1']);
290+
291+
assert.equal(pet1?.owner?.id, '1', 'first pet stays on the first user after the second patch');
292+
assert.equal(pet2?.owner, null, 'second pet owner is cleared after the second patch');
293+
assert.deepEqual(
294+
user1?.pets.map((value) => value.id),
295+
['1'],
296+
'first user is reduced to the first pet'
297+
);
298+
assert.deepEqual(
299+
user2?.pets.map((value) => value.id),
300+
[],
301+
'second user still has no pets after the second patch'
302+
);
303+
});
304+
111305
todo('bulk delete', function (assert) {});
112306
todo('bulk update with 204 response', function (assert) {});
113307
todo('bulk-delete with 204 response', function (assert) {});

0 commit comments

Comments
 (0)