foxhound 2.0.26 → 2.0.28

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.
@@ -618,7 +618,7 @@ suite
618
618
  // This is the query generated by the MSSQL dialect
619
619
  _Fable.log.trace('Select Query', tmpQuery.query);
620
620
  Expect(tmpQuery.query.body)
621
- .to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] WHERE [Deleted] = @Deleted_w0 OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
621
+ .to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] WHERE [Deleted] = @Deleted_w0 ORDER BY [IDAnimal] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
622
622
  }
623
623
  );
624
624
  test
@@ -640,7 +640,7 @@ suite
640
640
  // This is the query generated by the MSSQL dialect
641
641
  _Fable.log.trace('Select Query', tmpQuery.query);
642
642
  Expect(tmpQuery.query.body)
643
- .to.equal('SELECT DISTINCT [Name], [Age], [Cost] FROM [Animal] WHERE [Deleted] = @Deleted_w0 OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
643
+ .to.equal('SELECT DISTINCT [Name], [Age], [Cost] FROM [Animal] WHERE [Deleted] = @Deleted_w0 ORDER BY [IDAnimal] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
644
644
  }
645
645
  );
646
646
  test
@@ -669,7 +669,7 @@ suite
669
669
  // This is the query generated by the MSSQL dialect
670
670
  _Fable.log.trace('Select Query', tmpQuery.query);
671
671
  Expect(tmpQuery.query.body)
672
- .to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] WHERE [Age] = @Age_w0 AND ( [Color] = @Color_w2 OR [Color] = @Color_w3 ) AND [Description] IS NOT NULL AND [IDOffice] IN ( @IDOffice_w6 ) AND [Deleted] = @Deleted_w7 OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
672
+ .to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] WHERE [Age] = @Age_w0 AND ( [Color] = @Color_w2 OR [Color] = @Color_w3 ) AND [Description] IS NOT NULL AND [IDOffice] IN ( @IDOffice_w6 ) AND [Deleted] = @Deleted_w7 ORDER BY [IDAnimal] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
673
673
  }
674
674
  );
675
675
  test
@@ -691,7 +691,62 @@ suite
691
691
  // This is the query generated by the MSSQL dialect
692
692
  _Fable.log.trace('Select Query', tmpQuery.query);
693
693
  Expect(tmpQuery.query.body)
694
- .to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
694
+ .to.equal('SELECT [Name], [Age], [Cost] FROM [Animal] ORDER BY [IDAnimal] OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;');
695
+ }
696
+ );
697
+ test
698
+ (
699
+ 'Read Query with legacyPagination uses ROW_NUMBER() wrapper (caller sort)',
700
+ function()
701
+ {
702
+ var tmpQuery = libFoxHound.new(_Fable)
703
+ .setDialect('MSSQL')
704
+ .setScope('Animal')
705
+ .setDataElements(['Name', 'Age', 'Cost'])
706
+ .setCap(10)
707
+ .setBegin(20)
708
+ .setSort([{Column:'Age',Direction:'Ascending'}]);
709
+ tmpQuery.query.schema = _AnimalSchema;
710
+ tmpQuery.parameters.legacyPagination = true;
711
+ tmpQuery.buildReadQuery();
712
+ _Fable.log.trace('Legacy Select Query', tmpQuery.query);
713
+ Expect(tmpQuery.query.body)
714
+ .to.equal('SELECT [Name], [Age], [Cost] FROM (SELECT [Name], [Age], [Cost], ROW_NUMBER() OVER (ORDER BY [Age]) AS [_RowNum] FROM [Animal] WHERE [Deleted] = @Deleted_w0) AS [_Paged] WHERE [_RowNum] > 20 AND [_RowNum] <= 30;');
715
+ }
716
+ );
717
+ test
718
+ (
719
+ 'Read Query with legacyPagination injects PK ORDER BY when caller omits sort',
720
+ function()
721
+ {
722
+ var tmpQuery = libFoxHound.new(_Fable)
723
+ .setDialect('MSSQL')
724
+ .setScope('Animal')
725
+ .setDataElements(['Name', 'Age', 'Cost'])
726
+ .setCap(10);
727
+ tmpQuery.query.schema = _AnimalSchema;
728
+ tmpQuery.parameters.legacyPagination = true;
729
+ tmpQuery.buildReadQuery();
730
+ _Fable.log.trace('Legacy Select Query', tmpQuery.query);
731
+ Expect(tmpQuery.query.body)
732
+ .to.equal('SELECT [Name], [Age], [Cost] FROM (SELECT [Name], [Age], [Cost], ROW_NUMBER() OVER (ORDER BY [IDAnimal]) AS [_RowNum] FROM [Animal] WHERE [Deleted] = @Deleted_w0) AS [_Paged] WHERE [_RowNum] > 0 AND [_RowNum] <= 10;');
733
+ }
734
+ );
735
+ test
736
+ (
737
+ 'Read Query with legacyPagination is inert without cap',
738
+ function()
739
+ {
740
+ // No cap → no pagination at all (legacy or otherwise);
741
+ // the flag should be a no-op and not produce a ROW_NUMBER wrapper.
742
+ var tmpQuery = libFoxHound.new(_Fable)
743
+ .setDialect('MSSQL')
744
+ .setScope('Animal');
745
+ tmpQuery.addSort({Column:'Cost',Direction:'Descending'});
746
+ tmpQuery.parameters.legacyPagination = true;
747
+ tmpQuery.buildReadQuery();
748
+ Expect(tmpQuery.query.body)
749
+ .to.equal('SELECT [Animal].* FROM [Animal] ORDER BY [Cost] DESC;');
695
750
  }
696
751
  );
