meadow-integration 1.0.23 → 1.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/_cover.md +1 -1
- package/docs/_version.json +7 -0
- package/docs/css/docuserve.css +277 -23
- package/docs/index.html +2 -2
- package/docs/retold-catalog.json +40 -1
- package/docs/retold-keyword-index.json +6150 -5279
- package/package.json +3 -2
- package/source/Meadow-Service-Integration-Adapter.js +5 -8
- package/source/services/clone/Meadow-Service-Sync-Entity-ComparisonOnly.js +435 -0
- package/source/services/clone/Meadow-Service-Sync-Entity-OngoingEventualConsistency.js +353 -0
- package/source/services/clone/Meadow-Service-Sync-Entity-TrueUp.js +199 -0
- package/source/services/clone/Meadow-Service-Sync.js +61 -6
- package/test/Meadow-Integration-Adapter_test.js +431 -48
- package/test/Meadow-Integration-ComprehensionPush_test.js +102 -8
- package/test/Meadow-Integration-NewStrategies_test.js +1265 -0
|
@@ -1,72 +1,455 @@
|
|
|
1
1
|
/*
|
|
2
2
|
Unit tests for Retold Integration Adapter
|
|
3
|
+
|
|
4
|
+
Validates that the integration adapter correctly fetches the remote
|
|
5
|
+
schema, marshals source records, and upserts them to the server.
|
|
6
|
+
|
|
7
|
+
Uses a mock HTTP server to simulate meadow-endpoints API responses.
|
|
3
8
|
*/
|
|
4
9
|
|
|
5
10
|
const Chai = require('chai');
|
|
6
11
|
const Expect = Chai.expect;
|
|
7
12
|
|
|
13
|
+
const libHTTP = require('http');
|
|
8
14
|
const libPict = require('pict');
|
|
9
15
|
const libIntegrationAdapter = require('../source/Meadow-Service-Integration-Adapter.js');
|
|
16
|
+
const libMeadowCloneRestClient = require('../source/services/clone/Meadow-Service-RestClient.js');
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
// ── Test Constants ──────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
const MOCK_PORT = 18199;
|
|
21
|
+
const MOCK_BASE_URL = `http://localhost:${MOCK_PORT}/1.0/`;
|
|
22
|
+
|
|
23
|
+
// ── Mock Schema ─────────────────────────────────────────────────────────────────
|
|
24
|
+
// This is what the Schema endpoint returns — a JSON schema with properties.
|
|
25
|
+
|
|
26
|
+
const MOCK_SCHEMA =
|
|
27
|
+
{
|
|
28
|
+
title: 'TestEntity',
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties:
|
|
31
|
+
{
|
|
32
|
+
IDTestEntity: { type: 'integer' },
|
|
33
|
+
GUIDTestEntity: { type: 'string', size: 128 },
|
|
34
|
+
Name: { type: 'string', size: 200 },
|
|
35
|
+
Value: { type: 'integer' }
|
|
36
|
+
},
|
|
37
|
+
required: ['IDTestEntity']
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// ── Mock Server State ───────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
let _MockState =
|
|
43
|
+
{
|
|
44
|
+
NextID: 1,
|
|
45
|
+
UpsertedRecords: [],
|
|
46
|
+
StoredRecords: {}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function resetMockState()
|
|
50
|
+
{
|
|
51
|
+
_MockState.NextID = 1;
|
|
52
|
+
_MockState.UpsertedRecords = [];
|
|
53
|
+
_MockState.StoredRecords = {};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── Mock HTTP Server ────────────────────────────────────────────────────────────
|
|
57
|
+
// Simulates meadow-endpoints API responses for the TestEntity entity.
|
|
58
|
+
|
|
59
|
+
function createMockServer()
|
|
60
|
+
{
|
|
61
|
+
return libHTTP.createServer(
|
|
62
|
+
(pRequest, pResponse) =>
|
|
15
63
|
{
|
|
16
|
-
|
|
64
|
+
let tmpURL = pRequest.url;
|
|
65
|
+
let tmpBody = '';
|
|
17
66
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
67
|
+
pResponse.setHeader('Content-Type', 'application/json');
|
|
68
|
+
|
|
69
|
+
pRequest.on('data',
|
|
70
|
+
(pChunk) =>
|
|
71
|
+
{
|
|
72
|
+
tmpBody += pChunk;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
pRequest.on('end',
|
|
76
|
+
() =>
|
|
77
|
+
{
|
|
78
|
+
// GET /1.0/TestEntity/Schema
|
|
79
|
+
if (pRequest.method === 'GET' && tmpURL.match(/\/1\.0\/TestEntity\/Schema/))
|
|
80
|
+
{
|
|
81
|
+
pResponse.end(JSON.stringify(MOCK_SCHEMA));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// PUT /1.0/TestEntity/Upsert
|
|
86
|
+
if (pRequest.method === 'PUT' && tmpURL.match(/\/1\.0\/TestEntity\/Upsert/))
|
|
87
|
+
{
|
|
88
|
+
let tmpRecord = {};
|
|
89
|
+
try
|
|
90
|
+
{
|
|
91
|
+
tmpRecord = JSON.parse(tmpBody);
|
|
92
|
+
}
|
|
93
|
+
catch (pError)
|
|
94
|
+
{
|
|
95
|
+
pResponse.statusCode = 400;
|
|
96
|
+
pResponse.end(JSON.stringify({ Error: 'Invalid JSON' }));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Assign an auto-incremented ID if not present or zero
|
|
101
|
+
if (!tmpRecord.IDTestEntity || tmpRecord.IDTestEntity === 0)
|
|
102
|
+
{
|
|
103
|
+
tmpRecord.IDTestEntity = _MockState.NextID++;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Store and track the upserted record
|
|
107
|
+
_MockState.UpsertedRecords.push(JSON.parse(JSON.stringify(tmpRecord)));
|
|
108
|
+
_MockState.StoredRecords[tmpRecord.GUIDTestEntity] = tmpRecord;
|
|
109
|
+
|
|
110
|
+
pResponse.end(JSON.stringify(tmpRecord));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// GET /1.0/TestEntitys/By/GUIDTestEntity/{guid}/0/1
|
|
115
|
+
if (pRequest.method === 'GET' && tmpURL.match(/\/1\.0\/TestEntitys\/By\/GUIDTestEntity\//))
|
|
116
|
+
{
|
|
117
|
+
let tmpParts = tmpURL.split('/');
|
|
118
|
+
// URL pattern: /1.0/TestEntitys/By/GUIDTestEntity/{guid}/0/1
|
|
119
|
+
let tmpGUID = tmpParts[5];
|
|
120
|
+
let tmpRecord = _MockState.StoredRecords[tmpGUID];
|
|
121
|
+
if (tmpRecord)
|
|
122
|
+
{
|
|
123
|
+
pResponse.end(JSON.stringify([tmpRecord]));
|
|
124
|
+
}
|
|
125
|
+
else
|
|
126
|
+
{
|
|
127
|
+
pResponse.end(JSON.stringify([]));
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Fallback — 404
|
|
133
|
+
pResponse.statusCode = 404;
|
|
134
|
+
pResponse.end(JSON.stringify({ Error: `Unknown endpoint: ${pRequest.method} ${tmpURL}` }));
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
suite
|
|
140
|
+
(
|
|
141
|
+
'Integration Adapter Basic',
|
|
142
|
+
() =>
|
|
143
|
+
{
|
|
144
|
+
setup(() => { });
|
|
145
|
+
|
|
146
|
+
suite
|
|
147
|
+
(
|
|
148
|
+
'Basic Tests',
|
|
149
|
+
() =>
|
|
150
|
+
{
|
|
151
|
+
test(
|
|
152
|
+
'Object Instantiation',
|
|
153
|
+
(fDone) =>
|
|
154
|
+
{
|
|
155
|
+
let _Fable = new libPict();
|
|
156
|
+
_Fable.addServiceType('IntegrationAdapter', libIntegrationAdapter);
|
|
157
|
+
let tmpIntegrationAdapter = _Fable.instantiateServiceProvider('IntegrationAdapter', { Entity: 'TestEntity' }, 'TestEntity');
|
|
158
|
+
Expect(tmpIntegrationAdapter).to.be.an('object');
|
|
159
|
+
return fDone();
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
suite
|
|
167
|
+
(
|
|
168
|
+
'Integration Adapter with Mock Server',
|
|
169
|
+
() =>
|
|
170
|
+
{
|
|
171
|
+
let _MockServer = null;
|
|
172
|
+
|
|
173
|
+
suiteSetup
|
|
174
|
+
(
|
|
175
|
+
(fDone) =>
|
|
176
|
+
{
|
|
177
|
+
_MockServer = createMockServer();
|
|
178
|
+
_MockServer.listen(MOCK_PORT,
|
|
21
179
|
() =>
|
|
22
180
|
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
181
|
+
return fDone();
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
suiteTeardown
|
|
187
|
+
(
|
|
188
|
+
(fDone) =>
|
|
189
|
+
{
|
|
190
|
+
if (_MockServer)
|
|
191
|
+
{
|
|
192
|
+
_MockServer.close(fDone);
|
|
193
|
+
}
|
|
194
|
+
else
|
|
195
|
+
{
|
|
196
|
+
return fDone();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
setup
|
|
202
|
+
(
|
|
203
|
+
() =>
|
|
204
|
+
{
|
|
205
|
+
resetMockState();
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// ── Schema Fetch ────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
suite
|
|
212
|
+
(
|
|
213
|
+
'Schema Fetch',
|
|
214
|
+
() =>
|
|
215
|
+
{
|
|
216
|
+
test
|
|
217
|
+
(
|
|
218
|
+
'meadowSchema should be populated after integrateRecords',
|
|
219
|
+
(fDone) =>
|
|
220
|
+
{
|
|
221
|
+
let tmpFable = new libPict(
|
|
222
|
+
{
|
|
223
|
+
Product: 'AdapterTest',
|
|
224
|
+
ProductVersion: '1.0.0',
|
|
225
|
+
LogStreams: [{ streamtype: 'console', level: 'error' }]
|
|
226
|
+
});
|
|
227
|
+
tmpFable.addServiceType('IntegrationAdapter', libIntegrationAdapter);
|
|
228
|
+
tmpFable.serviceManager.addServiceType('MeadowCloneRestClient', libMeadowCloneRestClient);
|
|
229
|
+
tmpFable.serviceManager.instantiateServiceProvider('MeadowCloneRestClient',
|
|
230
|
+
{
|
|
231
|
+
ServerURL: MOCK_BASE_URL
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
let tmpAdapter = tmpFable.instantiateServiceProvider('IntegrationAdapter',
|
|
235
|
+
{
|
|
236
|
+
Entity: 'TestEntity',
|
|
237
|
+
SimpleMarshal: true
|
|
238
|
+
}, 'TestEntity');
|
|
239
|
+
|
|
240
|
+
tmpAdapter.addSourceRecord({ GUIDTestEntity: 'test-schema-1', Name: 'SchemaCheck', Value: 1 });
|
|
241
|
+
|
|
242
|
+
// Before integration, meadowSchema should not be set
|
|
243
|
+
Expect(tmpAdapter.meadowSchema).to.not.be.ok;
|
|
244
|
+
|
|
245
|
+
tmpAdapter.integrateRecords(
|
|
246
|
+
(pError) =>
|
|
247
|
+
{
|
|
248
|
+
Expect(pError).to.not.exist;
|
|
249
|
+
|
|
250
|
+
// After integration, meadowSchema should be populated
|
|
251
|
+
Expect(tmpAdapter.meadowSchema).to.be.an('object');
|
|
252
|
+
Expect(tmpAdapter.meadowSchema).to.have.property('properties');
|
|
253
|
+
Expect(tmpAdapter.meadowSchema.properties).to.have.property('Name');
|
|
254
|
+
Expect(tmpAdapter.meadowSchema.properties).to.have.property('Value');
|
|
255
|
+
Expect(tmpAdapter.meadowSchema.properties).to.have.property('IDTestEntity');
|
|
256
|
+
Expect(tmpAdapter.meadowSchema.properties).to.have.property('GUIDTestEntity');
|
|
257
|
+
|
|
31
258
|
return fDone();
|
|
32
259
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
// ── Full Integration Pipeline ───────────────────────────────────────
|
|
266
|
+
|
|
267
|
+
suite
|
|
268
|
+
(
|
|
269
|
+
'Full Integration Pipeline',
|
|
270
|
+
() =>
|
|
271
|
+
{
|
|
272
|
+
test
|
|
273
|
+
(
|
|
274
|
+
'upsert body should include marshaled field values (not just the GUID)',
|
|
275
|
+
function (fDone)
|
|
276
|
+
{
|
|
277
|
+
this.timeout(10000);
|
|
278
|
+
|
|
279
|
+
let tmpFable = new libPict(
|
|
280
|
+
{
|
|
281
|
+
Product: 'AdapterTest',
|
|
282
|
+
ProductVersion: '1.0.0',
|
|
283
|
+
LogStreams: [{ streamtype: 'console', level: 'error' }]
|
|
284
|
+
});
|
|
285
|
+
tmpFable.addServiceType('IntegrationAdapter', libIntegrationAdapter);
|
|
286
|
+
tmpFable.serviceManager.addServiceType('MeadowCloneRestClient', libMeadowCloneRestClient);
|
|
287
|
+
tmpFable.serviceManager.instantiateServiceProvider('MeadowCloneRestClient',
|
|
288
|
+
{
|
|
289
|
+
ServerURL: MOCK_BASE_URL
|
|
49
290
|
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
(fDone) =>
|
|
291
|
+
|
|
292
|
+
let tmpAdapter = tmpFable.instantiateServiceProvider('IntegrationAdapter',
|
|
53
293
|
{
|
|
54
|
-
|
|
294
|
+
Entity: 'TestEntity',
|
|
295
|
+
SimpleMarshal: true
|
|
296
|
+
}, 'TestEntity');
|
|
297
|
+
|
|
298
|
+
tmpAdapter.addSourceRecord({ GUIDTestEntity: 'test-1', Name: 'Alice', Value: 42 });
|
|
299
|
+
|
|
300
|
+
tmpAdapter.integrateRecords(
|
|
301
|
+
(pError) =>
|
|
302
|
+
{
|
|
303
|
+
Expect(pError).to.not.exist;
|
|
304
|
+
|
|
305
|
+
// The mock server should have received exactly one upsert
|
|
306
|
+
Expect(_MockState.UpsertedRecords.length).to.equal(1,
|
|
307
|
+
`Expected 1 upserted record, got ${_MockState.UpsertedRecords.length}`);
|
|
55
308
|
|
|
56
|
-
|
|
309
|
+
let tmpUpsertedRecord = _MockState.UpsertedRecords[0];
|
|
310
|
+
|
|
311
|
+
// The GUID should be present (with the adapter prefix)
|
|
312
|
+
Expect(tmpUpsertedRecord).to.have.property('GUIDTestEntity');
|
|
313
|
+
Expect(tmpUpsertedRecord.GUIDTestEntity).to.be.a('string');
|
|
314
|
+
Expect(tmpUpsertedRecord.GUIDTestEntity).to.include('test-1');
|
|
315
|
+
|
|
316
|
+
// CRITICAL: Verify field values were marshaled through.
|
|
317
|
+
// If the arrow-function arguments bug is present, the schema
|
|
318
|
+
// fetch would fail silently, meadowSchema would be null, and
|
|
319
|
+
// SimpleMarshal would not copy Name and Value through.
|
|
320
|
+
Expect(tmpUpsertedRecord).to.have.property('Name');
|
|
321
|
+
Expect(tmpUpsertedRecord.Name).to.equal('Alice');
|
|
322
|
+
Expect(tmpUpsertedRecord).to.have.property('Value');
|
|
323
|
+
Expect(tmpUpsertedRecord.Value).to.equal(42);
|
|
324
|
+
|
|
325
|
+
// Verify the schema was also populated
|
|
326
|
+
Expect(tmpAdapter.meadowSchema).to.be.an('object');
|
|
327
|
+
Expect(tmpAdapter.meadowSchema.properties).to.have.property('Name');
|
|
328
|
+
|
|
329
|
+
return fDone();
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
test
|
|
335
|
+
(
|
|
336
|
+
'should integrate multiple records with correct field values',
|
|
337
|
+
function (fDone)
|
|
338
|
+
{
|
|
339
|
+
this.timeout(10000);
|
|
340
|
+
|
|
341
|
+
let tmpFable = new libPict(
|
|
342
|
+
{
|
|
343
|
+
Product: 'AdapterTest',
|
|
344
|
+
ProductVersion: '1.0.0',
|
|
345
|
+
LogStreams: [{ streamtype: 'console', level: 'error' }]
|
|
346
|
+
});
|
|
347
|
+
tmpFable.addServiceType('IntegrationAdapter', libIntegrationAdapter);
|
|
348
|
+
tmpFable.serviceManager.addServiceType('MeadowCloneRestClient', libMeadowCloneRestClient);
|
|
349
|
+
tmpFable.serviceManager.instantiateServiceProvider('MeadowCloneRestClient',
|
|
350
|
+
{
|
|
351
|
+
ServerURL: MOCK_BASE_URL
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
let tmpAdapter = tmpFable.instantiateServiceProvider('IntegrationAdapter',
|
|
355
|
+
{
|
|
356
|
+
Entity: 'TestEntity',
|
|
357
|
+
SimpleMarshal: true
|
|
358
|
+
}, 'TestEntity');
|
|
359
|
+
|
|
360
|
+
tmpAdapter.addSourceRecord({ GUIDTestEntity: 'multi-1', Name: 'Alice', Value: 42 });
|
|
361
|
+
tmpAdapter.addSourceRecord({ GUIDTestEntity: 'multi-2', Name: 'Bob', Value: 99 });
|
|
362
|
+
tmpAdapter.addSourceRecord({ GUIDTestEntity: 'multi-3', Name: 'Charlie', Value: 7 });
|
|
363
|
+
|
|
364
|
+
tmpAdapter.integrateRecords(
|
|
365
|
+
(pError) =>
|
|
366
|
+
{
|
|
367
|
+
Expect(pError).to.not.exist;
|
|
57
368
|
|
|
58
|
-
|
|
369
|
+
Expect(_MockState.UpsertedRecords.length).to.equal(3,
|
|
370
|
+
`Expected 3 upserted records, got ${_MockState.UpsertedRecords.length}`);
|
|
59
371
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
372
|
+
// Build a lookup by the original GUID suffix for easy verification
|
|
373
|
+
let tmpByGUID = {};
|
|
374
|
+
for (let i = 0; i < _MockState.UpsertedRecords.length; i++)
|
|
375
|
+
{
|
|
376
|
+
let tmpRec = _MockState.UpsertedRecords[i];
|
|
377
|
+
if (tmpRec.GUIDTestEntity.indexOf('multi-1') > -1) tmpByGUID['multi-1'] = tmpRec;
|
|
378
|
+
if (tmpRec.GUIDTestEntity.indexOf('multi-2') > -1) tmpByGUID['multi-2'] = tmpRec;
|
|
379
|
+
if (tmpRec.GUIDTestEntity.indexOf('multi-3') > -1) tmpByGUID['multi-3'] = tmpRec;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
Expect(tmpByGUID['multi-1'].Name).to.equal('Alice');
|
|
383
|
+
Expect(tmpByGUID['multi-1'].Value).to.equal(42);
|
|
384
|
+
|
|
385
|
+
Expect(tmpByGUID['multi-2'].Name).to.equal('Bob');
|
|
386
|
+
Expect(tmpByGUID['multi-2'].Value).to.equal(99);
|
|
387
|
+
|
|
388
|
+
Expect(tmpByGUID['multi-3'].Name).to.equal('Charlie');
|
|
389
|
+
Expect(tmpByGUID['multi-3'].Value).to.equal(7);
|
|
390
|
+
|
|
391
|
+
// Each record should have been assigned a server-side ID
|
|
392
|
+
Expect(tmpByGUID['multi-1'].IDTestEntity).to.be.above(0);
|
|
393
|
+
Expect(tmpByGUID['multi-2'].IDTestEntity).to.be.above(0);
|
|
394
|
+
Expect(tmpByGUID['multi-3'].IDTestEntity).to.be.above(0);
|
|
395
|
+
|
|
396
|
+
return fDone();
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
test
|
|
402
|
+
(
|
|
403
|
+
'without SimpleMarshal, fields not in schema properties are excluded',
|
|
404
|
+
function (fDone)
|
|
405
|
+
{
|
|
406
|
+
this.timeout(10000);
|
|
407
|
+
|
|
408
|
+
let tmpFable = new libPict(
|
|
409
|
+
{
|
|
410
|
+
Product: 'AdapterTest',
|
|
411
|
+
ProductVersion: '1.0.0',
|
|
412
|
+
LogStreams: [{ streamtype: 'console', level: 'error' }]
|
|
413
|
+
});
|
|
414
|
+
tmpFable.addServiceType('IntegrationAdapter', libIntegrationAdapter);
|
|
415
|
+
tmpFable.serviceManager.addServiceType('MeadowCloneRestClient', libMeadowCloneRestClient);
|
|
416
|
+
tmpFable.serviceManager.instantiateServiceProvider('MeadowCloneRestClient',
|
|
417
|
+
{
|
|
418
|
+
ServerURL: MOCK_BASE_URL
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
let tmpAdapter = tmpFable.instantiateServiceProvider('IntegrationAdapter',
|
|
422
|
+
{
|
|
423
|
+
Entity: 'TestEntity',
|
|
424
|
+
SimpleMarshal: false
|
|
425
|
+
}, 'TestEntity');
|
|
426
|
+
|
|
427
|
+
// ExtraField is not in the mock schema
|
|
428
|
+
tmpAdapter.addSourceRecord({ GUIDTestEntity: 'test-extra', Name: 'Diana', Value: 55, ExtraField: 'should-not-appear' });
|
|
429
|
+
|
|
430
|
+
tmpAdapter.integrateRecords(
|
|
431
|
+
(pError) =>
|
|
432
|
+
{
|
|
433
|
+
Expect(pError).to.not.exist;
|
|
434
|
+
|
|
435
|
+
Expect(_MockState.UpsertedRecords.length).to.equal(1);
|
|
436
|
+
|
|
437
|
+
let tmpUpsertedRecord = _MockState.UpsertedRecords[0];
|
|
438
|
+
|
|
439
|
+
// Name and Value should be marshaled through the schema
|
|
440
|
+
Expect(tmpUpsertedRecord).to.have.property('Name');
|
|
441
|
+
Expect(tmpUpsertedRecord.Name).to.equal('Diana');
|
|
442
|
+
Expect(tmpUpsertedRecord).to.have.property('Value');
|
|
443
|
+
Expect(tmpUpsertedRecord.Value).to.equal(55);
|
|
444
|
+
|
|
445
|
+
// ExtraField should NOT be in the upserted record
|
|
446
|
+
Expect(tmpUpsertedRecord).to.not.have.property('ExtraField');
|
|
447
|
+
|
|
448
|
+
return fDone();
|
|
67
449
|
});
|
|
68
|
-
*/
|
|
69
450
|
}
|
|
70
451
|
);
|
|
71
|
-
|
|
72
|
-
|
|
452
|
+
}
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
);
|
|
@@ -79,7 +79,15 @@ suite
|
|
|
79
79
|
NameFirst TEXT DEFAULT '',
|
|
80
80
|
NameLast TEXT DEFAULT '',
|
|
81
81
|
FullName TEXT DEFAULT '',
|
|
82
|
-
Config TEXT DEFAULT ''
|
|
82
|
+
Config TEXT DEFAULT '',
|
|
83
|
+
IDCustomer INTEGER DEFAULT 0,
|
|
84
|
+
Email TEXT DEFAULT '',
|
|
85
|
+
Phone TEXT DEFAULT '',
|
|
86
|
+
Address TEXT DEFAULT '',
|
|
87
|
+
City TEXT DEFAULT '',
|
|
88
|
+
State TEXT DEFAULT '',
|
|
89
|
+
Postal TEXT DEFAULT '',
|
|
90
|
+
Country TEXT DEFAULT ''
|
|
83
91
|
);
|
|
84
92
|
CREATE TABLE IF NOT EXISTS Book (
|
|
85
93
|
IDBook INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -97,13 +105,15 @@ suite
|
|
|
97
105
|
ISBN TEXT DEFAULT '',
|
|
98
106
|
Language TEXT DEFAULT '',
|
|
99
107
|
ImageURL TEXT DEFAULT '',
|
|
100
|
-
PublicationYear INTEGER DEFAULT 0
|
|
108
|
+
PublicationYear INTEGER DEFAULT 0,
|
|
109
|
+
IDCustomer INTEGER DEFAULT 0
|
|
101
110
|
);
|
|
102
111
|
CREATE TABLE IF NOT EXISTS BookAuthorJoin (
|
|
103
112
|
IDBookAuthorJoin INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
104
113
|
GUIDBookAuthorJoin TEXT DEFAULT '',
|
|
105
114
|
IDBook INTEGER DEFAULT 0,
|
|
106
|
-
IDAuthor INTEGER DEFAULT 0
|
|
115
|
+
IDAuthor INTEGER DEFAULT 0,
|
|
116
|
+
IDCustomer INTEGER DEFAULT 0
|
|
107
117
|
);
|
|
108
118
|
CREATE TABLE IF NOT EXISTS Author (
|
|
109
119
|
IDAuthor INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -116,7 +126,8 @@ suite
|
|
|
116
126
|
DeleteDate TEXT DEFAULT '',
|
|
117
127
|
DeletingIDUser INTEGER DEFAULT 0,
|
|
118
128
|
Name TEXT DEFAULT '',
|
|
119
|
-
IDUser INTEGER DEFAULT 0
|
|
129
|
+
IDUser INTEGER DEFAULT 0,
|
|
130
|
+
IDCustomer INTEGER DEFAULT 0
|
|
120
131
|
);
|
|
121
132
|
CREATE TABLE IF NOT EXISTS BookPrice (
|
|
122
133
|
IDBookPrice INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -133,7 +144,8 @@ suite
|
|
|
133
144
|
EndDate TEXT DEFAULT '',
|
|
134
145
|
Discountable INTEGER DEFAULT 0,
|
|
135
146
|
CouponCode TEXT DEFAULT '',
|
|
136
|
-
IDBook INTEGER DEFAULT 0
|
|
147
|
+
IDBook INTEGER DEFAULT 0,
|
|
148
|
+
IDCustomer INTEGER DEFAULT 0
|
|
137
149
|
);
|
|
138
150
|
CREATE TABLE IF NOT EXISTS BookStore (
|
|
139
151
|
IDBookStore INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -150,7 +162,11 @@ suite
|
|
|
150
162
|
City TEXT DEFAULT '',
|
|
151
163
|
State TEXT DEFAULT '',
|
|
152
164
|
Postal TEXT DEFAULT '',
|
|
153
|
-
Country TEXT DEFAULT ''
|
|
165
|
+
Country TEXT DEFAULT '',
|
|
166
|
+
IDCustomer INTEGER DEFAULT 0,
|
|
167
|
+
StoreType TEXT DEFAULT '',
|
|
168
|
+
Phone TEXT DEFAULT '',
|
|
169
|
+
Email TEXT DEFAULT ''
|
|
154
170
|
);
|
|
155
171
|
CREATE TABLE IF NOT EXISTS BookStoreInventory (
|
|
156
172
|
IDBookStoreInventory INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -168,7 +184,8 @@ suite
|
|
|
168
184
|
IDBook INTEGER DEFAULT 0,
|
|
169
185
|
IDBookStore INTEGER DEFAULT 0,
|
|
170
186
|
IDBookPrice INTEGER DEFAULT 0,
|
|
171
|
-
StockingAssociate INTEGER DEFAULT 0
|
|
187
|
+
StockingAssociate INTEGER DEFAULT 0,
|
|
188
|
+
IDCustomer INTEGER DEFAULT 0
|
|
172
189
|
);
|
|
173
190
|
CREATE TABLE IF NOT EXISTS Review (
|
|
174
191
|
IDReview INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -183,7 +200,84 @@ suite
|
|
|
183
200
|
Text TEXT DEFAULT '',
|
|
184
201
|
Rating INTEGER DEFAULT 0,
|
|
185
202
|
IDBook INTEGER DEFAULT 0,
|
|
186
|
-
IDUser INTEGER DEFAULT 0
|
|
203
|
+
IDUser INTEGER DEFAULT 0,
|
|
204
|
+
IDCustomer INTEGER DEFAULT 0
|
|
205
|
+
);
|
|
206
|
+
CREATE TABLE IF NOT EXISTS Customer (
|
|
207
|
+
IDCustomer INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
208
|
+
GUIDCustomer TEXT DEFAULT '',
|
|
209
|
+
CreateDate TEXT DEFAULT '',
|
|
210
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
211
|
+
UpdateDate TEXT DEFAULT '',
|
|
212
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
213
|
+
Deleted INTEGER DEFAULT 0,
|
|
214
|
+
DeleteDate TEXT DEFAULT '',
|
|
215
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
216
|
+
Name TEXT DEFAULT '',
|
|
217
|
+
Description TEXT DEFAULT '',
|
|
218
|
+
ContactName TEXT DEFAULT '',
|
|
219
|
+
ContactEmail TEXT DEFAULT '',
|
|
220
|
+
ContactPhone TEXT DEFAULT '',
|
|
221
|
+
Address TEXT DEFAULT '',
|
|
222
|
+
City TEXT DEFAULT '',
|
|
223
|
+
State TEXT DEFAULT '',
|
|
224
|
+
Postal TEXT DEFAULT '',
|
|
225
|
+
Country TEXT DEFAULT '',
|
|
226
|
+
Active INTEGER DEFAULT 0
|
|
227
|
+
);
|
|
228
|
+
CREATE TABLE IF NOT EXISTS BookStoreEmployee (
|
|
229
|
+
IDBookStoreEmployee INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
230
|
+
GUIDBookStoreEmployee TEXT DEFAULT '',
|
|
231
|
+
CreateDate TEXT DEFAULT '',
|
|
232
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
233
|
+
UpdateDate TEXT DEFAULT '',
|
|
234
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
235
|
+
Deleted INTEGER DEFAULT 0,
|
|
236
|
+
DeleteDate TEXT DEFAULT '',
|
|
237
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
238
|
+
Title TEXT DEFAULT '',
|
|
239
|
+
HireDate TEXT DEFAULT '',
|
|
240
|
+
TerminationDate TEXT DEFAULT '',
|
|
241
|
+
IsActive INTEGER DEFAULT 0,
|
|
242
|
+
IDUser INTEGER DEFAULT 0,
|
|
243
|
+
IDBookStore INTEGER DEFAULT 0,
|
|
244
|
+
IDCustomer INTEGER DEFAULT 0
|
|
245
|
+
);
|
|
246
|
+
CREATE TABLE IF NOT EXISTS BookStoreSale (
|
|
247
|
+
IDBookStoreSale INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
248
|
+
GUIDBookStoreSale TEXT DEFAULT '',
|
|
249
|
+
CreateDate TEXT DEFAULT '',
|
|
250
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
251
|
+
UpdateDate TEXT DEFAULT '',
|
|
252
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
253
|
+
Deleted INTEGER DEFAULT 0,
|
|
254
|
+
DeleteDate TEXT DEFAULT '',
|
|
255
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
256
|
+
SaleDate TEXT DEFAULT '',
|
|
257
|
+
TotalAmount REAL DEFAULT 0,
|
|
258
|
+
PaymentMethod TEXT DEFAULT '',
|
|
259
|
+
TransactionID TEXT DEFAULT '',
|
|
260
|
+
IDBookStore INTEGER DEFAULT 0,
|
|
261
|
+
IDUser INTEGER DEFAULT 0,
|
|
262
|
+
IDCustomer INTEGER DEFAULT 0
|
|
263
|
+
);
|
|
264
|
+
CREATE TABLE IF NOT EXISTS BookStoreSaleItem (
|
|
265
|
+
IDBookStoreSaleItem INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
266
|
+
GUIDBookStoreSaleItem TEXT DEFAULT '',
|
|
267
|
+
CreateDate TEXT DEFAULT '',
|
|
268
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
269
|
+
UpdateDate TEXT DEFAULT '',
|
|
270
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
271
|
+
Deleted INTEGER DEFAULT 0,
|
|
272
|
+
DeleteDate TEXT DEFAULT '',
|
|
273
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
274
|
+
Quantity INTEGER DEFAULT 0,
|
|
275
|
+
UnitPrice REAL DEFAULT 0,
|
|
276
|
+
LineTotal REAL DEFAULT 0,
|
|
277
|
+
IDBookStoreSale INTEGER DEFAULT 0,
|
|
278
|
+
IDBook INTEGER DEFAULT 0,
|
|
279
|
+
IDBookPrice INTEGER DEFAULT 0,
|
|
280
|
+
IDCustomer INTEGER DEFAULT 0
|
|
187
281
|
);
|
|
188
282
|
`);
|
|
189
283
|
|