1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | const Promise = global.Promise = require('bluebird');
|
16 | const DataSource = require('../').DataSource;
|
17 | const Memory = require('../lib/connectors/memory').Memory;
|
18 |
|
19 | const HOOK_NAMES = [
|
20 | 'access',
|
21 | 'before save', 'persist', 'loaded', 'after save',
|
22 | 'before delete', 'after delete',
|
23 | ];
|
24 |
|
25 | const dataSources = [
|
26 | createOptimizedDataSource(),
|
27 | createUnoptimizedDataSource(),
|
28 | ];
|
29 |
|
30 | const observedContexts = [];
|
31 | let lastId = 0;
|
32 |
|
33 | Promise.onPossiblyUnhandledRejection(function(err) {
|
34 | console.error('POSSIBLY UNHANDLED REJECTION', err.stack);
|
35 | });
|
36 |
|
37 |
|
38 | const operations = [
|
39 | function find(ds) {
|
40 | return ds.TestModel.find({where: {id: '1'}});
|
41 | },
|
42 |
|
43 | function count(ds) {
|
44 | return ds.TestModel.count({id: ds.existingInstance.id});
|
45 | },
|
46 |
|
47 | function create(ds) {
|
48 | return ds.TestModel.create({name: 'created'});
|
49 | },
|
50 |
|
51 | function findOrCreate_found(ds) {
|
52 | return ds.TestModel.findOrCreate(
|
53 | {where: {name: ds.existingInstance.name}},
|
54 | {name: ds.existingInstance.name},
|
55 | );
|
56 | },
|
57 |
|
58 | function findOrCreate_create(ds) {
|
59 | return ds.TestModel.findOrCreate(
|
60 | {where: {name: 'new-record'}},
|
61 | {name: 'new-record'},
|
62 | );
|
63 | },
|
64 |
|
65 | function updateOrCreate_create(ds) {
|
66 | return ds.TestModel.updateOrCreate({id: 'not-found', name: 'not found'});
|
67 | },
|
68 |
|
69 | function updateOrCreate_update(ds) {
|
70 | return ds.TestModel.updateOrCreate(
|
71 | {id: ds.existingInstance.id, name: 'new name'},
|
72 | );
|
73 | },
|
74 |
|
75 | function replaceOrCreate_create(ds) {
|
76 | return ds.TestModel.replaceOrCreate({id: 'not-found', name: 'not found'});
|
77 | },
|
78 |
|
79 | function replaceOrCreate_update(ds) {
|
80 | return ds.TestModel.replaceOrCreate(
|
81 | {id: ds.existingInstance.id, name: 'new name'},
|
82 | );
|
83 | },
|
84 |
|
85 | function replaceById(ds) {
|
86 | return ds.TestModel.replaceById(
|
87 | ds.existingInstance.id,
|
88 | {name: 'new name'},
|
89 | );
|
90 | },
|
91 |
|
92 | function updateAll(ds) {
|
93 | return ds.TestModel.updateAll({name: 'searched'}, {name: 'updated'});
|
94 | },
|
95 |
|
96 | function prototypeSave(ds) {
|
97 | ds.existingInstance.name = 'changed';
|
98 | return ds.existingInstance.save();
|
99 | },
|
100 |
|
101 | function prototypeUpdateAttributes(ds) {
|
102 | return ds.existingInstance.updateAttributes({name: 'changed'});
|
103 | },
|
104 |
|
105 | function prototypeDelete(ds) {
|
106 | return ds.existingInstance.delete();
|
107 | },
|
108 |
|
109 | function deleteAll(ds) {
|
110 | return ds.TestModel.deleteAll({name: ds.existingInstance.name});
|
111 | },
|
112 | ];
|
113 |
|
114 |
|
115 | let p = setupTestModels();
|
116 | operations.forEach(function(op) {
|
117 | p = p.then(runner(op));
|
118 | });
|
119 |
|
120 | p.then(report, function(err) { console.error(err.stack); });
|
121 |
|
122 | function createOptimizedDataSource() {
|
123 | const ds = new DataSource({connector: Memory});
|
124 | ds.name = 'Optimized';
|
125 | return ds;
|
126 | }
|
127 |
|
128 | function createUnoptimizedDataSource() {
|
129 | const ds = new DataSource({connector: Memory});
|
130 | ds.name = 'Unoptimized';
|
131 |
|
132 |
|
133 | ds.connector.updateOrCreate = false;
|
134 | ds.connector.findOrCreate = false;
|
135 | ds.connector.replaceOrCreate = false;
|
136 |
|
137 | return ds;
|
138 | }
|
139 |
|
140 | function setupTestModels() {
|
141 | dataSources.forEach(function setupOnDataSource(ds) {
|
142 | const TestModel = ds.TestModel = ds.createModel('TestModel', {
|
143 | id: {type: String, id: true, default: uid},
|
144 | name: {type: String, required: true},
|
145 | extra: {type: String, required: false},
|
146 | });
|
147 | });
|
148 | return Promise.resolve();
|
149 | }
|
150 |
|
151 | function uid() {
|
152 | lastId += 1;
|
153 | return '' + lastId;
|
154 | }
|
155 |
|
156 | function runner(fn) {
|
157 | return function() {
|
158 | let res = Promise.resolve();
|
159 | dataSources.forEach(function(ds) {
|
160 | res = res.then(function() {
|
161 | return resetStorage(ds);
|
162 | }).then(function() {
|
163 | observedContexts.push({
|
164 | operation: fn.name,
|
165 | connector: ds.name,
|
166 | hooks: {},
|
167 | });
|
168 | return fn(ds);
|
169 | });
|
170 | });
|
171 | return res;
|
172 | };
|
173 | }
|
174 |
|
175 | function resetStorage(ds) {
|
176 | const TestModel = ds.TestModel;
|
177 | HOOK_NAMES.forEach(function(hook) {
|
178 | TestModel.clearObservers(hook);
|
179 | });
|
180 | return TestModel.deleteAll()
|
181 | .then(function() {
|
182 | return TestModel.create({name: 'first'});
|
183 | })
|
184 | .then(function(instance) {
|
185 |
|
186 | return TestModel.findById(instance.id);
|
187 | })
|
188 | .then(function(instance) {
|
189 | ds.existingInstance = instance;
|
190 | return TestModel.create({name: 'second'});
|
191 | })
|
192 | .then(function() {
|
193 | HOOK_NAMES.forEach(function(hook) {
|
194 | TestModel.observe(hook, function(ctx, next) {
|
195 | const row = observedContexts[observedContexts.length - 1];
|
196 | row.hooks[hook] = Object.keys(ctx);
|
197 | next();
|
198 | });
|
199 | });
|
200 | });
|
201 | }
|
202 |
|
203 | function report() {
|
204 | console.log('<style>');
|
205 | console.log('td { font-family: "monospace": }');
|
206 | console.log('td, th {');
|
207 | console.log(' vertical-align: text-top;');
|
208 | console.log(' padding: 0.5em;');
|
209 | console.log(' border-bottom: 1px solid gray;');
|
210 | console.log('</style>');
|
211 |
|
212 |
|
213 | observedContexts.forEach(function(row, ix) {
|
214 | if (!ix) return;
|
215 | const last = observedContexts[ix - 1];
|
216 | if (row.operation != last.operation) return;
|
217 | if (JSON.stringify(row.hooks) !== JSON.stringify(last.hooks)) return;
|
218 | last.merge = true;
|
219 | row.skip = true;
|
220 | });
|
221 |
|
222 | console.log('<!-- ==== BEGIN DATA ==== -->\n');
|
223 | console.log('<table><thead><tr>\n <th></th>');
|
224 | HOOK_NAMES.forEach(function(h) { console.log(' <th>' + h + '</th>'); });
|
225 | console.log('</tr></thead><tbody>');
|
226 |
|
227 | observedContexts.forEach(function(row) {
|
228 | if (row.skip) return;
|
229 | let caption = row.operation;
|
230 | if (!row.merge) caption += ' (' + row.connector + ')';
|
231 | console.log('<tr><th>' + caption + '</th>');
|
232 | HOOK_NAMES.forEach(function(h) {
|
233 | const text = row.hooks[h] ? row.hooks[h].join('<br/>') : '';
|
234 | console.log(' <td>' + text + '</td>');
|
235 | });
|
236 | console.log('</tr>');
|
237 | });
|
238 | console.log('</table>');
|
239 | }
|