pure-orm 4.0.2 → 4.1.4

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.
Files changed (209) hide show
  1. package/.benchmarks/bench-core-baseline.json +303 -0
  2. package/.eslintrc.json +20 -10
  3. package/README.md +0 -7
  4. package/coverage/clover.xml +1493 -1232
  5. package/coverage/coverage-final.json +103 -103
  6. package/coverage/lcov-report/dist/src/core.js.html +590 -383
  7. package/coverage/lcov-report/dist/src/driver-integrations/index.html +20 -20
  8. package/coverage/lcov-report/dist/src/driver-integrations/pgp.js.html +52 -52
  9. package/coverage/lcov-report/dist/src/index.html +28 -28
  10. package/coverage/lcov-report/dist/src/index.js.html +2 -2
  11. package/coverage/lcov-report/dist/src/orm.js.html +392 -209
  12. package/coverage/lcov-report/dist/test-utils/blog/entities.js.html +1 -1
  13. package/coverage/lcov-report/dist/test-utils/blog/index.html +1 -1
  14. package/coverage/lcov-report/dist/test-utils/blog/models/article.js.html +15 -15
  15. package/coverage/lcov-report/dist/test-utils/blog/models/article_tag.js.html +2 -2
  16. package/coverage/lcov-report/dist/test-utils/blog/models/index.html +1 -1
  17. package/coverage/lcov-report/dist/test-utils/blog/models/person.js.html +13 -13
  18. package/coverage/lcov-report/dist/test-utils/blog/models/tag.js.html +1 -1
  19. package/coverage/lcov-report/dist/test-utils/five/entities.js.html +1 -1
  20. package/coverage/lcov-report/dist/test-utils/five/index.html +1 -1
  21. package/coverage/lcov-report/dist/test-utils/five/models/index.html +1 -1
  22. package/coverage/lcov-report/dist/test-utils/five/models/line-item.js.html +4 -4
  23. package/coverage/lcov-report/dist/test-utils/five/models/order.js.html +3 -3
  24. package/coverage/lcov-report/dist/test-utils/five/models/parcel-event.js.html +6 -6
  25. package/coverage/lcov-report/dist/test-utils/five/models/parcel-line-item.js.html +7 -7
  26. package/coverage/lcov-report/dist/test-utils/five/models/parcel.js.html +2 -2
  27. package/coverage/lcov-report/dist/test-utils/fourteen/entities.js.html +1 -1
  28. package/coverage/lcov-report/dist/test-utils/fourteen/index.html +1 -1
  29. package/coverage/lcov-report/dist/test-utils/fourteen/models/customer.js.html +1 -1
  30. package/coverage/lcov-report/dist/test-utils/fourteen/models/index.html +1 -1
  31. package/coverage/lcov-report/dist/test-utils/fourteen/models/person.js.html +1 -1
  32. package/coverage/lcov-report/dist/test-utils/nine/entities.js.html +1 -1
  33. package/coverage/lcov-report/dist/test-utils/nine/index.html +1 -1
  34. package/coverage/lcov-report/dist/test-utils/nine/models/feature-switch.js.html +6 -6
  35. package/coverage/lcov-report/dist/test-utils/nine/models/index.html +1 -1
  36. package/coverage/lcov-report/dist/test-utils/order/entities.js.html +9 -9
  37. package/coverage/lcov-report/dist/test-utils/order/index.html +1 -1
  38. package/coverage/lcov-report/dist/test-utils/order/models/index.html +14 -14
  39. package/coverage/lcov-report/dist/test-utils/order/models/line-item.js.html +11 -11
  40. package/coverage/lcov-report/dist/test-utils/order/models/order.js.html +41 -41
  41. package/coverage/lcov-report/dist/test-utils/order/models/product-variant.js.html +18 -18
  42. package/coverage/lcov-report/dist/test-utils/order/models/product.js.html +17 -17
  43. package/coverage/lcov-report/dist/test-utils/order/models/utm-source.js.html +12 -12
  44. package/coverage/lcov-report/dist/test-utils/order-more/entities.js.html +1 -1
  45. package/coverage/lcov-report/dist/test-utils/order-more/index.html +1 -1
  46. package/coverage/lcov-report/dist/test-utils/order-more/models/actual-product-variant.js.html +3 -3
  47. package/coverage/lcov-report/dist/test-utils/order-more/models/color.js.html +6 -6
  48. package/coverage/lcov-report/dist/test-utils/order-more/models/customer.js.html +3 -3
  49. package/coverage/lcov-report/dist/test-utils/order-more/models/gender.js.html +4 -4
  50. package/coverage/lcov-report/dist/test-utils/order-more/models/index.html +23 -23
  51. package/coverage/lcov-report/dist/test-utils/order-more/models/inventory-level.js.html +11 -11
  52. package/coverage/lcov-report/dist/test-utils/order-more/models/line-item.js.html +15 -15
  53. package/coverage/lcov-report/dist/test-utils/order-more/models/order.js.html +39 -39
  54. package/coverage/lcov-report/dist/test-utils/order-more/models/parcel-event.js.html +6 -6
  55. package/coverage/lcov-report/dist/test-utils/order-more/models/parcel-line-item.js.html +7 -7
  56. package/coverage/lcov-report/dist/test-utils/order-more/models/parcel.js.html +2 -2
  57. package/coverage/lcov-report/dist/test-utils/order-more/models/physical-address.js.html +12 -12
  58. package/coverage/lcov-report/dist/test-utils/order-more/models/product-variant-image.js.html +7 -7
  59. package/coverage/lcov-report/dist/test-utils/order-more/models/product-variant.js.html +22 -22
  60. package/coverage/lcov-report/dist/test-utils/order-more/models/product.js.html +11 -11
  61. package/coverage/lcov-report/dist/test-utils/order-more/models/refund.js.html +11 -11
  62. package/coverage/lcov-report/dist/test-utils/order-more/models/shipment-actual-product-variant.js.html +9 -9
  63. package/coverage/lcov-report/dist/test-utils/order-more/models/shipment.js.html +4 -4
  64. package/coverage/lcov-report/dist/test-utils/order-more/models/size.js.html +4 -4
  65. package/coverage/lcov-report/dist/test-utils/order-more/models/utm-medium.js.html +15 -15
  66. package/coverage/lcov-report/dist/test-utils/order-more/models/utm-source.js.html +17 -17
  67. package/coverage/lcov-report/dist/test-utils/six/entities.js.html +1 -1
  68. package/coverage/lcov-report/dist/test-utils/six/index.html +1 -1
  69. package/coverage/lcov-report/dist/test-utils/six/models/customer.js.html +3 -3
  70. package/coverage/lcov-report/dist/test-utils/six/models/index.html +21 -21
  71. package/coverage/lcov-report/dist/test-utils/six/models/line-item.js.html +10 -10
  72. package/coverage/lcov-report/dist/test-utils/six/models/order.js.html +13 -13
  73. package/coverage/lcov-report/dist/test-utils/six/models/parcel-line-item.js.html +2 -2
  74. package/coverage/lcov-report/dist/test-utils/six/models/parcel.js.html +2 -2
  75. package/coverage/lcov-report/dist/test-utils/thirteen/entities.js.html +1 -1
  76. package/coverage/lcov-report/dist/test-utils/thirteen/index.html +1 -1
  77. package/coverage/lcov-report/dist/test-utils/thirteen/models/audience.js.html +2 -2
  78. package/coverage/lcov-report/dist/test-utils/thirteen/models/brand.js.html +2 -2
  79. package/coverage/lcov-report/dist/test-utils/thirteen/models/category.js.html +1 -1
  80. package/coverage/lcov-report/dist/test-utils/thirteen/models/index.html +14 -14
  81. package/coverage/lcov-report/dist/test-utils/thirteen/models/member.js.html +2 -2
  82. package/coverage/lcov-report/dist/test-utils/thirteen/models/passion.js.html +2 -2
  83. package/coverage/lcov-report/dist/test-utils/thirteen/models/product.js.html +10 -10
  84. package/coverage/lcov-report/dist/test-utils/thirteen/models/recommendation-audience.js.html +2 -2
  85. package/coverage/lcov-report/dist/test-utils/thirteen/models/recommendation.js.html +2 -2
  86. package/coverage/lcov-report/dist/test-utils/three/index.html +1 -1
  87. package/coverage/lcov-report/dist/test-utils/three/results.js.html +1 -1
  88. package/coverage/lcov-report/dist/test-utils/twelve/entities.js.html +1 -1
  89. package/coverage/lcov-report/dist/test-utils/twelve/index.html +1 -1
  90. package/coverage/lcov-report/dist/test-utils/twelve/models/index.html +1 -1
  91. package/coverage/lcov-report/dist/test-utils/twelve/models/member.js.html +1 -1
  92. package/coverage/lcov-report/dist/test-utils/twelve/models/prompt.js.html +2 -2
  93. package/coverage/lcov-report/dist/test-utils/two/index.html +1 -1
  94. package/coverage/lcov-report/dist/test-utils/two/results.js.html +1 -1
  95. package/coverage/lcov-report/index.html +103 -103
  96. package/coverage/lcov-report/src/core.ts.html +841 -415
  97. package/coverage/lcov-report/src/driver-integrations/index.html +20 -20
  98. package/coverage/lcov-report/src/driver-integrations/pgp.ts.html +63 -63
  99. package/coverage/lcov-report/src/index.html +28 -28
  100. package/coverage/lcov-report/src/index.ts.html +1 -1
  101. package/coverage/lcov-report/src/orm.ts.html +388 -298
  102. package/coverage/lcov-report/test-utils/blog/entities.ts.html +1 -1
  103. package/coverage/lcov-report/test-utils/blog/index.html +1 -1
  104. package/coverage/lcov-report/test-utils/blog/models/article.ts.html +15 -15
  105. package/coverage/lcov-report/test-utils/blog/models/article_tag.ts.html +2 -2
  106. package/coverage/lcov-report/test-utils/blog/models/index.html +1 -1
  107. package/coverage/lcov-report/test-utils/blog/models/person.ts.html +13 -13
  108. package/coverage/lcov-report/test-utils/blog/models/tag.ts.html +1 -1
  109. package/coverage/lcov-report/test-utils/five/entities.ts.html +1 -1
  110. package/coverage/lcov-report/test-utils/five/index.html +1 -1
  111. package/coverage/lcov-report/test-utils/five/models/index.html +1 -1
  112. package/coverage/lcov-report/test-utils/five/models/line-item.ts.html +4 -4
  113. package/coverage/lcov-report/test-utils/five/models/order.ts.html +3 -3
  114. package/coverage/lcov-report/test-utils/five/models/parcel-event.ts.html +6 -6
  115. package/coverage/lcov-report/test-utils/five/models/parcel-line-item.ts.html +7 -7
  116. package/coverage/lcov-report/test-utils/five/models/parcel.ts.html +2 -2
  117. package/coverage/lcov-report/test-utils/fourteen/entities.ts.html +1 -1
  118. package/coverage/lcov-report/test-utils/fourteen/index.html +1 -1
  119. package/coverage/lcov-report/test-utils/fourteen/models/customer.ts.html +1 -1
  120. package/coverage/lcov-report/test-utils/fourteen/models/index.html +1 -1
  121. package/coverage/lcov-report/test-utils/fourteen/models/person.ts.html +1 -1
  122. package/coverage/lcov-report/test-utils/nine/entities.ts.html +1 -1
  123. package/coverage/lcov-report/test-utils/nine/index.html +1 -1
  124. package/coverage/lcov-report/test-utils/nine/models/feature-switch.ts.html +8 -8
  125. package/coverage/lcov-report/test-utils/nine/models/index.html +1 -1
  126. package/coverage/lcov-report/test-utils/order/entities.ts.html +2 -2
  127. package/coverage/lcov-report/test-utils/order/index.html +1 -1
  128. package/coverage/lcov-report/test-utils/order/models/index.html +14 -14
  129. package/coverage/lcov-report/test-utils/order/models/line-item.ts.html +5 -5
  130. package/coverage/lcov-report/test-utils/order/models/order.ts.html +36 -36
  131. package/coverage/lcov-report/test-utils/order/models/product-variant.ts.html +13 -13
  132. package/coverage/lcov-report/test-utils/order/models/product.ts.html +13 -13
  133. package/coverage/lcov-report/test-utils/order/models/utm-source.ts.html +8 -8
  134. package/coverage/lcov-report/test-utils/order-more/entities.ts.html +1 -1
  135. package/coverage/lcov-report/test-utils/order-more/index.html +1 -1
  136. package/coverage/lcov-report/test-utils/order-more/models/actual-product-variant.ts.html +3 -3
  137. package/coverage/lcov-report/test-utils/order-more/models/color.ts.html +6 -6
  138. package/coverage/lcov-report/test-utils/order-more/models/customer.ts.html +3 -3
  139. package/coverage/lcov-report/test-utils/order-more/models/gender.ts.html +4 -4
  140. package/coverage/lcov-report/test-utils/order-more/models/index.html +23 -23
  141. package/coverage/lcov-report/test-utils/order-more/models/inventory-level.ts.html +11 -11
  142. package/coverage/lcov-report/test-utils/order-more/models/line-item.ts.html +15 -15
  143. package/coverage/lcov-report/test-utils/order-more/models/order.ts.html +45 -45
  144. package/coverage/lcov-report/test-utils/order-more/models/parcel-event.ts.html +6 -6
  145. package/coverage/lcov-report/test-utils/order-more/models/parcel-line-item.ts.html +7 -7
  146. package/coverage/lcov-report/test-utils/order-more/models/parcel.ts.html +2 -2
  147. package/coverage/lcov-report/test-utils/order-more/models/physical-address.ts.html +12 -12
  148. package/coverage/lcov-report/test-utils/order-more/models/product-variant-image.ts.html +7 -7
  149. package/coverage/lcov-report/test-utils/order-more/models/product-variant.ts.html +22 -22
  150. package/coverage/lcov-report/test-utils/order-more/models/product.ts.html +11 -11
  151. package/coverage/lcov-report/test-utils/order-more/models/refund.ts.html +11 -11
  152. package/coverage/lcov-report/test-utils/order-more/models/shipment-actual-product-variant.ts.html +9 -9
  153. package/coverage/lcov-report/test-utils/order-more/models/shipment.ts.html +4 -4
  154. package/coverage/lcov-report/test-utils/order-more/models/size.ts.html +4 -4
  155. package/coverage/lcov-report/test-utils/order-more/models/utm-medium.ts.html +15 -15
  156. package/coverage/lcov-report/test-utils/order-more/models/utm-source.ts.html +17 -17
  157. package/coverage/lcov-report/test-utils/six/entities.ts.html +1 -1
  158. package/coverage/lcov-report/test-utils/six/index.html +1 -1
  159. package/coverage/lcov-report/test-utils/six/models/customer.ts.html +3 -3
  160. package/coverage/lcov-report/test-utils/six/models/index.html +21 -21
  161. package/coverage/lcov-report/test-utils/six/models/line-item.ts.html +10 -10
  162. package/coverage/lcov-report/test-utils/six/models/order.ts.html +13 -13
  163. package/coverage/lcov-report/test-utils/six/models/parcel-line-item.ts.html +2 -2
  164. package/coverage/lcov-report/test-utils/six/models/parcel.ts.html +2 -2
  165. package/coverage/lcov-report/test-utils/thirteen/entities.ts.html +1 -1
  166. package/coverage/lcov-report/test-utils/thirteen/index.html +1 -1
  167. package/coverage/lcov-report/test-utils/thirteen/models/audience.ts.html +2 -2
  168. package/coverage/lcov-report/test-utils/thirteen/models/brand.ts.html +2 -2
  169. package/coverage/lcov-report/test-utils/thirteen/models/category.ts.html +1 -1
  170. package/coverage/lcov-report/test-utils/thirteen/models/index.html +14 -14
  171. package/coverage/lcov-report/test-utils/thirteen/models/member.ts.html +2 -2
  172. package/coverage/lcov-report/test-utils/thirteen/models/passion.ts.html +2 -2
  173. package/coverage/lcov-report/test-utils/thirteen/models/product.ts.html +10 -10
  174. package/coverage/lcov-report/test-utils/thirteen/models/recommendation-audience.ts.html +2 -2
  175. package/coverage/lcov-report/test-utils/thirteen/models/recommendation.ts.html +2 -2
  176. package/coverage/lcov-report/test-utils/three/index.html +1 -1
  177. package/coverage/lcov-report/test-utils/three/results.js.html +1 -1
  178. package/coverage/lcov-report/test-utils/twelve/entities.ts.html +1 -1
  179. package/coverage/lcov-report/test-utils/twelve/index.html +1 -1
  180. package/coverage/lcov-report/test-utils/twelve/models/index.html +1 -1
  181. package/coverage/lcov-report/test-utils/twelve/models/member.ts.html +1 -1
  182. package/coverage/lcov-report/test-utils/twelve/models/prompt.ts.html +2 -2
  183. package/coverage/lcov-report/test-utils/two/index.html +1 -1
  184. package/coverage/lcov-report/test-utils/two/results.js.html +1 -1
  185. package/coverage/lcov.info +2136 -1991
  186. package/dist/example/data-access/person.d.ts +1 -1
  187. package/dist/src/core.d.ts +13 -7
  188. package/dist/src/core.js +258 -189
  189. package/dist/src/core.spec.js +413 -0
  190. package/dist/src/driver-integrations/index.d.ts +5 -5
  191. package/dist/src/driver-integrations/pgp.spec.d.ts +1 -0
  192. package/dist/src/driver-integrations/pgp.spec.js +376 -0
  193. package/dist/src/orm.d.ts +9 -9
  194. package/dist/src/orm.js +137 -76
  195. package/dist/src/orm.spec.js +535 -85
  196. package/dist/test-utils/nine/models/feature-switch.d.ts +2 -2
  197. package/dist/test-utils/nine/models/feature-switch.ts +2 -2
  198. package/example/data-access/person.ts +1 -1
  199. package/package.json +9 -6
  200. package/scripts/bench-core.js +636 -0
  201. package/scripts/check-bench-scenarios.js +47 -0
  202. package/src/core.spec.ts +485 -2
  203. package/src/core.ts +369 -227
  204. package/src/driver-integrations/index.ts +5 -5
  205. package/src/driver-integrations/pgp.spec.ts +444 -0
  206. package/src/driver-integrations/pgp.ts +5 -5
  207. package/src/orm.spec.ts +592 -88
  208. package/src/orm.ts +173 -143
  209. package/test-utils/nine/models/feature-switch.ts +2 -2
