diff --git a/packages/-ember-data/tests/integration/records/delete-record-test.js b/packages/-ember-data/tests/integration/records/delete-record-test.js index f6190198b64..29204049a71 100644 --- a/packages/-ember-data/tests/integration/records/delete-record-test.js +++ b/packages/-ember-data/tests/integration/records/delete-record-test.js @@ -11,7 +11,7 @@ import { setupTest } from 'ember-qunit'; import Adapter from '@ember-data/adapter'; import { InvalidError } from '@ember-data/adapter/error'; -import Model, { attr, hasMany } from '@ember-data/model'; +import Model, { attr, belongsTo, hasMany } from '@ember-data/model'; import JSONAPISerializer from '@ember-data/serializer/json-api'; module('integration/deletedRecord - Deleting Records', function (hooks) { @@ -352,4 +352,105 @@ module('integration/deletedRecord - Deleting Records', function (hooks) { record.unloadRecord(); await settled(); }); + + test('Records with an async hasMany can be pushed again after they were destroyed on client side', async function (assert) { + let group; + let employee; + + class Company extends Model { + @attr('string') name; + toString() { + return 'Company'; + } + } + class Group extends Model { + @belongsTo('company', { async: true }) company; + @hasMany('employee', { inverse: 'groups', async: true }) employees; + toString() { + return 'Group'; + } + } + class Employee extends Model { + @hasMany('group', { inverse: 'employees', async: true }) groups; + @attr('string') name; + } + + this.owner.register('model:company', Company); + this.owner.register('model:group', Group); + this.owner.register('model:employee', Employee); + + let store = this.owner.lookup('service:store'); + let adapter = store.adapterFor('application'); + + adapter.deleteRecord = function () { + return EmberPromise.resolve(); + }; + + // Push the company as a long-lived record that will be referenced by the group + store.push({ + data: { + type: 'company', + id: '1', + attributes: { + name: 'Inc.', + }, + }, + }); + + const jsonEmployee = { + data: { + type: 'employee', + id: '1', + attributes: { + name: 'Adam Sunderland', + }, + relationships: { + groups: { + data: [{ type: 'group', id: '1' }], + }, + }, + }, + }; + + const jsonGroup = { + data: { + type: 'group', + id: '1', + relationships: { + company: { + data: { + id: '1', + type: 'company', + }, + }, + }, + }, + }; + + // Server push with the group and employee + store.push(jsonEmployee); + store.push(jsonGroup); + + group = store.peekRecord('group', '1'); + + // Sanity Check + assert.ok(group, 'expected group to be found'); + assert.equal(group.get('company.name'), 'Inc.', 'group belongs to our company'); + assert.equal(group.get('employees.length'), 1, 'expected 1 related record before delete'); + employee = group.get('employees').objectAt(0); + assert.equal(employee.get('name'), 'Adam Sunderland', 'expected related records to be loaded'); + + await group.destroyRecord(); + await employee.destroyRecord(); + + assert.equal(store.peekAll('employee').get('length'), 0, 'no employee record loaded'); + assert.equal(store.peekAll('group').get('length'), 0, 'no group record loaded'); + + // Server pushes the same group and employee once more after they have been destroyed client-side. (The company is a long-lived record) + store.push(jsonEmployee); + store.push(jsonGroup); + + group = store.peekRecord('group', '1'); + assert.equal(group.get('employees.length'), 1, 'expected 1 related record after delete and restore'); + }); });