spice-js 2.7.17 → 2.7.19
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/build/models/SpiceModel.js +136 -58
- package/package.json +1 -1
- package/src/models/SpiceModel.js +377 -160
- package/tests/models/SpiceModel.test.js +156 -0
- package/src/models/SpiceModel.js.bak +0 -2186
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
const { createTestModel, seedDatabase, clearDatabase } = require('../helpers/modelFactory');
|
|
18
18
|
const { expectValidIdsOnly, expectUniqueIds, expectNoEmptyIdCalls } = require('../helpers/assertions');
|
|
19
19
|
const { emptyIdCases, sampleDbResults, relationshipData, duplicateIdData, generateLargeDataset } = require('../fixtures/testData');
|
|
20
|
+
const SpiceModel = require('../../src/models/SpiceModel').default;
|
|
20
21
|
|
|
21
22
|
describe('SpiceModel - Critical Fixes for Empty/Null Values', () => {
|
|
22
23
|
|
|
@@ -167,6 +168,104 @@ describe('SpiceModel - Critical Fixes for Empty/Null Values', () => {
|
|
|
167
168
|
expect(result.name).toBe('Alice');
|
|
168
169
|
expect(result.email).toBe('alice@example.com');
|
|
169
170
|
});
|
|
171
|
+
|
|
172
|
+
test('should keep deep dot-notation under the first nested segment', () => {
|
|
173
|
+
const rows = [{
|
|
174
|
+
id: 'user-1',
|
|
175
|
+
first_name: '',
|
|
176
|
+
committee_votes: [
|
|
177
|
+
{
|
|
178
|
+
member: {
|
|
179
|
+
first_name: 'Ada',
|
|
180
|
+
last_name: 'Lovelace'
|
|
181
|
+
},
|
|
182
|
+
value: 'yes'
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
}];
|
|
186
|
+
|
|
187
|
+
const [result] = model.filterResultsByColumns(
|
|
188
|
+
rows,
|
|
189
|
+
'`committee_votes`.`member`.`first_name`'
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
expect(result.first_name).toBeUndefined();
|
|
193
|
+
expect(result.committee_votes).toBeDefined();
|
|
194
|
+
expect(result.committee_votes[0].member.first_name).toBe('Ada');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('should remove base model prefix before selecting nested segment', () => {
|
|
198
|
+
const rows = [{
|
|
199
|
+
id: 'user-1',
|
|
200
|
+
group: {
|
|
201
|
+
name: 'Admins'
|
|
202
|
+
}
|
|
203
|
+
}];
|
|
204
|
+
|
|
205
|
+
const [result] = model.filterResultsByColumns(
|
|
206
|
+
rows,
|
|
207
|
+
'`user`.`group`.`name`'
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
expect(result.group).toBeDefined();
|
|
211
|
+
expect(result.group.name).toBe('Admins');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('should keep mixed scalar and mapped columns without empty select slots', () => {
|
|
215
|
+
const applicationModel = createTestModel({ type: 'application' });
|
|
216
|
+
const requestedColumns = 'meta().id,dependents,dd_assessment_notes,ceo_assessment_notes,first_name,title,last_name,project,`committee_votes`.`member`,`committee_votes`.`date_of_vote`,`committee_votes`.`vote`,`committee_votes`.`rejection_reasons`,`committee_votes`.`vote_justification`,`committee_votes`.`comments`,`committee_votes`.`application`,control_number,full_name,application_type,stage,committee_submission_date,votes_casted';
|
|
217
|
+
|
|
218
|
+
const corrected = applicationModel.correctColumns(
|
|
219
|
+
requestedColumns,
|
|
220
|
+
[{ alias: 'committee_votes' }]
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const selectedColumns = corrected
|
|
224
|
+
.split(',')
|
|
225
|
+
.map((item) => item.trim());
|
|
226
|
+
|
|
227
|
+
expect(selectedColumns.every((item) => item !== '')).toBe(true);
|
|
228
|
+
expect(selectedColumns).toContain('dependents');
|
|
229
|
+
expect(selectedColumns).toContain('first_name');
|
|
230
|
+
expect(selectedColumns).toContain('`application`.`committee_votes`');
|
|
231
|
+
expect(selectedColumns).toContain('meta().id');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test('should collapse deep mapped paths even without mapped nestings', () => {
|
|
235
|
+
expect(
|
|
236
|
+
SpiceModel.prototype.correctColumns.call(
|
|
237
|
+
{
|
|
238
|
+
type: 'application',
|
|
239
|
+
props: {
|
|
240
|
+
dependents: {
|
|
241
|
+
map: {
|
|
242
|
+
reference: 'dependent'
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
'dependents.passports.holder',
|
|
248
|
+
[],
|
|
249
|
+
)
|
|
250
|
+
).toBe('`application`.`dependents`');
|
|
251
|
+
|
|
252
|
+
expect(
|
|
253
|
+
SpiceModel.prototype.correctColumns.call(
|
|
254
|
+
{
|
|
255
|
+
type: 'application',
|
|
256
|
+
props: {
|
|
257
|
+
dependents: {
|
|
258
|
+
map: {
|
|
259
|
+
reference: 'dependent'
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
'`application`.`dependents`.`passports`.`holder`',
|
|
265
|
+
[],
|
|
266
|
+
)
|
|
267
|
+
).toBe('`application`.`dependents`');
|
|
268
|
+
});
|
|
170
269
|
});
|
|
171
270
|
|
|
172
271
|
describe('Fix #4-6: mapToObject() - Strict Checks and Deduplication', () => {
|
|
@@ -258,6 +357,63 @@ describe('SpiceModel - Critical Fixes for Empty/Null Values', () => {
|
|
|
258
357
|
});
|
|
259
358
|
});
|
|
260
359
|
|
|
360
|
+
describe('Fix #8: mapToObjectArray() - Empty Key Projection Fallback', () => {
|
|
361
|
+
let model;
|
|
362
|
+
|
|
363
|
+
beforeEach(() => {
|
|
364
|
+
model = createTestModel({ type: 'user' });
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
afterEach(() => {
|
|
368
|
+
clearDatabase(model);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
test('should map array relationships when source field is under empty-string key', async () => {
|
|
372
|
+
let capturedIds = [];
|
|
373
|
+
let capturedColumns = [];
|
|
374
|
+
|
|
375
|
+
class FakeRelatedModel {
|
|
376
|
+
async getMulti({ ids, columns }) {
|
|
377
|
+
capturedIds = ids;
|
|
378
|
+
capturedColumns = columns;
|
|
379
|
+
return [
|
|
380
|
+
{ id: 'group-1', type: 'group', name: 'Admins' },
|
|
381
|
+
{ id: 'group-2', type: 'group', name: 'Editors' }
|
|
382
|
+
];
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const sourceRows = [
|
|
387
|
+
{
|
|
388
|
+
id: 'user-1',
|
|
389
|
+
type: 'user',
|
|
390
|
+
'': {
|
|
391
|
+
groups: ['group-1', 'group-2']
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
];
|
|
395
|
+
|
|
396
|
+
const mapped = await model.mapToObjectArray(
|
|
397
|
+
sourceRows,
|
|
398
|
+
FakeRelatedModel,
|
|
399
|
+
'groups',
|
|
400
|
+
'groups',
|
|
401
|
+
{},
|
|
402
|
+
{},
|
|
403
|
+
'read',
|
|
404
|
+
3,
|
|
405
|
+
0,
|
|
406
|
+
'groups.name'
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
expect(capturedIds).toEqual(['group-1', 'group-2']);
|
|
410
|
+
expect(capturedColumns).toEqual(['name']);
|
|
411
|
+
expect(mapped[0].groups).toHaveLength(2);
|
|
412
|
+
expect(mapped[0].groups[0].name).toBe('Admins');
|
|
413
|
+
expect(mapped[0].groups[1].name).toBe('Editors');
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
261
417
|
describe('Fix #7: Strict Equality in ID Matching', () => {
|
|
262
418
|
let model;
|
|
263
419
|
|