From bdea2d92ca79a6463a10eca33b14f81429d6e803 Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Mon, 27 Aug 2018 15:14:17 -0700 Subject: [PATCH] [BUGFIX] Reference.reload should not cause sync-relationship assertion (#5582) * refactor reload tests and add tests for reference reloading * dont assert loaded state if reloading * only disable data integrity checks for the forced reload itself --- .../system/relationships/state/belongs-to.js | 10 +- .../system/relationships/state/has-many.js | 4 +- .../relationships/state/relationship.js | 2 +- .../acceptance/relationships/has-many-test.js | 2 +- tests/integration/records/reload-test.js | 832 ++++++++++++++---- 5 files changed, 659 insertions(+), 191 deletions(-) diff --git a/addon/-private/system/relationships/state/belongs-to.js b/addon/-private/system/relationships/state/belongs-to.js index a038df3e859..3d308b3b6d5 100644 --- a/addon/-private/system/relationships/state/belongs-to.js +++ b/addon/-private/system/relationships/state/belongs-to.js @@ -211,7 +211,13 @@ export default class BelongsToRelationship extends Relationship { }); } - getData() { + /* + While the `shouldForceReload` flag will also be true when `isForcedReload` is true, + `isForcedReload` is only `true` for an initial `getData` call during a forced reload. + Other calls must conform to the typical expectations, for instance, sync relationships + expect that their data is already loaded. + */ + getData(isForcedReload = false) { //TODO(Igor) flushCanonical here once our syncing is not stupid let record = this.inverseInternalModel ? this.inverseInternalModel.getRecord() : null; @@ -250,7 +256,7 @@ export default class BelongsToRelationship extends Relationship { "' with id " + this.internalModel.id + ' but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.belongsTo({ async: true })`)', - record === null || !record.get('isEmpty') + record === null || !record.get('isEmpty') || isForcedReload ); return record; } diff --git a/addon/-private/system/relationships/state/has-many.js b/addon/-private/system/relationships/state/has-many.js index d44a114877c..911c2f5924a 100755 --- a/addon/-private/system/relationships/state/has-many.js +++ b/addon/-private/system/relationships/state/has-many.js @@ -343,7 +343,7 @@ export default class ManyRelationship extends Relationship { }); } - getData() { + getData(isForcedReload = false) { //TODO(Igor) sync server here, once our syncing is not stupid let manyArray = this.manyArray; @@ -378,7 +378,7 @@ export default class ManyRelationship extends Relationship { }' with id ${ this.internalModel.id } but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (\`DS.hasMany({ async: true })\`)`, - this.allInverseRecordsAreLoaded + this.allInverseRecordsAreLoaded || isForcedReload ); return manyArray; diff --git a/addon/-private/system/relationships/state/relationship.js b/addon/-private/system/relationships/state/relationship.js index fc9477c6a84..9ba21737e66 100644 --- a/addon/-private/system/relationships/state/relationship.js +++ b/addon/-private/system/relationships/state/relationship.js @@ -543,7 +543,7 @@ export default class Relationship { this.setHasFailedLoadAttempt(false); this.setShouldForceReload(true); - this.getData(); + this.getData(true); return this._promiseProxy; } diff --git a/tests/acceptance/relationships/has-many-test.js b/tests/acceptance/relationships/has-many-test.js index b62329848c1..6938b45ab24 100644 --- a/tests/acceptance/relationships/has-many-test.js +++ b/tests/acceptance/relationships/has-many-test.js @@ -30,7 +30,7 @@ class TestAdapter extends JSONAPIAdapter { this._payloads = arr; } - shouldBackgroundReload() { + shouldBackgroundReloadRecord() { return false; } diff --git a/tests/integration/records/reload-test.js b/tests/integration/records/reload-test.js index 694d801ecb5..23e1f29f6a4 100644 --- a/tests/integration/records/reload-test.js +++ b/tests/integration/records/reload-test.js @@ -1,228 +1,690 @@ import { resolve, reject } from 'rsvp'; -import { run } from '@ember/runloop'; import { get } from '@ember/object'; -import setupStore from 'dummy/tests/helpers/store'; - +import { setupTest } from 'ember-qunit'; import { module, test } from 'qunit'; - -import DS from 'ember-data'; - -var attr = DS.attr; -var Person, env; - -module("integration/reload - Reloading Records", { - beforeEach() { - Person = DS.Model.extend({ - updatedAt: attr('string'), - name: attr('string'), - firstName: attr('string'), - lastName: attr('string') +import Store from 'ember-data/store'; +import JSONAPIAdapter from 'ember-data/adapters/json-api'; +import JSONAPISerializer from 'ember-data/serializers/json-api'; +import Model from 'ember-data/model'; +import attr from 'ember-data/attr'; +import { belongsTo, hasMany } from 'ember-data/relationships'; + +module('integration/reload - Reloading Records', function(hooks) { + let store; + setupTest(hooks); + + hooks.beforeEach(function() { + const Person = Model.extend({ + updatedAt: attr(), + name: attr(), + firstName: attr(), + lastName: attr() }); - env = setupStore({ person: Person }); - }, + let { owner } = this; + owner.register('service:store', Store); + owner.register('model:person', Person); + owner.register( + 'serializer:application', + JSONAPISerializer.extend({ + normalizeResponse(_, __, jsonApiPayload) { + return jsonApiPayload; + }, + }) + ); + store = owner.lookup('service:store'); + }); - afterEach() { - run(env.container, 'destroy'); - } -}); + test("When a single record is requested, the adapter's find method should be called unless it's loaded.", async function(assert) { + let count = 0; + let reloadOptions = { + adapterOptions: { + makeSnazzy: true, + }, + }; + + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + shouldBackgroundReloadRecord() { + return false; + }, + + findRecord(store, type, id, snapshot) { + if (count === 0) { + count++; + return resolve({ data: { id: id, type: 'person', attributes: { name: 'Tom Dale' } } }); + } else if (count === 1) { + assert.equal( + snapshot.adapterOptions, + reloadOptions.adapterOptions, + 'We passed adapterOptions via reload' + ); + count++; + return resolve({ + data: { id: id, type: 'person', attributes: { name: 'Braaaahm Dale' } }, + }); + } else { + assert.ok(false, 'Should not get here'); + } + }, + }) + ); -test("When a single record is requested, the adapter's find method should be called unless it's loaded.", function(assert) { - var count = 0; - - env.adapter.findRecord = function(store, type, id, snapshot) { - if (count === 0) { - count++; - return resolve({ data: { id: id, type: 'person', attributes: { name: "Tom Dale" } } }); - } else if (count === 1) { - count++; - return resolve({ data: { id: id, type: 'person', attributes: { name: "Braaaahm Dale" } } }); - } else { - assert.ok(false, "Should not get here"); - } - }; - - run(function() { - env.store.findRecord('person', 1).then(function(person) { - assert.equal(get(person, 'name'), "Tom Dale", "The person is loaded with the right name"); - assert.equal(get(person, 'isLoaded'), true, "The person is now loaded"); - var promise = person.reload(); - assert.equal(get(person, 'isReloading'), true, "The person is now reloading"); - return promise; - }).then(function(person) { - assert.equal(get(person, 'isReloading'), false, "The person is no longer reloading"); - assert.equal(get(person, 'name'), "Braaaahm Dale", "The person is now updated with the right name"); - }); - }); -}); + let person = await store.findRecord('person', '1'); -test("When a single record is requested, the adapter's find method should be called unless it's loaded.", function(assert) { - let count = 0; - let reloadOptions = { - adapterOptions: { - makeSnazzy: true - } - }; - - env.adapter.findRecord = function(store, type, id, snapshot) { - if (count === 0) { - count++; - return resolve({ data: { id: id, type: 'person', attributes: { name: "Tom Dale" } } }); - } else if (count === 1) { - assert.equal(snapshot.adapterOptions, reloadOptions.adapterOptions, 'We passed adapterOptions via reload'); - count++; - return resolve({ data: { id: id, type: 'person', attributes: { name: "Braaaahm Dale" } } }); - } else { - assert.ok(false, "Should not get here"); - } - }; + assert.equal(get(person, 'name'), 'Tom Dale', 'The person is loaded with the right name'); + assert.equal(get(person, 'isLoaded'), true, 'The person is now loaded'); - run(function() { - env.store.findRecord('person', 1).then(function(person) { - assert.equal(get(person, 'name'), "Tom Dale", "The person is loaded with the right name"); - assert.equal(get(person, 'isLoaded'), true, "The person is now loaded"); + let promise = person.reload(reloadOptions); - let promise = person.reload(reloadOptions); + assert.equal(get(person, 'isReloading'), true, 'The person is now reloading'); - assert.equal(get(person, 'isReloading'), true, "The person is now reloading"); + await promise; - return promise; - }).then(function(person) { - assert.equal(get(person, 'isReloading'), false, "The person is no longer reloading"); - assert.equal(get(person, 'name'), "Braaaahm Dale", "The person is now updated with the right name"); - }); + assert.equal(get(person, 'isReloading'), false, 'The person is no longer reloading'); + assert.equal( + get(person, 'name'), + 'Braaaahm Dale', + 'The person is now updated with the right name' + ); + + // ensure we won't call adapter.findRecord again + await store.findRecord('person', '1'); }); -}); -test("When a record is reloaded and fails, it can try again", function(assert) { - var tom; - run(function() { - env.store.push({ + test('When a record is reloaded and fails, it can try again', async function(assert) { + let tom = store.push({ data: { type: 'person', id: '1', attributes: { - name: 'Tom Dale' - } - } + name: 'Tom Dale', + }, + }, }); - tom = env.store.peekRecord('person', 1); - }); + let count = 0; + + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + shouldBackgroundReloadRecord() { + return true; + }, + + findRecord() { + assert.equal(tom.get('isReloading'), true, 'Tom is reloading'); + if (count++ === 0) { + return reject(); + } else { + return resolve({ + data: { id: 1, type: 'person', attributes: { name: 'Thomas Dale' } }, + }); + } + }, + }) + ); - var count = 0; - env.adapter.findRecord = function(store, type, id, snapshot) { - assert.equal(tom.get('isReloading'), true, "Tom is reloading"); - if (count++ === 0) { - return reject(); - } else { - return resolve({ data: { id: 1, type: 'person', attributes: { name: "Thomas Dale" } } }); - } - }; - - run(function() { - tom.reload().then(null, function() { - assert.equal(tom.get('isError'), true, "Tom is now errored"); - assert.equal(tom.get('isReloading'), false, "Tom is no longer reloading"); - return tom.reload(); - }).then(function(person) { - assert.equal(person, tom, "The resolved value is the record"); - assert.equal(tom.get('isError'), false, "Tom is no longer errored"); - assert.equal(tom.get('isReloading'), false, "Tom is no longer reloading"); - assert.equal(tom.get('name'), "Thomas Dale", "the updates apply"); + await tom.reload().catch(() => { + assert.ok(true, 'we throw an error'); }); + + assert.equal(tom.get('isError'), true, 'Tom is now errored'); + assert.equal(tom.get('isReloading'), false, 'Tom is no longer reloading'); + + let person = await tom.reload(); + + assert.equal(person, tom, 'The resolved value is the record'); + assert.equal(tom.get('isError'), false, 'Tom is no longer errored'); + assert.equal(tom.get('isReloading'), false, 'Tom is no longer reloading'); + assert.equal(tom.get('name'), 'Thomas Dale', 'the updates apply'); }); -}); -test("When a record is loaded a second time, isLoaded stays true", function(assert) { - let record = { - data: { - type: 'person', - id: '1', - attributes: { - name: 'Tom Dale' - } + test('When a record is loaded a second time, isLoaded stays true', async function(assert) { + assert.expect(3); + function getTomDale() { + return { + data: { + type: 'person', + id: '1', + attributes: { + name: 'Tom Dale', + }, + }, + }; } - }; - env.adapter.findRecord = function(store, type, id, snapshot) { - return record; - }; - run(function() { - env.store.push(record); - }); - run(function() { - env.store.findRecord('person', 1).then(function(person) { - assert.equal(get(person, 'isLoaded'), true, "The person is loaded"); - person.addObserver('isLoaded', isLoadedDidChange); + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + shouldBackgroundReloadRecord() { + return true; + }, + + findRecord(store, type, id, snapshot) { + assert.ok(true, 'We should call findRecord'); + return resolve(getTomDale()); + }, + }) + ); + + function isLoadedDidChange() { + // This observer should never fire + assert.ok(false, 'We should not trigger the isLoaded observer'); + // but if it does we should still have the same isLoaded state + assert.equal(get(this, 'isLoaded'), true, 'The person is still loaded after change'); + } - // Reload the record - env.store.push(record); + store.push(getTomDale()); - assert.equal(get(person, 'isLoaded'), true, "The person is still loaded after load"); + let person = await store.findRecord('person', '1'); - person.removeObserver('isLoaded', isLoadedDidChange); - }); + person.addObserver('isLoaded', isLoadedDidChange); + assert.equal(get(person, 'isLoaded'), true, 'The person is loaded'); + + // Reload the record + store.push(getTomDale()); + + assert.equal(get(person, 'isLoaded'), true, 'The person is still loaded after load'); + + person.removeObserver('isLoaded', isLoadedDidChange); }); - function isLoadedDidChange() { - // This shouldn't be hit - assert.equal(get(this, 'isLoaded'), true, "The person is still loaded after change"); - } -}); + test('When a record is reloaded, its async hasMany relationships still work', async function(assert) { + const Person = Model.extend({ + name: attr(), + tags: hasMany('tag', { async: true, inverse: null }) + }); + const Tag = Model.extend({ + name: attr() + }); + + this.owner.unregister('model:person'); + this.owner.register('model:person', Person); + this.owner.register('model:tag', Tag); + + let tagsById = { 1: 'hipster', 2: 'hair' }; + + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + shouldBackgroundReloadRecord() { + return false; + }, + + findRecord(store, type, id, snapshot) { + switch (type.modelName) { + case 'person': + return resolve({ + data: { + id: '1', + type: 'person', + attributes: { name: 'Tom' }, + relationships: { + tags: { + data: [{ id: '1', type: 'tag' }, { id: '2', type: 'tag' }], + }, + }, + }, + }); + case 'tag': + return resolve({ data: { id: id, type: 'tag', attributes: { name: tagsById[id] } } }); + } + }, + }) + ); -test("When a record is reloaded, its async hasMany relationships still work", function(assert) { - env.registry.register('model:person', DS.Model.extend({ - name: DS.attr(), - tags: DS.hasMany('tag', { async: true }) - })); + let tom; + let person = await store.findRecord('person', '1'); - env.registry.register('model:tag', DS.Model.extend({ - name: DS.attr() - })); + tom = person; + assert.equal(person.get('name'), 'Tom', 'precond'); - var tags = { 1: "hipster", 2: "hair" }; + let tags = await person.get('tags'); - env.adapter.findRecord = function(store, type, id, snapshot) { - switch (type.modelName) { - case 'person': - return resolve({ - data: { - id: 1, + assert.deepEqual(tags.mapBy('name'), ['hipster', 'hair']); + + person = await tom.reload(); + assert.equal(person.get('name'), 'Tom', 'precond'); + + tags = await person.get('tags'); + + assert.deepEqual(tags.mapBy('name'), ['hipster', 'hair'], 'The tags are still there'); + }); + + module('Reloading via relationship reference and { type, id }', function() { + test('When a sync belongsTo relationship has been loaded, it can still be reloaded via the reference', async function(assert) { + assert.expect(2); + const Pet = Model.extend({ + name: attr(), + owner: belongsTo('person', { async: false, inverse: null }) + }); + + this.owner.register('model:pet', Pet); + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + findRecord() { + assert.ok('We called findRecord'); + return resolve({ + data: { + type: 'person', + id: '1', + attributes: { + name: 'Chris', + }, + }, + }); + }, + }) + ); + + let shen = store.push({ + data: { + type: 'pet', + id: '1', + attributes: { name: 'Shen' }, + relationships: { + owner: { + data: { type: 'person', id: '1' }, + }, + }, + }, + included: [ + { type: 'person', - attributes: { name: "Tom" }, - relationships: { - tags: { - data: [ - { id: 1, type: 'tag' }, - { id: 2, type: 'tag' } - ] - } - } - } - }); - case 'tag': - return resolve({ data: { id: id, type: 'tag', attributes: { name: tags[id] } } }); - } - }; + id: '1', + attributes: { + name: 'Chris', + }, + }, + ], + }); + + let ownerRef = shen.belongsTo('owner'); + let owner = shen.get('owner'); + let ownerViaRef = await ownerRef.reload(); + + assert.ok(owner === ownerViaRef, 'We received the same reference via reload'); + }); - var tom; + test('When a sync belongsTo relationship has not been loaded, it can still be reloaded via the reference', async function(assert) { + assert.expect(2); + const Pet = Model.extend({ + name: attr(), + owner: belongsTo('person', { async: false, inverse: null }) + }); + + this.owner.register('model:pet', Pet); + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + findRecord() { + assert.ok('We called findRecord'); + return resolve({ + data: { + type: 'person', + id: '1', + attributes: { + name: 'Chris', + }, + }, + }); + }, + }) + ); + + let shen = store.push({ + data: { + type: 'pet', + id: '1', + attributes: { name: 'Shen' }, + relationships: { + owner: { + data: { type: 'person', id: '1' }, + }, + }, + }, + }); + + let ownerRef = shen.belongsTo('owner'); + let ownerViaRef = await ownerRef.reload(); + let owner = shen.get('owner'); + + assert.ok(owner === ownerViaRef, 'We received the same reference via reload'); + }); - run(function() { - env.store.findRecord('person', 1).then(function(person) { - tom = person; - assert.equal(person.get('name'), "Tom", "precond"); + test('When a sync hasMany relationship has been loaded, it can still be reloaded via the reference', async function(assert) { + assert.expect(2); + const Pet = Model.extend({ + name: attr(), + owners: hasMany('person', { async: false, inverse: null }) + }); + + this.owner.register('model:pet', Pet); + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + findRecord() { + assert.ok('We called findRecord'); + return resolve({ + data: { + type: 'person', + id: '1', + attributes: { + name: 'Chris', + }, + }, + }); + }, + }) + ); + + let shen = store.push({ + data: { + type: 'pet', + id: '1', + attributes: { name: 'Shen' }, + relationships: { + owners: { + data: [{ type: 'person', id: '1' }], + }, + }, + }, + included: [ + { + type: 'person', + id: '1', + attributes: { + name: 'Chris', + }, + }, + ], + }); + + let ownersRef = shen.hasMany('owners'); + let owners = shen.get('owners'); + let ownersViaRef = await ownersRef.reload(); + + assert.ok( + owners.objectAt(0) === ownersViaRef.objectAt(0), + 'We received the same reference via reload' + ); + }); - return person.get('tags'); - }).then(function(tags) { - assert.deepEqual(tags.mapBy('name'), ['hipster', 'hair']); + test('When a sync hasMany relationship has not been loaded, it can still be reloaded via the reference', async function(assert) { + assert.expect(2); + const Pet = Model.extend({ + name: attr(), + owners: hasMany('person', { async: false, inverse: null }) + }); + + this.owner.register('model:pet', Pet); + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + findRecord() { + assert.ok('We called findRecord'); + return resolve({ + data: { + type: 'person', + id: '1', + attributes: { + name: 'Chris', + }, + }, + }); + }, + }) + ); + + let shen = store.push({ + data: { + type: 'pet', + id: '1', + attributes: { name: 'Shen' }, + relationships: { + owners: { + data: [{ type: 'person', id: '1' }], + }, + }, + }, + }); + + let ownersRef = shen.hasMany('owners'); + let ownersViaRef = await ownersRef.reload(); + let owners = shen.get('owners'); + + assert.ok( + owners.objectAt(0) === ownersViaRef.objectAt(0), + 'We received the same reference via reload' + ); + }); + }); - return tom.reload(); - }).then(function(person) { - assert.equal(person.get('name'), "Tom", "precond"); + module('Reloading via relationship reference and links', function() { + test('When a sync belongsTo relationship has been loaded, it can still be reloaded via the reference', async function(assert) { + assert.expect(2); + const Pet = Model.extend({ + name: attr(), + owner: belongsTo('person', { async: false, inverse: null }) + }); + + this.owner.register('model:pet', Pet); + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + findBelongsTo() { + assert.ok('We called findRecord'); + return resolve({ + data: { + type: 'person', + id: '1', + attributes: { + name: 'Chris', + }, + }, + }); + }, + }) + ); + + let shen = store.push({ + data: { + type: 'pet', + id: '1', + attributes: { name: 'Shen' }, + relationships: { + owner: { + data: { type: 'person', id: '1' }, + links: { + related: './owner', + }, + }, + }, + }, + included: [ + { + type: 'person', + id: '1', + attributes: { + name: 'Chris', + }, + }, + ], + }); + + let ownerRef = shen.belongsTo('owner'); + let owner = shen.get('owner'); + let ownerViaRef = await ownerRef.reload(); + + assert.ok(owner === ownerViaRef, 'We received the same reference via reload'); + }); + + test('When a sync belongsTo relationship has not been loaded, it can still be reloaded via the reference', async function(assert) { + assert.expect(2); + const Pet = Model.extend({ + name: attr(), + owner: belongsTo('person', { async: false, inverse: null }) + }); + + this.owner.register('model:pet', Pet); + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + findBelongsTo() { + assert.ok('We called findRecord'); + return resolve({ + data: { + type: 'person', + id: '1', + attributes: { + name: 'Chris', + }, + }, + }); + }, + }) + ); + + let shen = store.push({ + data: { + type: 'pet', + id: '1', + attributes: { name: 'Shen' }, + relationships: { + owner: { + data: { type: 'person', id: '1' }, + links: { + related: './owner', + }, + }, + }, + }, + }); + + let ownerRef = shen.belongsTo('owner'); + let ownerViaRef = await ownerRef.reload(); + let owner = shen.get('owner'); + + assert.ok(owner === ownerViaRef, 'We received the same reference via reload'); + }); + + test('When a sync hasMany relationship has been loaded, it can still be reloaded via the reference', async function(assert) { + assert.expect(2); + const Pet = Model.extend({ + name: attr(), + owners: hasMany('person', { async: false, inverse: null }) + }); + + this.owner.register('model:pet', Pet); + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + findHasMany() { + assert.ok('We called findRecord'); + return resolve({ + data: [ + { + type: 'person', + id: '1', + attributes: { + name: 'Chris', + }, + }, + ], + }); + }, + }) + ); + + let shen = store.push({ + data: { + type: 'pet', + id: '1', + attributes: { name: 'Shen' }, + relationships: { + owners: { + data: [{ type: 'person', id: '1' }], + links: { + related: './owners', + }, + }, + }, + }, + included: [ + { + type: 'person', + id: '1', + attributes: { + name: 'Chris', + }, + }, + ], + }); + + let ownersRef = shen.hasMany('owners'); + let owners = shen.get('owners'); + let ownersViaRef = await ownersRef.reload(); + + assert.ok( + owners.objectAt(0) === ownersViaRef.objectAt(0), + 'We received the same reference via reload' + ); + }); - return person.get('tags'); - }).then(function(tags) { - assert.deepEqual(tags.mapBy('name'), ['hipster', 'hair'], "The tags are still there"); + test('When a sync hasMany relationship has not been loaded, it can still be reloaded via the reference', async function(assert) { + assert.expect(2); + const Pet = Model.extend({ + name: attr(), + owners: hasMany('person', { async: false, inverse: null }) + }); + + this.owner.register('model:pet', Pet); + this.owner.register( + 'adapter:application', + JSONAPIAdapter.extend({ + findHasMany() { + assert.ok('We called findRecord'); + return resolve({ + data: [ + { + type: 'person', + id: '1', + attributes: { + name: 'Chris', + }, + }, + ], + }); + }, + }) + ); + + let shen = store.push({ + data: { + type: 'pet', + id: '1', + attributes: { name: 'Shen' }, + relationships: { + owners: { + data: [{ type: 'person', id: '1' }], + links: { + related: './owners', + }, + }, + }, + }, + }); + + let ownersRef = shen.hasMany('owners'); + let ownersViaRef = await ownersRef.reload(); + let owners = shen.get('owners'); + + assert.ok( + owners.objectAt(0) === ownersViaRef.objectAt(0), + 'We received the same reference via reload' + ); }); }); });