697
752
  test
@@ -0,0 +1,463 @@
1
+ /**
2
+ * Unit tests for FoxHound Oracle Dialect
3
+ *
4
+ * @license MIT
5
+ *
6
+ * @author Steven Velozo <steven@velozo.com>
7
+ */
8
+
9
+ var Chai = require('chai');
10
+ var Expect = Chai.expect;
11
+
12
+ var libFable = require('fable');
13
+ const _Fable = new libFable({Product:'FoxhoundTestsOracle'});
14
+ var libFoxHound = require('../source/FoxHound.js');
15
+
16
+ var _AnimalSchema = (
17
+ [
18
+ { Column: "IDAnimal", Type:"AutoIdentity" },
19
+ { Column: "GUIDAnimal", Type:"AutoGUID" },
20
+ { Column: "CreateDate", Type:"CreateDate" },
21
+ { Column: "CreatingIDUser", Type:"CreateIDUser" },
22
+ { Column: "UpdateDate", Type:"UpdateDate" },
23
+ { Column: "UpdatingIDUser", Type:"UpdateIDUser" },
24
+ { Column: "Deleted", Type:"Deleted" },
25
+ { Column: "DeletingIDUser", Type:"DeleteIDUser" },
26
+ { Column: "DeleteDate", Type:"DeleteDate" },
27
+ { Column: "Name", Type:"String" },
28
+ { Column: "Age", Type:"Integer" }
29
+ ]);
30
+
31
+ var _AnimalSchemaWithoutDeleted = (
32
+ [
33
+ { Column: "IDAnimal", Type:"AutoIdentity" },
34
+ { Column: "GUIDAnimal", Type:"AutoGUID" },
35
+ { Column: "CreateDate", Type:"CreateDate" },
36
+ { Column: "CreatingIDUser", Type:"CreateIDUser" },
37
+ { Column: "UpdateDate", Type:"UpdateDate" },
38
+ { Column: "UpdatingIDUser", Type:"UpdateIDUser" }
39
+ ]);
40
+
41
+ suite
42
+ (
43
+ 'FoxHound-Dialect-Oracle',
44
+ function()
45
+ {
46
+ suite
47
+ (
48
+ 'Object Sanity',
49
+ function()
50
+ {
51
+ test
52
+ (
53
+ 'initialize should build a happy little object',
54
+ function()
55
+ {
56
+ var testFoxHound = libFoxHound.new(_Fable).setDialect('Oracle');
57
+ Expect(testFoxHound.dialect.name)
58
+ .to.equal('Oracle');
59
+ Expect(testFoxHound)
60
+ .to.be.an('object', 'FoxHound with Oracle should initialize as an object directly from the require statement.');
61
+ }
62
+ );
63
+ }
64
+ );
65
+
66
+ suite
67
+ (
68
+ 'Create Query Generation',
69
+ function()
70
+ {
71
+ test
72
+ (
73
+ 'Create Query without schema parameterizes every column and has no RETURNING',
74
+ function()
75
+ {
76
+ var tmpQuery = libFoxHound.new(_Fable)
77
+ .setDialect('Oracle')
78
+ .setScope('Animal')
79
+ .addRecord({IDAnimal:null, Name:'Foo Foo', Age:15});
80
+ tmpQuery.buildCreateQuery();
81
+ Expect(tmpQuery.query.body)
82
+ .to.equal("INSERT INTO Animal ( IDAnimal, Name, Age) VALUES ( :IDAnimal_0, :Name_1, :Age_2)");
83
+ }
84
+ );
85
+ test
86
+ (
87
+ 'Create Query with schema skips AutoIdentity and appends RETURNING INTO',
88
+ function()
89
+ {
90
+ var tmpQuery = libFoxHound.new(_Fable)
91
+ .setDialect('Oracle')
92
+ .setScope('Animal')
93
+ .addRecord({IDAnimal:null, Name:'Foo Foo', Age:15});
94
+ tmpQuery.query.schema = _AnimalSchema;
95
+ tmpQuery.buildCreateQuery();
96
+ Expect(tmpQuery.query.body)
97
+ .to.equal("INSERT INTO Animal ( Name, Age) VALUES ( :Name_0, :Age_1) RETURNING IDAnimal INTO :RETURNING_ID");
98
+ }
99
+ );
100
+ test
101
+ (
102
+ 'Bad Create Query returns false',
103
+ function()
104
+ {
105
+ var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle');
106
+ tmpQuery.buildCreateQuery();
107
+ tmpQuery.addRecord({});
108
+ tmpQuery.buildCreateQuery();
109
+ Expect(tmpQuery.query.body)
110
+ .to.equal(false);
111
+ }
112
+ );
113
+ }
114
+ );
115
+
116
+ suite
117
+ (
118
+ 'Read Query Generation',
119
+ function()
120
+ {
121
+ test
122
+ (
123
+ 'Simple Read Query (no quoting)',
124
+ function()
125
+ {
126
+ var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle').setScope('Animal');
127
+ tmpQuery.addSort({Column:'Cost',Direction:'Descending'});
128
+ tmpQuery.buildReadQuery();
129
+ Expect(tmpQuery.query.body)
130
+ .to.equal('SELECT Animal.* FROM Animal ORDER BY Cost DESC');
131
+ }
132
+ );
133
+ test
134
+ (
135
+ 'Read Query with named binds and OFFSET/FETCH pagination',
136
+ function()
137
+ {
138
+ var tmpQuery = libFoxHound.new(_Fable)
139
+ .setDialect('Oracle')
140
+ .setScope('Animal')
141
+ .setDataElements(['Name', 'Age'])
142
+ .setCap(10)
143
+ .setBegin(0)
144
+ .addFilter('Age', '15')
145
+ .addSort('Age');
146
+ tmpQuery.buildReadQuery();
147
+ Expect(tmpQuery.query.body)
148
+ .to.equal('SELECT Name, Age FROM Animal WHERE Age = :Age_w0 ORDER BY Age OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY');
149
+ }
150
+ );
151
+ test
152
+ (
153
+ 'Complex Read Query expands IN lists into discrete binds',
154
+ function()
155
+ {
156
+ var tmpQuery = libFoxHound.new(_Fable)
157
+ .setDialect('Oracle')
158
+ .setScope('Animal')
159
+ .setDataElements(['Name', 'Age', 'Cost'])
160
+ .setCap(100)
161
+ .addFilter('Age', '25')
162
+ .addFilter('', '', '(')
163
+ .addFilter('Color', 'Red')
164
+ .addFilter('Color', 'Green', '=', 'OR')
165
+ .addFilter('', '', ')')
166
+ .addFilter('Description', '', 'IS NOT NULL')
167
+ .addFilter('IDOffice', [10, 11, 15, 18, 22], 'IN');
168
+ tmpQuery.addSort('Age');
169
+ tmpQuery.buildReadQuery();
170
+ Expect(tmpQuery.query.body)
171
+ .to.equal('SELECT Name, Age, Cost FROM Animal WHERE Age = :Age_w0 AND ( Color = :Color_w2 OR Color = :Color_w3 ) AND Description IS NOT NULL AND IDOffice IN (:IDOffice_w6_0, :IDOffice_w6_1, :IDOffice_w6_2, :IDOffice_w6_3, :IDOffice_w6_4) ORDER BY Age OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY');
172
+ }
173
+ );
174
+ test
175
+ (
176
+ 'Read Query with schema injects soft-delete filter and PK ORDER BY',
177
+ function()
178
+ {
179
+ var tmpQuery = libFoxHound.new(_Fable)
180
+ .setDialect('Oracle')
181
+ .setScope('Animal')
182
+ .setDataElements(['Name', 'Age', 'Cost'])
183
+ .setCap(100);
184
+ tmpQuery.query.schema = _AnimalSchema;
185
+ tmpQuery.buildReadQuery();
186
+ Expect(tmpQuery.query.body)
187
+ .to.equal('SELECT Name, Age, Cost FROM Animal WHERE Deleted = :Deleted_w0 ORDER BY IDAnimal OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY');
188
+ }
189
+ );
190
+ test
191
+ (
192
+ 'Read Query with quoteIdentifiers wraps identifiers in double quotes',
193
+ function()
194
+ {
195
+ var tmpQuery = libFoxHound.new(_Fable)
196
+ .setDialect('Oracle')
197
+ .setScope('Animal')
198
+ .setDataElements(['Name', 'Age'])
199
+ .addSort('Age');
200
+ tmpQuery.parameters.quoteIdentifiers = true;
201
+ tmpQuery.buildReadQuery();
202
+ Expect(tmpQuery.query.body)
203
+ .to.equal('SELECT "Name", "Age" FROM "Animal" ORDER BY "Age"');
204
+ }
205
+ );
206
+ test
207
+ (
208
+ 'Custom Read Query honors the query override template',
209
+ function()
210
+ {
211
+ var tmpQuery = libFoxHound.new(_Fable)
212
+ .setDialect('Oracle')
213
+ .setScope('Animal')
214
+ .setCap(10)
215
+ .setBegin(0)
216
+ .setDataElements(['Name', 'Age', 'Cost'])
217
+ .setFilter({Column:'Age',Operator:'=',Value:'15',Connector:'AND',Parameter:'Age'});
218
+ tmpQuery.parameters.queryOverride = 'SELECT Name, Age * 5, Cost FROM <%= TableName %> <%= Where %> <%= Limit %>';
219
+ tmpQuery.buildReadQuery();
220
+ Expect(tmpQuery.query.body)
221
+ .to.equal('SELECT Name, Age * 5, Cost FROM Animal WHERE Age = :Age_w0 OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY');
222
+ }
223
+ );
224
+ }
225
+ );
226
+
227
+ suite
228
+ (
229
+ 'Legacy Pagination (ROWNUM double-subquery)',
230
+ function()
231
+ {
232
+ test
233
+ (
234
+ 'legacyPagination wraps the query in a ROWNUM subquery (caller sort)',
235
+ function()
236
+ {
237
+ var tmpQuery = libFoxHound.new(_Fable)
238
+ .setDialect('Oracle')
239
+ .setScope('Animal')
240
+ .setDataElements(['Name', 'Age', 'Cost'])
241
+ .setCap(10)
242
+ .setBegin(20)
243
+ .addSort('Age');
244
+ tmpQuery.query.schema = _AnimalSchema;
245
+ tmpQuery.parameters.legacyPagination = true;
246
+ tmpQuery.buildReadQuery();
247
+ Expect(tmpQuery.query.body)
248
+ .to.equal('SELECT Name, Age, Cost FROM (SELECT meadow_inner.*, ROWNUM AS "_RowNum" FROM (SELECT Name, Age, Cost FROM Animal WHERE Deleted = :Deleted_w0 ORDER BY Age) meadow_inner WHERE ROWNUM <= 30) WHERE "_RowNum" > 20');
249
+ }
250
+ );
251
+ test
252
+ (
253
+ 'legacyPagination injects PK ORDER BY when caller omits sort',
254
+ function()
255
+ {
256
+ var tmpQuery = libFoxHound.new(_Fable)
257
+ .setDialect('Oracle')
258
+ .setScope('Animal')
259
+ .setDataElements(['Name', 'Age', 'Cost'])
260
+ .setCap(10);
261
+ tmpQuery.query.schema = _AnimalSchema;
262
+ tmpQuery.parameters.legacyPagination = true;
263
+ tmpQuery.buildReadQuery();
264
+ Expect(tmpQuery.query.body)
265
+ .to.equal('SELECT Name, Age, Cost FROM (SELECT meadow_inner.*, ROWNUM AS "_RowNum" FROM (SELECT Name, Age, Cost FROM Animal WHERE Deleted = :Deleted_w0 ORDER BY IDAnimal) meadow_inner WHERE ROWNUM <= 10) WHERE "_RowNum" > 0');
266
+ }
267
+ );
268
+ test
269
+ (
270
+ 'legacyPagination is inert without a cap',
271
+ function()
272
+ {
273
+ var tmpQuery = libFoxHound.new(_Fable)
274
+ .setDialect('Oracle')
275
+ .setScope('Animal')
276
+ .setDataElements(['Name']);
277
+ tmpQuery.parameters.legacyPagination = true;
278
+ tmpQuery.buildReadQuery();
279
+ Expect(tmpQuery.query.body)
280
+ .to.equal('SELECT Name FROM Animal');
281
+ }
282
+ );
283
+ }
284
+ );
285
+
286
+ suite
287
+ (
288
+ 'Update / Delete / Undelete Query Generation',
289
+ function()
290
+ {
291
+ test
292
+ (
293
+ 'Update Query auto-stamps UpdateDate with SYS_EXTRACT_UTC',
294
+ function()
295
+ {
296
+ var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
297
+ .setScope('Animal')
298
+ .addFilter('IDAnimal', 9)
299
+ .addRecord({
300
+ IDAnimal:82,
301
+ GUIDAnimal:'1111-2222-3333-4444-5555-6666-7777',
302
+ CreateDate:false,
303
+ CreatingIDUser:false,
304
+ UpdateDate:false,
305
+ UpdatingIDUser:false,
306
+ Name:'Froo Froo',
307
+ Age:18
308
+ });
309
+ tmpQuery.query.schema = _AnimalSchemaWithoutDeleted;
310
+ tmpQuery.buildUpdateQuery();
311
+ Expect(tmpQuery.query.body)
312
+ .to.equal('UPDATE Animal SET GUIDAnimal = :GUIDAnimal_0, UpdateDate = SYS_EXTRACT_UTC(SYSTIMESTAMP), UpdatingIDUser = :UpdatingIDUser_2, Name = :Name_3, Age = :Age_4 WHERE IDAnimal = :IDAnimal_w0');
313
+ }
314
+ );
315
+ test
316
+ (
317
+ 'Update Query with disabled stamps parameterizes UpdateDate',
318
+ function()
319
+ {
320
+ var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
321
+ .setScope('Animal')
322
+ .addFilter('IDAnimal', 9)
323
+ .setDisableAutoUserStamp(true)
324
+ .setDisableAutoDateStamp(true)
325
+ .addRecord({
326
+ IDAnimal:82,
327
+ GUIDAnimal:'1111-2222-3333-4444-5555-6666-7777',
328
+ CreateDate:false,
329
+ CreatingIDUser:false,
330
+ UpdateDate:false,
331
+ UpdatingIDUser:false,
332
+ Name:'Froo Froo',
333
+ Age:18
334
+ });
335
+ tmpQuery.query.schema = _AnimalSchemaWithoutDeleted;
336
+ tmpQuery.buildUpdateQuery();
337
+ Expect(tmpQuery.query.body)
338
+ .to.equal('UPDATE Animal SET GUIDAnimal = :GUIDAnimal_0, UpdateDate = :MANUAL_UpdateDate, Name = :Name_2, Age = :Age_3 WHERE IDAnimal = :IDAnimal_w0');
339
+ }
340
+ );
341
+ test
342
+ (
343
+ 'Delete Query converts to soft-delete UPDATE when a Deleted column exists',
344
+ function()
345
+ {
346
+ var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
347
+ .setScope('Animal')
348
+ .addFilter('IDAnimal', 10);
349
+ tmpQuery.query.schema = _AnimalSchema;
350
+ tmpQuery.buildDeleteQuery();
351
+ Expect(tmpQuery.query.body)
352
+ .to.equal('UPDATE Animal SET UpdateDate = SYS_EXTRACT_UTC(SYSTIMESTAMP), Deleted = 1, DeletingIDUser = :DeletingIDUser_2, DeleteDate = SYS_EXTRACT_UTC(SYSTIMESTAMP) WHERE IDAnimal = :IDAnimal_w0 AND Deleted = :Deleted_w1');
353
+ }
354
+ );
355
+ test
356
+ (
357
+ 'Delete Query is a hard DELETE when there is no Deleted column',
358
+ function()
359
+ {
360
+ var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
361
+ .setScope('Animal')
362
+ .addFilter('IDAnimal', 9)
363
+ .addRecord({IDAnimal:82, GUIDAnimal:'1111-2222'});
364
+ tmpQuery.query.schema = _AnimalSchemaWithoutDeleted;
365
+ tmpQuery.buildDeleteQuery();
366
+ Expect(tmpQuery.query.body)
367
+ .to.equal('DELETE FROM Animal WHERE IDAnimal = :IDAnimal_w0');
368
+ }
369
+ );
370
+ test
371
+ (
372
+ 'Delete Query is a hard DELETE when delete tracking is disabled',
373
+ function()
374
+ {
375
+ var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
376
+ .setScope('Animal')
377
+ .setDisableDeleteTracking(true)
378
+ .addFilter('IDAnimal', 9)
379
+ .addRecord({IDAnimal:82, GUIDAnimal:'1111-2222'});
380
+ tmpQuery.query.schema = _AnimalSchema;
381
+ tmpQuery.buildDeleteQuery();
382
+ Expect(tmpQuery.query.body)
383
+ .to.equal('DELETE FROM Animal WHERE IDAnimal = :IDAnimal_w0');
384
+ }
385
+ );
386
+ test
387
+ (
388
+ 'Undelete Query clears the Deleted bit',
389
+ function()
390
+ {
391
+ var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
392
+ .setScope('Animal')
393
+ .addFilter('IDAnimal', 10);
394
+ tmpQuery.query.schema = _AnimalSchema;
395
+ tmpQuery.buildUndeleteQuery();
396
+ Expect(tmpQuery.query.body)
397
+ .to.equal('UPDATE Animal SET UpdateDate = SYS_EXTRACT_UTC(SYSTIMESTAMP), UpdatingIDUser = :UpdatingIDUser_1, Deleted = 0 WHERE IDAnimal = :IDAnimal_w0');
398
+ }
399
+ );
400
+ }
401
+ );
402
+
403
+ suite
404
+ (
405
+ 'Count Query Generation',
406
+ function()
407
+ {
408
+ test
409
+ (
410
+ 'Count Query injects soft-delete filter and aliases as RowCount',
411
+ function()
412
+ {
413
+ var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle').setScope('Animal');
414
+ tmpQuery.query.schema = _AnimalSchema;
415
+ tmpQuery.buildCountQuery();
416
+ Expect(tmpQuery.query.body)
417
+ .to.equal('SELECT COUNT(*) AS RowCount FROM Animal WHERE Deleted = :Deleted_w0');
418
+ }
419
+ );
420
+ test
421
+ (
422
+ 'Count Query honors DISTINCT on the selected field',
423
+ function()
424
+ {
425
+ var tmpQuery = libFoxHound.new(_Fable).setDialect('Oracle')
426
+ .setScope('Animal')
427
+ .setDataElements(['Name'])
428
+ .setDistinct(true);
429
+ tmpQuery.query.schema = _AnimalSchema;
430
+ tmpQuery.buildCountQuery();
431
+ Expect(tmpQuery.query.body)
432
+ .to.equal('SELECT COUNT(DISTINCT Name) AS RowCount FROM Animal WHERE Deleted = :Deleted_w0');
433
+ }
434
+ );
435
+ }
436
+ );
437
+
438
+ suite
439
+ (
440
+ 'Oracle Bind Type Tracking',
441
+ function()
442
+ {
443
+ test
444
+ (
445
+ 'parameterTypes maps schema types to oracledb type strings',
446
+ function()
447
+ {
448
+ var tmpQuery = libFoxHound.new(_Fable)
449
+ .setDialect('Oracle')
450
+ .setScope('Animal')
451
+ .addFilter('Age', 15)
452
+ .addFilter('Name', 'Foo');
453
+ tmpQuery.query.schema = _AnimalSchema;
454
+ tmpQuery.buildReadQuery();
455
+ Expect(tmpQuery.query.parameterTypes.Age_w0).to.equal('NUMBER');
456
+ Expect(tmpQuery.query.parameterTypes.Name_w1).to.equal('STRING');
457
+ Expect(tmpQuery.query.parameterTypes.Deleted_w2).to.equal('NUMBER');
458
+ }
459
+ );
460
+ }
461
+ );
462
+ }
463
+ );
@@ -1,73 +0,0 @@
1
- /* ============================================================================
2
- Pict Docuserve - Base Styles
3
- ============================================================================ */
4
-
5
- /* Reset and base */
6
- *, *::before, *::after {
7
- box-sizing: border-box;
8
- }
9
-
10
- html, body {
11
- margin: 0;
12
- padding: 0;
13
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
14
- font-size: 16px;
15
- line-height: 1.5;
16
- color: #423D37;
17
- background-color: #fff;
18
- -webkit-font-smoothing: antialiased;
19
- -moz-osx-font-smoothing: grayscale;
20
- }
21
-
22
- /* Typography */
23
- h1, h2, h3, h4, h5, h6 {
24
- margin-top: 0;
25
- line-height: 1.3;
26
- }
27
-
28
- a {
29
- color: #2E7D74;
30
- text-decoration: none;
31
- }
32
-
33
- a:hover {
34
- color: #256861;
35
- }
36
-
37
- /* Application container */
38
- #Docuserve-Application-Container {
39
- min-height: 100vh;
40
- }
41
-
42
- /* Utility: scrollbar styling */
43
- ::-webkit-scrollbar {
44
- width: 8px;
45
- }
46
-
47
- ::-webkit-scrollbar-track {
48
- background: #F5F0E8;
49
- }
50
-
51
- ::-webkit-scrollbar-thumb {
52
- background: #D4CCBE;
53
- border-radius: 4px;
54
- }
55
-
56
- ::-webkit-scrollbar-thumb:hover {
57
- background: #B5AA9A;
58
- }
59
-
60
- /* Responsive adjustments */
61
- @media (max-width: 768px) {
62
- html {
63
- font-size: 14px;
64
- }
65
-
66
- #Docuserve-Sidebar-Container {
67
- display: none;
68
- }
69
-
70
- .docuserve-body {
71
- flex-direction: column;
72
- }
73
- }