Blame view

node_modules/loopback-datasource-juggler/test/operation-hooks.suite/embeds-many-destroy.suite.js 4.7 KB
f7563de62   Palak Handa   first commit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  // Copyright IBM Corp. 2015,2016. All Rights Reserved.
  // Node module: loopback-datasource-juggler
  // This file is licensed under the MIT License.
  // License text available at https://opensource.org/licenses/MIT
  'use strict';
  
  var Promise = require('bluebird');
  var ValidationError = require('../..').ValidationError;
  
  var contextTestHelpers = require('../helpers/context-test-helpers');
  var ContextRecorder = contextTestHelpers.ContextRecorder;
  var aCtxForModel = contextTestHelpers.aCtxForModel;
  
  var uid = require('../helpers/uid-generator');
  var HookMonitor = require('../helpers/hook-monitor');
  
  module.exports = function(dataSource, should, connectorCapabilities) {
    describe('EmbedsMany - destroy', function() {
      var ctxRecorder, hookMonitor, expectedError;
      beforeEach(function sharedSetup() {
        ctxRecorder = new ContextRecorder('hook not called');
        hookMonitor = new HookMonitor({includeModelName: true});
        expectedError = new Error('test error');
      });
  
      var Owner, Embedded;
      var migrated = false;
      beforeEach(function setupDatabase() {
        Embedded = dataSource.createModel('Embedded', {
          // Set id.generated to false to honor client side values
          id: {type: String, id: true, generated: false, default: uid.next},
          name: {type: String, required: true},
          extra: {type: String, required: false},
        });
  
        Owner = dataSource.createModel('Owner', {});
        Owner.embedsMany(Embedded);
  
        hookMonitor.install(Embedded);
        hookMonitor.install(Owner);
  
        if (migrated) {
          return Owner.deleteAll();
        } else {
          return dataSource.automigrate(Owner.modelName)
            .then(function() { migrated = true; });
        }
      });
  
      var ownerInstance, existingInstance, existingItem;
      beforeEach(function setupData() {
        return Owner.create({})
          .then(function(inst) {
            ownerInstance = inst;
          })
          .then(function() {
            var item = new Embedded({name: 'created'});
            return ownerInstance.embeddedList.create(item).then(function(it) {
              existingItem = it;
            });
          })
          .then(function() {
            hookMonitor.resetNames();
          });
      });
  
      function callDestroy() {
        // Unfortunately, updateById was not promisified yet
        return new Promise(function(resolve, reject) {
          return ownerInstance.embeddedList.destroy(
            existingItem.id,
            function(err, result) {
              if (err) reject(err);
              else resolve(result);
            });
        });
      }
  
      it('triggers hooks in the correct order', function() {
        return callDestroy().then(function(result) {
          hookMonitor.names.should.eql([
            'Embedded:before delete',
            'Owner:before save',
            'Owner:persist',
            'Owner:loaded',
            'Owner:after save',
            'Embedded:after delete',
          ]);
        });
      });
  
      it('trigers `before delete` hook', function() {
        Embedded.observe('before delete', ctxRecorder.recordAndNext());
        return callDestroy().then(function() {
          ctxRecorder.records.should.eql(aCtxForModel(Embedded, {
            instance: {
              id: existingItem.id,
              name: 'created',
              extra: undefined,
            },
          }));
        });
      });
  
      // TODO
      // In order to allow "before delete" hook to make changes,
      // we need to enhance the context to include information
      // about the model instance being deleted.
      // "ctx.where: { id: embedded.id }" may not be enough,
      // as it does not identify the parent (owner) model
      it('applies updates from `before delete` hook');
  
      it('aborts when `before delete` hook fails', function() {
        Embedded.observe('before delete', nextWithError(expectedError));
        return callDestroy().then(throwShouldHaveFailed, function(err) {
          err.should.eql(expectedError);
        });
      });
  
      it('trigers `after delete` hook', function() {
        Embedded.observe('after delete', ctxRecorder.recordAndNext());
        return callDestroy().then(function() {
          ctxRecorder.records.should.eql(aCtxForModel(Embedded, {
            instance: {
              id: existingItem.id,
              name: 'created',
              extra: undefined,
            },
          }));
        });
      });
  
      it('aborts when `after delete` hook fails', function() {
        Embedded.observe('after delete', nextWithError(expectedError));
        return callDestroy().then(throwShouldHaveFailed, function(err) {
          err.should.eql(expectedError);
        });
      });
  
      function nextWithError(err) {
        return function(context, next) {
          next(err);
        };
      }
  
      function throwShouldHaveFailed() {
        throw new Error('operation should have failed');
      }
    });
  };