@@ -26,33 +26,33 @@ export interface ICoreIntegratedDriver extends ICore {
26
26
  query: string,
27
27
  values?: object,
28
28
  errorHandler?: (err: Error) => never
29
- ) => T;
29
+ ) => Promise<T>;
30
30
 
31
31
  // Execute a query returning either single model or undefined, or throws.
32
32
  oneOrNone: <T extends IModel>(
33
33
  query: string,
34
34
  values?: object,
35
35
  errorHandler?: (err: Error) => never
36
- ) => T | void;
36
+ ) => Promise<T | void>;
37
37
 
38
38
  // Execute a query returning a Collection with at least one model, or throws.
39
39
  many: <T extends ICollection<IModel>>(
40
40
  query: string,
41
41
  values?: object,
42
42
  errorHandler?: (err: Error) => never
43
- ) => T;
43
+ ) => Promise<T>;
44
44
 
45
45
  // Execute a query returning a Collection.
46
46
  any: <T extends ICollection<IModel>>(
47
47
  query: string,
48
48
  values?: object,
49
49
  errorHandler?: (err: Error) => never
50
- ) => T | void;
50
+ ) => Promise<T | void>;
51
51
 
52
52
  // Execute a query returning null.
53
53
  none: (
54
54
  query: string,
55
55
  values?: object,
56
56
  errorHandler?: (err: Error) => never
57
- ) => void;
57
+ ) => Promise<void>;
58
58
  }
