@@ -6,7 +6,7 @@ import type { Type } from '@warp-drive/core/types/symbols';
66import { module , test , todo } from '@warp-drive/diagnostic' ;
77import type { TestContext } from '@warp-drive/diagnostic/-types' ;
88import { MockServerHandler } from '@warp-drive/holodeck' ;
9- import { POST } from '@warp-drive/holodeck/mock' ;
9+ import { PATCH , POST } from '@warp-drive/holodeck/mock' ;
1010import { JSONAPICache } from '@warp-drive/json-api' ;
1111import { 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
2735interface 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