@@ -0,0 +1,444 @@
1
+ /* eslint-disable max-len */
2
+ import { createForPGP } from './pgp';
3
+ import { createCore, IModel, ICollection } from '../core';
4
+ import { entities as orderEntities } from '../../test-utils/order/entities';
5
+
6
+ class SimpleModel implements IModel {
7
+ id: number;
8
+ constructor(props: any) {
9
+ this.id = props.id;
10
+ }
11
+ }
12
+ class SimpleCollection implements ICollection<SimpleModel> {
13
+ models: Array<SimpleModel>;
14
+ constructor({ models }: any) {
15
+ this.models = models;
16
+ }
17
+ }
18
+
19
+ const simpleEntities = [
20
+ {
21
+ tableName: 'widget',
22
+ columns: ['id'] as any,
23
+ Model: SimpleModel,
24
+ Collection: SimpleCollection
25
+ }
26
+ ];
27
+
28
+ const makeRow = (id: number) => ({
29
+ 'widget#id': id
30
+ });
31
+
32
+ const makeOrderRow = (id: number, email: string) => ({
33
+ 'order#id': id,
34
+ 'order#email': email,
35
+ 'order#browser_ip': null,
36
+ 'order#browser_user_agent': null,
37
+ 'order#kujo_imported_date': null,
38
+ 'order#created_date': null,
39
+ 'order#cancel_reason': null,
40
+ 'order#cancelled_date': null,
41
+ 'order#closed_date': null,
42
+ 'order#processed_date': null,
43
+ 'order#updated_date': null,
44
+ 'order#note': null,
45
+ 'order#subtotal_price': null,
46
+ 'order#taxes_included': null,
47
+ 'order#total_discounts': null,
48
+ 'order#total_price': null,
49
+ 'order#total_tax': null,
50
+ 'order#total_weight': null,
51
+ 'order#order_status_url': null,
52
+ 'order#utm_source_id': null,
53
+ 'order#utm_medium_id': null,
54
+ 'order#utm_campaign': null,
55
+ 'order#utm_content': null,
56
+ 'order#utm_term': null
57
+ });
58
+
59
+ /* -------------------------------------------------------------------------*/
60
+ /* one ---------------------------------------------------------------------*/
61
+ /* -------------------------------------------------------------------------*/
62
+
63
+ describe('one', () => {
64
+ test('calls db.many and returns a single model', async () => {
65
+ const core = createCore({ entities: simpleEntities });
66
+ const db = { many: jest.fn().mockResolvedValue([makeRow(1)]) };
67
+ const pgp = createForPGP({ core, db });
68
+
69
+ const result = await pgp.one('SELECT ...', { id: 1 });
70
+ expect(db.many).toHaveBeenCalledWith('SELECT ...', { id: 1 });
71
+ expect(result.id).toEqual(1);
72
+ });
73
+
74
+ test('throws when db.many returns empty array', async () => {
75
+ const core = createCore({ entities: simpleEntities });
76
+ const db = { many: jest.fn().mockResolvedValue([]) };
77
+ const pgp = createForPGP({ core, db });
78
+
79
+ await expect(pgp.one('SELECT ...')).rejects.toThrow();
80
+ });
81
+
82
+ test('throws when result nests into more than one model', async () => {
83
+ const core = createCore({ entities: simpleEntities });
84
+ const db = {
85
+ many: jest.fn().mockResolvedValue([makeRow(1), makeRow(2)])
86
+ };
87
+ const pgp = createForPGP({ core, db });
88
+
89
+ await expect(pgp.one('SELECT ...')).rejects.toThrow();
90
+ });
91
+
92
+ test('passes values to db.many', async () => {
93
+ const core = createCore({ entities: simpleEntities });
94
+ const db = { many: jest.fn().mockResolvedValue([makeRow(5)]) };
95
+ const pgp = createForPGP({ core, db });
96
+
97
+ await pgp.one('SELECT ... WHERE id = $1', [5]);
98
+ expect(db.many).toHaveBeenCalledWith('SELECT ... WHERE id = $1', [5]);
99
+ });
100
+ });
101
+
102
+ /* -------------------------------------------------------------------------*/
103
+ /* oneOrNone ---------------------------------------------------------------*/
104
+ /* -------------------------------------------------------------------------*/
105
+
106
+ describe('oneOrNone', () => {
107
+ test('returns a model when one result exists', async () => {
108
+ const core = createCore({ entities: simpleEntities });
109
+ const db = { any: jest.fn().mockResolvedValue([makeRow(1)]) };
110
+ const pgp = createForPGP({ core, db });
111
+
112
+ const result = await pgp.oneOrNone('SELECT ...', { id: 1 });
113
+ expect(db.any).toHaveBeenCalledWith('SELECT ...', { id: 1 });
114
+ expect(result!.id).toEqual(1);
115
+ });
116
+
117
+ test('returns undefined when no results', async () => {
118
+ const core = createCore({ entities: simpleEntities });
119
+ const db = { any: jest.fn().mockResolvedValue([]) };
120
+ const pgp = createForPGP({ core, db });
121
+
122
+ const result = await pgp.oneOrNone('SELECT ...');
123
+ expect(result).toBeUndefined();
124
+ });
125
+
126
+ test('throws when more than one result', async () => {
127
+ const core = createCore({ entities: simpleEntities });
128
+ const db = {
129
+ any: jest.fn().mockResolvedValue([makeRow(1), makeRow(2)])
130
+ };
131
+ const pgp = createForPGP({ core, db });
132
+
133
+ await expect(pgp.oneOrNone('SELECT ...')).rejects.toThrow();
134
+ });
135
+ });
136
+
137
+ /* -------------------------------------------------------------------------*/
138
+ /* many --------------------------------------------------------------------*/
139
+ /* -------------------------------------------------------------------------*/
140
+
141
+ describe('many', () => {
142
+ test('returns a collection with at least one model', async () => {
143
+ const core = createCore({ entities: simpleEntities });
144
+ const db = {
145
+ any: jest.fn().mockResolvedValue([makeRow(1), makeRow(2)])
146
+ };
147
+ const pgp = createForPGP({ core, db });
148
+
149
+ const result: any = await pgp.many('SELECT ...');
150
+ expect(db.any).toHaveBeenCalledWith('SELECT ...', undefined);
151
+ expect(result.models.length).toEqual(2);
152
+ expect(result.models[0].id).toEqual(1);
153
+ expect(result.models[1].id).toEqual(2);
154
+ });
155
+
156
+ test('works with a single result row', async () => {
157
+ const core = createCore({ entities: simpleEntities });
158
+ const db = { any: jest.fn().mockResolvedValue([makeRow(42)]) };
159
+ const pgp = createForPGP({ core, db });
160
+
161
+ const result: any = await pgp.many('SELECT ...');
162
+ expect(result.models.length).toEqual(1);
163
+ expect(result.models[0].id).toEqual(42);
164
+ });
165
+
166
+ test('throws when no results', async () => {
167
+ const core = createCore({ entities: simpleEntities });
168
+ const db = { any: jest.fn().mockResolvedValue([]) };
169
+ const pgp = createForPGP({ core, db });
170
+
171
+ await expect(pgp.many('SELECT ...')).rejects.toThrow();
172
+ });
173
+
174
+ test('passes values to db.any', async () => {
175
+ const core = createCore({ entities: simpleEntities });
176
+ const db = { any: jest.fn().mockResolvedValue([makeRow(1)]) };
177
+ const pgp = createForPGP({ core, db });
178
+
179
+ await pgp.many('SELECT ... WHERE x = $1', [10]);
180
+ expect(db.any).toHaveBeenCalledWith('SELECT ... WHERE x = $1', [10]);
181
+ });
182
+ });
183
+
184
+ /* -------------------------------------------------------------------------*/
185
+ /* any ---------------------------------------------------------------------*/
186
+ /* -------------------------------------------------------------------------*/
187
+
188
+ describe('any', () => {
189
+ test('returns a collection when results exist', async () => {
190
+ const core = createCore({ entities: simpleEntities });
191
+ const db = {
192
+ result: jest.fn().mockResolvedValue({
193
+ rows: [makeRow(1), makeRow(2)],
194
+ fields: [{ name: 'widget#id' }]
195
+ })
196
+ };
197
+ const pgp = createForPGP({ core, db });
198
+
199
+ const result: any = await pgp.any('SELECT ...');
200
+ expect(db.result).toHaveBeenCalledWith('SELECT ...', undefined);
201
+ expect(result.models.length).toEqual(2);
202
+ });
203
+
204
+ test('returns empty collection when no results', async () => {
205
+ const core = createCore({ entities: simpleEntities });
206
+ const db = {
207
+ result: jest.fn().mockResolvedValue({
208
+ rows: [],
209
+ fields: [{ name: 'widget#id' }]
210
+ })
211
+ };
212
+ const pgp = createForPGP({ core, db });
213
+
214
+ const result: any = await pgp.any('SELECT ...');
215
+ expect(result.models).toBeDefined();
216
+ expect(result.models.length).toEqual(0);
217
+ });
218
+
219
+ test('extracts table name from first field name', async () => {
220
+ const core = createCore({ entities: orderEntities });
221
+ const db = {
222
+ result: jest.fn().mockResolvedValue({
223
+ rows: [makeOrderRow(1, 'a@b.com')],
224
+ fields: [{ name: 'order#id' }]
225
+ })
226
+ };
227
+ const pgp = createForPGP({ core, db });
228
+
229
+ const result: any = await pgp.any('SELECT ...');
230
+ expect(result.models.length).toEqual(1);
231
+ expect(result.models[0].id).toEqual(1);
232
+ });
233
+
234
+ test('passes values to db.result', async () => {
235
+ const core = createCore({ entities: simpleEntities });
236
+ const db = {
237
+ result: jest.fn().mockResolvedValue({
238
+ rows: [],
239
+ fields: [{ name: 'widget#id' }]
240
+ })
241
+ };
242
+ const pgp = createForPGP({ core, db });
243
+
244
+ await pgp.any('SELECT ... WHERE x = $1', [5]);
245
+ expect(db.result).toHaveBeenCalledWith('SELECT ... WHERE x = $1', [5]);
246
+ });
247
+ });
248
+
249
+ /* -------------------------------------------------------------------------*/
250
+ /* none --------------------------------------------------------------------*/
251
+ /* -------------------------------------------------------------------------*/
252
+
253
+ describe('none', () => {
254
+ test('calls db.none and resolves to null', async () => {
255
+ const core = createCore({ entities: simpleEntities });
256
+ const db = { none: jest.fn().mockResolvedValue(undefined) };
257
+ const pgp = createForPGP({ core, db });
258
+
259
+ const result = await pgp.none('DELETE ...');
260
+ expect(db.none).toHaveBeenCalledWith('DELETE ...', undefined);
261
+ expect(result).toBeNull();
262
+ });
263
+
264
+ test('passes values to db.none', async () => {
265
+ const core = createCore({ entities: simpleEntities });
266
+ const db = { none: jest.fn().mockResolvedValue(undefined) };
267
+ const pgp = createForPGP({ core, db });
268
+
269
+ await pgp.none('DELETE ... WHERE id = $(id)', { id: 7 });
270
+ expect(db.none).toHaveBeenCalledWith('DELETE ... WHERE id = $(id)', {
271
+ id: 7
272
+ });
273
+ });
274
+ });
275
+
276
+ /* -------------------------------------------------------------------------*/
277
+ /* Error handling ----------------------------------------------------------*/
278
+ /* -------------------------------------------------------------------------*/
279
+
280
+ describe('error handling', () => {
281
+ test('defaultErrorHandler rethrows the error', async () => {
282
+ const core = createCore({ entities: simpleEntities });
283
+ const err = new Error('db error');
284
+ const db = { many: jest.fn().mockRejectedValue(err) };
285
+ const pgp = createForPGP({ core, db });
286
+
287
+ await expect(pgp.one('SELECT ...')).rejects.toThrow('db error');
288
+ });
289
+
290
+ test('defaultErrorHandler calls logError for non-QueryResultError', async () => {
291
+ const logError = jest.fn((err: Error) => {
292
+ throw err;
293
+ }) as any;
294
+ const core = createCore({ entities: simpleEntities });
295
+ const err = new Error('unexpected error');
296
+ const db = { many: jest.fn().mockRejectedValue(err) };
297
+ const pgp = createForPGP({ core, db, logError });
298
+
299
+ await expect(pgp.one('SELECT ...')).rejects.toThrow('unexpected error');
300
+ expect(logError).toHaveBeenCalledWith(err);
301
+ });
302
+
303
+ test('defaultErrorHandler does not call logError for QueryResultError', async () => {
304
+ const logError = jest.fn((err: Error) => {
305
+ throw err;
306
+ }) as any;
307
+ const core = createCore({ entities: simpleEntities });
308
+ const err = new Error('no data');
309
+ err.name = 'QueryResultError';
310
+ const db = { many: jest.fn().mockRejectedValue(err) };
311
+ const pgp = createForPGP({ core, db, logError });
312
+
313
+ await expect(pgp.one('SELECT ...')).rejects.toThrow('no data');
314
+ expect(logError).not.toHaveBeenCalled();
315
+ });
316
+
317
+ test('custom errorHandler overrides defaultErrorHandler', async () => {
318
+ const core = createCore({ entities: simpleEntities });
319
+ const err = new Error('db failure');
320
+ const db = { many: jest.fn().mockRejectedValue(err) };
321
+ const pgp = createForPGP({ core, db });
322
+ const customHandler = jest.fn((_err: Error) => {
323
+ throw new Error('custom handled');
324
+ }) as any;
325
+
326
+ await expect(
327
+ pgp.one('SELECT ...', undefined, customHandler)
328
+ ).rejects.toThrow('custom handled');
329
+ expect(customHandler).toHaveBeenCalledWith(err);
330
+ });
331
+
332
+ test('custom errorHandler works for oneOrNone', async () => {
333
+ const core = createCore({ entities: simpleEntities });
334
+ const err = new Error('fail');
335
+ const db = { any: jest.fn().mockRejectedValue(err) };
336
+ const pgp = createForPGP({ core, db });
337
+ const customHandler = jest.fn((_err: Error) => {
338
+ throw new Error('caught');
339
+ }) as any;
340
+
341
+ await expect(
342
+ pgp.oneOrNone('SELECT ...', undefined, customHandler)
343
+ ).rejects.toThrow('caught');
344
+ expect(customHandler).toHaveBeenCalledWith(err);
345
+ });
346
+
347
+ test('custom errorHandler works for many', async () => {
348
+ const core = createCore({ entities: simpleEntities });
349
+ const err = new Error('fail');
350
+ const db = { any: jest.fn().mockRejectedValue(err) };
351
+ const pgp = createForPGP({ core, db });
352
+ const customHandler = jest.fn((_err: Error) => {
353
+ throw new Error('caught');
354
+ }) as any;
355
+
356
+ await expect(
357
+ pgp.many('SELECT ...', undefined, customHandler)
358
+ ).rejects.toThrow('caught');
359
+ });
360
+
361
+ test('custom errorHandler works for any', async () => {
362
+ const core = createCore({ entities: simpleEntities });
363
+ const err = new Error('fail');
364
+ const db = { result: jest.fn().mockRejectedValue(err) };
365
+ const pgp = createForPGP({ core, db });
366
+ const customHandler = jest.fn((_err: Error) => {
367
+ throw new Error('caught');
368
+ }) as any;
369
+
370
+ await expect(
371
+ pgp.any('SELECT ...', undefined, customHandler)
372
+ ).rejects.toThrow('caught');
373
+ });
374
+
375
+ test('custom errorHandler works for none', async () => {
376
+ const core = createCore({ entities: simpleEntities });
377
+ const err = new Error('fail');
378
+ const db = { none: jest.fn().mockRejectedValue(err) };
379
+ const pgp = createForPGP({ core, db });
380
+ const customHandler = jest.fn((_err: Error) => {
381
+ throw new Error('caught');
382
+ }) as any;
383
+
384
+ await expect(
385
+ pgp.none('DELETE ...', undefined, customHandler)
386
+ ).rejects.toThrow('caught');
387
+ });
388
+ });
389
+
390
+ /* -------------------------------------------------------------------------*/
391
+ /* Returned object structure -----------------------------------------------*/
392
+ /* -------------------------------------------------------------------------*/
393
+
394
+ describe('createForPGP return structure', () => {
395
+ test('includes all core methods', () => {
396
+ const core = createCore({ entities: simpleEntities });
397
+ const db = {
398
+ many: jest.fn(),
399
+ any: jest.fn(),
400
+ result: jest.fn(),
401
+ none: jest.fn()
402
+ };
403
+ const pgp = createForPGP({ core, db });
404
+
405
+ expect(typeof pgp.createFromDatabase).toBe('function');
406
+ expect(typeof pgp.createOneFromDatabase).toBe('function');
407
+ expect(typeof pgp.createOneOrNoneFromDatabase).toBe('function');
408
+ expect(typeof pgp.createManyFromDatabase).toBe('function');
409
+ expect(typeof pgp.createAnyFromDatabase).toBe('function');
410
+ expect(typeof pgp.getEntityByModel).toBe('function');
411
+ expect(typeof pgp.getEntityByTableName).toBe('function');
412
+ expect(pgp.tables).toBeDefined();
413
+ });
414
+
415
+ test('includes all query methods', () => {
416
+ const core = createCore({ entities: simpleEntities });
417
+ const db = {
418
+ many: jest.fn(),
419
+ any: jest.fn(),
420
+ result: jest.fn(),
421
+ none: jest.fn()
422
+ };
423
+ const pgp = createForPGP({ core, db });
424
+
425
+ expect(typeof pgp.one).toBe('function');
426
+ expect(typeof pgp.oneOrNone).toBe('function');
427
+ expect(typeof pgp.many).toBe('function');
428
+ expect(typeof pgp.any).toBe('function');
429
+ expect(typeof pgp.none).toBe('function');
430
+ });
431
+
432
+ test('exposes db reference', () => {
433
+ const core = createCore({ entities: simpleEntities });
434
+ const db = {
435
+ many: jest.fn(),
436
+ any: jest.fn(),
437
+ result: jest.fn(),
438
+ none: jest.fn()
439
+ };
440
+ const pgp = createForPGP({ core, db });
441
+
442
+ expect(pgp.db).toBe(db);
443
+ });
444
+ });
@@ -23,7 +23,7 @@ export const createForPGP = ({
23
23
  query: string,
24
24
  values?: object,
25
25
  errorHandler = defaultErrorHandler
26
- ): T => {
26
+ ): Promise<T> => {
27
27
  return db
28
28
  .many(query, values)
29
29
  .then((rows: any) => core.createOneFromDatabase(rows))
@@ -34,7 +34,7 @@ export const createForPGP = ({
34
34
  query: string,
35
35
  values?: object,
36
36
  errorHandler = defaultErrorHandler
37
- ): T | void => {
37
+ ): Promise<T | void> => {
38
38
  return db
39
39
  .any(query, values)
40
40
  .then((rows: any) => core.createOneOrNoneFromDatabase(rows))
@@ -45,7 +45,7 @@ export const createForPGP = ({
45
45
  query: string,
46
46
  values?: object,
47
47
  errorHandler = defaultErrorHandler
48
- ): T => {
48
+ ): Promise<T> => {
49
49
  return db
50
50
  .any(query, values)
51
51
  .then((rows: any) => core.createManyFromDatabase(rows))
@@ -56,7 +56,7 @@ export const createForPGP = ({
56
56
  query: string,
57
57
  values?: object,
58
58
  errorHandler = defaultErrorHandler
59
- ): T | void => {
59
+ ): Promise<T | void> => {
60
60
  return db
61
61
  .result(query, values)
62
62
  .then((result: any) =>
@@ -72,7 +72,7 @@ export const createForPGP = ({
72
72
  query: string,
73
73
  values?: object,
74
74
  errorHandler = defaultErrorHandler
75
- ): void => {
75
+ ): Promise<void> => {
76
76
  return db
77
77
  .none(query, values)
78
78
  .then(() => null)