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
package/src/orm.spec.ts CHANGED
@@ -1,113 +1,617 @@
1
1
  /* eslint-disable max-len */
2
2
  import { create } from './index';
3
3
  import { entities as orderEntities } from '../test-utils/order/entities';
4
+ import { Order } from '../test-utils/order/models/order';
5
+ import { UtmSource } from '../test-utils/order/models/utm-source';
4
6
 
5
- test('getSqlInsertParts', () => {
6
- const orm = create({
7
- entities: orderEntities,
8
- db: { $config: { pgp: true } }
7
+ const mockPgpDb = (overrides: any = {}) => ({
8
+ $config: { pgp: true },
9
+ many: jest.fn(),
10
+ any: jest.fn(),
11
+ result: jest.fn(),
12
+ none: jest.fn(),
13
+ ...overrides
14
+ });
15
+
16
+ /* -------------------------------------------------------------------------*/
17
+ /* Helper Utility Functions ------------------------------------------------*/
18
+ /* -------------------------------------------------------------------------*/
19
+
20
+ describe('getSqlInsertParts', () => {
21
+ test('generates correct insert parts for a model with defined values', () => {
22
+ const orm = create({
23
+ entities: orderEntities,
24
+ db: mockPgpDb()
25
+ });
26
+ const order = new Order({
27
+ id: 1,
28
+ email: 'test@test.com',
29
+ subtotalPrice: 100,
30
+ utmSourceId: 10
31
+ });
32
+ expect(orm.getSqlInsertParts(order)).toEqual({
33
+ columns: '"id", "email", "subtotal_price", "utm_source_id"',
34
+ values: [1, 'test@test.com', 100, 10],
35
+ valuesVar: ['$1', '$2', '$3', '$4']
36
+ });
9
37
  });
10
- const order = new orderEntities[0].Model({
11
- id: 1,
12
- email: 'test@test.com',
13
- subtotalPrice: 100,
14
- utmSourceId: 10
38
+
39
+ test('filters out undefined properties', () => {
40
+ const orm = create({
41
+ entities: orderEntities,
42
+ db: mockPgpDb()
43
+ });
44
+ const order = new Order({ id: 5 });
45
+ const result = orm.getSqlInsertParts(order);
46
+ expect(result.columns).toEqual('"id"');
47
+ expect(result.values).toEqual([5]);
48
+ expect(result.valuesVar).toEqual(['$1']);
15
49
  });
16
- expect(orm.getSqlInsertParts(order)).toEqual({
17
- columns: '"id", "email", "subtotal_price", "utm_source_id"',
18
- values: [1, 'test@test.com', 100, 10],
19
- valuesVar: ['$1', '$2', '$3', '$4']
50
+
51
+ test('includes all defined properties', () => {
52
+ const orm = create({
53
+ entities: orderEntities,
54
+ db: mockPgpDb()
55
+ });
56
+ const order = new Order({
57
+ id: 1,
58
+ email: 'a@b.com',
59
+ browserIp: '1.2.3.4',
60
+ subtotalPrice: 50,
61
+ totalPrice: 55
62
+ });
63
+ const result = orm.getSqlInsertParts(order);
64
+ expect(result.values).toContain(1);
65
+ expect(result.values).toContain('a@b.com');
66
+ expect(result.values).toContain(50);
67
+ expect(result.values).toContain(55);
68
+ expect(result.values.length).toEqual(result.valuesVar.length);
20
69
  });
21
70
  });
22
71
 
23
- test('getSqlUpdateParts', () => {
24
- const orm = create({
25
- entities: orderEntities,
26
- db: { $config: { pgp: true } }
72
+ describe('getSqlUpdateParts', () => {
73
+ test('generates correct update parts', () => {
74
+ const orm = create({
75
+ entities: orderEntities,
76
+ db: mockPgpDb()
77
+ });
78
+ const order = new Order({
79
+ id: 1,
80
+ email: 'test@test.com',
81
+ subtotalPrice: 100,
82
+ utmSourceId: 10
83
+ });
84
+ expect(orm.getSqlUpdateParts(order)).toEqual({
85
+ clause:
86
+ '"id" = $1, "email" = $2, "subtotal_price" = $3, "utm_source_id" = $4',
87
+ idVar: '$5',
88
+ values: [1, 'test@test.com', 100, 10, 1]
89
+ });
27
90
  });
28
- const order = new orderEntities[0].Model({
29
- id: 1,
30
- email: 'test@test.com',
31
- subtotalPrice: 100,
32
- utmSourceId: 10
91
+
92
+ test('appends the id value at the end of values array', () => {
93
+ const orm = create({
94
+ entities: orderEntities,
95
+ db: mockPgpDb()
96
+ });
97
+ const order = new Order({ id: 42, email: 'x@y.com' });
98
+ const result = orm.getSqlUpdateParts(order);
99
+ expect(result.values[result.values.length - 1]).toEqual(42);
33
100
  });
34
- expect(orm.getSqlUpdateParts(order)).toEqual({
35
- clause:
36
- '"id" = $1, "email" = $2, "subtotal_price" = $3, "utm_source_id" = $4',
37
- idVar: '$5',
38
- values: [1, 'test@test.com', 100, 10, 1]
101
+
102
+ test('custom "on" parameter uses that property for the id value', () => {
103
+ const orm = create({
104
+ entities: orderEntities,
105
+ db: mockPgpDb()
106
+ });
107
+ const order = new Order({ id: 1, email: 'x@y.com' });
108
+ const result = orm.getSqlUpdateParts(order, 'email');
109
+ expect(result.values[result.values.length - 1]).toEqual('x@y.com');
110
+ });
111
+
112
+ test('filters out undefined properties from clause', () => {
113
+ const orm = create({
114
+ entities: orderEntities,
115
+ db: mockPgpDb()
116
+ });
117
+ const order = new Order({ id: 7 });
118
+ const result = orm.getSqlUpdateParts(order);
119
+ expect(result.clause).toEqual('"id" = $1');
120
+ expect(result.idVar).toEqual('$2');
121
+ expect(result.values).toEqual([7, 7]);
39
122
  });
40
123
  });
41
124
 
42
- test('getMatchingParts', () => {
43
- const orm = create({
44
- entities: orderEntities,
45
- db: { $config: { pgp: true } }
125
+ describe('getMatchingParts', () => {
126
+ test('generates correct WHERE clause and values', () => {
127
+ const orm = create({
128
+ entities: orderEntities,
129
+ db: mockPgpDb()
130
+ });
131
+ const order = new Order({
132
+ id: 1,
133
+ email: 'test@test.com',
134
+ subtotalPrice: 100,
135
+ utmSourceId: 10
136
+ });
137
+ expect(orm.getMatchingParts(order)).toEqual({
138
+ values: [1, 'test@test.com', 100, 10],
139
+ whereClause:
140
+ '"order"."id" = $1 AND "order"."email" = $2 AND "order"."subtotal_price" = $3 AND "order"."utm_source_id" = $4'
141
+ });
46
142
  });
47
- const order = new orderEntities[0].Model({
48
- id: 1,
49
- email: 'test@test.com',
50
- subtotalPrice: 100,
51
- utmSourceId: 10
143
+
144
+ test('filters out null and undefined values', () => {
145
+ const orm = create({
146
+ entities: orderEntities,
147
+ db: mockPgpDb()
148
+ });
149
+ const order = new Order({ id: 1 });
150
+ const result = orm.getMatchingParts(order);
151
+ expect(result.whereClause).toEqual('"order"."id" = $1');
152
+ expect(result.values).toEqual([1]);
52
153
  });
53
- expect(orm.getMatchingParts(order)).toEqual({
54
- values: [1, 'test@test.com', 100, 10],
55
- whereClause:
56
- '"order"."id" = $1 AND "order"."email" = $2 AND "order"."subtotal_price" = $3 AND "order"."utm_source_id" = $4'
154
+ });
155
+
156
+ describe('getMatchingPartsObject', () => {
157
+ test('generates correct WHERE clause and object values', () => {
158
+ const orm = create({
159
+ entities: orderEntities,
160
+ db: mockPgpDb()
161
+ });
162
+ const order = new Order({
163
+ id: 1,
164
+ email: 'test@test.com',
165
+ subtotalPrice: 100,
166
+ utmSourceId: 10
167
+ });
168
+ expect(orm.getMatchingPartsObject(order)).toEqual({
169
+ values: {
170
+ 1: 1,
171
+ 2: 'test@test.com',
172
+ 3: 100,
173
+ 4: 10
174
+ },
175
+ whereClause:
176
+ '"order"."id" = $(1) AND "order"."email" = $(2) AND "order"."subtotal_price" = $(3) AND "order"."utm_source_id" = $(4)'
177
+ });
178
+ });
179
+
180
+ test('uses $() syntax for parameterized values', () => {
181
+ const orm = create({
182
+ entities: orderEntities,
183
+ db: mockPgpDb()
184
+ });
185
+ const order = new Order({ id: 5 });
186
+ const result = orm.getMatchingPartsObject(order);
187
+ expect(result.whereClause).toContain('$(1)');
188
+ expect(result.values).toEqual({ 1: 5 });
57
189
  });
58
190
  });
59
191
 
60
- test('getMatchingPartsObject', () => {
61
- const orm = create({
62
- entities: orderEntities,
63
- db: { $config: { pgp: true } }
64
- });
65
- const order = new orderEntities[0].Model({
66
- id: 1,
67
- email: 'test@test.com',
68
- subtotalPrice: 100,
69
- utmSourceId: 10
70
- });
71
- expect(orm.getMatchingPartsObject(order)).toEqual({
72
- values: {
73
- 1: 1,
74
- 2: 'test@test.com',
75
- 3: 100,
76
- 4: 10
77
- },
78
- whereClause:
79
- '"order"."id" = $(1) AND "order"."email" = $(2) AND "order"."subtotal_price" = $(3) AND "order"."utm_source_id" = $(4)'
192
+ describe('getValueBySqlColumn', () => {
193
+ test('retrieves values by SQL column name', () => {
194
+ const orm = create({
195
+ entities: orderEntities,
196
+ db: mockPgpDb()
197
+ });
198
+ const order = new Order({
199
+ id: 1,
200
+ email: 'test@test.com',
201
+ subtotalPrice: 100,
202
+ utmSourceId: 10
203
+ });
204
+ expect(orm.getValueBySqlColumn(order, 'id')).toEqual(1);
205
+ expect(orm.getValueBySqlColumn(order, 'email')).toEqual('test@test.com');
206
+ expect(orm.getValueBySqlColumn(order, 'subtotal_price')).toEqual(100);
207
+ expect(orm.getValueBySqlColumn(order, 'utm_source_id')).toEqual(10);
208
+ });
209
+
210
+ test('returns undefined for columns with no value set', () => {
211
+ const orm = create({
212
+ entities: orderEntities,
213
+ db: mockPgpDb()
214
+ });
215
+ const order = new Order({ id: 1 });
216
+ expect(orm.getValueBySqlColumn(order, 'email')).toBeUndefined();
80
217
  });
81
218
  });
82
219
 
83
- test('getValueBySqlColumn', () => {
84
- const orm = create({
85
- entities: orderEntities,
86
- db: { $config: { pgp: true } }
87
- });
88
- const order = new orderEntities[0].Model({
89
- id: 1,
90
- email: 'test@test.com',
91
- subtotalPrice: 100,
92
- utmSourceId: 10
93
- });
94
- expect(orm.getValueBySqlColumn(order, 'id')).toEqual(1);
95
- expect(orm.getValueBySqlColumn(order, 'email')).toEqual('test@test.com');
96
- expect(orm.getValueBySqlColumn(order, 'subtotal_price')).toEqual(100);
97
- expect(orm.getValueBySqlColumn(order, 'utm_source_id')).toEqual(10);
220
+ describe('getSqlColumnForPropertyName', () => {
221
+ test('maps property names to SQL column names', () => {
222
+ const orm = create({
223
+ entities: orderEntities,
224
+ db: mockPgpDb()
225
+ });
226
+ const order = new Order({ id: 1 });
227
+ expect(orm.getSqlColumnForPropertyName(order, 'id')).toEqual('id');
228
+ expect(orm.getSqlColumnForPropertyName(order, 'utmSourceId')).toEqual(
229
+ 'utm_source_id'
230
+ );
231
+ expect(orm.getSqlColumnForPropertyName(order, 'browserIP')).toEqual(
232
+ 'browser_ip'
233
+ );
234
+ });
235
+
236
+ test('returns undefined for non-existent property name', () => {
237
+ const orm = create({
238
+ entities: orderEntities,
239
+ db: mockPgpDb()
240
+ });
241
+ const order = new Order({ id: 1 });
242
+ expect(
243
+ orm.getSqlColumnForPropertyName(order, 'nonExistent')
244
+ ).toBeUndefined();
245
+ });
246
+ });
247
+
248
+ describe('getNewWith', () => {
249
+ test('creates a new model instance with specified SQL columns and values', () => {
250
+ const orm = create({
251
+ entities: orderEntities,
252
+ db: mockPgpDb()
253
+ });
254
+ const order = new Order({ id: 1 });
255
+ const newOrder = orm.getNewWith(
256
+ order,
257
+ ['id', 'email'],
258
+ [99, 'new@test.com']
259
+ );
260
+ expect(newOrder).toBeInstanceOf(Order);
261
+ expect(newOrder.id).toEqual(99);
262
+ expect(newOrder.email).toEqual('new@test.com');
263
+ });
264
+
265
+ test('creates model with single column', () => {
266
+ const orm = create({
267
+ entities: orderEntities,
268
+ db: mockPgpDb()
269
+ });
270
+ const order = new Order({ id: 1 });
271
+ const newOrder = orm.getNewWith(order, ['id'], [42]);
272
+ expect(newOrder).toBeInstanceOf(Order);
273
+ expect(newOrder.id).toEqual(42);
274
+ });
275
+
276
+ test('maps SQL column names to property names', () => {
277
+ const orm = create({
278
+ entities: orderEntities,
279
+ db: mockPgpDb()
280
+ });
281
+ const order = new Order({ id: 1 });
282
+ const newOrder = orm.getNewWith(
283
+ order,
284
+ ['id', 'subtotal_price', 'utm_source_id'],
285
+ [1, 200, 5]
286
+ );
287
+ expect(newOrder.id).toEqual(1);
288
+ expect(newOrder.subtotalPrice).toEqual(200);
289
+ expect(newOrder.utmSourceId).toEqual(5);
290
+ });
291
+ });
292
+
293
+ /* -------------------------------------------------------------------------*/
294
+ /* Unsupported driver error ------------------------------------------------*/
295
+ /* -------------------------------------------------------------------------*/
296
+
297
+ describe('unsupported database driver', () => {
298
+ test('throws for an unrecognized driver', () => {
299
+ expect(() =>
300
+ create({
301
+ entities: orderEntities,
302
+ db: { $config: {} }
303
+ })
304
+ ).toThrow('database driver is not yet supported');
305
+ });
306
+ });
307
+
308
+ /* -------------------------------------------------------------------------*/
309
+ /* CRUD operations (mocked db) ---------------------------------------------*/
310
+ /* -------------------------------------------------------------------------*/
311
+
312
+ describe('CRUD operations', () => {
313
+ const orderSelectClause =
314
+ '"order".id as "order#id", "order".email as "order#email", "order".browser_ip as "order#browser_ip", "order".browser_user_agent as "order#browser_user_agent", "order".kujo_imported_date as "order#kujo_imported_date", "order".created_date as "order#created_date", "order".cancel_reason as "order#cancel_reason", "order".cancelled_date as "order#cancelled_date", "order".closed_date as "order#closed_date", "order".processed_date as "order#processed_date", "order".updated_date as "order#updated_date", "order".note as "order#note", "order".subtotal_price as "order#subtotal_price", "order".taxes_included as "order#taxes_included", "order".total_discounts as "order#total_discounts", "order".total_price as "order#total_price", "order".total_tax as "order#total_tax", "order".total_weight as "order#total_weight", "order".order_status_url as "order#order_status_url", "order".utm_source_id as "order#utm_source_id", "order".utm_medium_id as "order#utm_medium_id", "order".utm_campaign as "order#utm_campaign", "order".utm_content as "order#utm_content", "order".utm_term as "order#utm_term"';
315
+
316
+ const makeRow = (id: number, email: string | null) => ({
317
+ 'order#id': id,
318
+ 'order#email': email,
319
+ 'order#browser_ip': null,
320
+ 'order#browser_user_agent': null,
321
+ 'order#kujo_imported_date': null,
322
+ 'order#created_date': null,
323
+ 'order#cancel_reason': null,
324
+ 'order#cancelled_date': null,
325
+ 'order#closed_date': null,
326
+ 'order#processed_date': null,
327
+ 'order#updated_date': null,
328
+ 'order#note': null,
329
+ 'order#subtotal_price': null,
330
+ 'order#taxes_included': null,
331
+ 'order#total_discounts': null,
332
+ 'order#total_price': null,
333
+ 'order#total_tax': null,
334
+ 'order#total_weight': null,
335
+ 'order#order_status_url': null,
336
+ 'order#utm_source_id': null,
337
+ 'order#utm_medium_id': null,
338
+ 'order#utm_campaign': null,
339
+ 'order#utm_content': null,
340
+ 'order#utm_term': null
341
+ });
342
+
343
+ describe('create (INSERT)', () => {
344
+ test('builds INSERT query and returns created model', async () => {
345
+ const db = mockPgpDb({
346
+ many: jest.fn().mockResolvedValue([makeRow(1, 'new@test.com')])
347
+ });
348
+ const orm = create({ entities: orderEntities, db });
349
+ const order = new Order({ id: 1, email: 'new@test.com' });
350
+ const result = await orm.create(order);
351
+ expect(db.many).toHaveBeenCalledTimes(1);
352
+ const [query, values] = db.many.mock.calls[0];
353
+ expect(query).toContain('INSERT INTO "order"');
354
+ expect(query).toContain('"id", "email"');
355
+ expect(query).toContain('$1,$2');
356
+ expect(query).toContain('RETURNING');
357
+ expect(values).toEqual([1, 'new@test.com']);
358
+ expect(result.id).toEqual(1);
359
+ expect(result.email).toEqual('new@test.com');
360
+ });
361
+
362
+ test('only includes defined columns in INSERT', async () => {
363
+ const db = mockPgpDb({
364
+ many: jest.fn().mockResolvedValue([makeRow(5, null)])
365
+ });
366
+ const orm = create({ entities: orderEntities, db });
367
+ const order = new Order({ id: 5 });
368
+ await orm.create(order);
369
+ const [query, values] = db.many.mock.calls[0];
370
+ expect(query).toContain('"id"');
371
+ expect(query).not.toContain('"email"');
372
+ expect(values).toEqual([5]);
373
+ });
374
+ });
375
+
376
+ describe('update (UPDATE)', () => {
377
+ test('builds UPDATE query and returns updated model', async () => {
378
+ const db = mockPgpDb({
379
+ many: jest.fn().mockResolvedValue([makeRow(1, 'updated@test.com')])
380
+ });
381
+ const orm = create({ entities: orderEntities, db });
382
+ const order = new Order({ id: 1, email: 'updated@test.com' });
383
+ const result = await orm.update(order);
384
+ expect(db.many).toHaveBeenCalledTimes(1);
385
+ const [query, values] = db.many.mock.calls[0];
386
+ expect(query).toContain('UPDATE "order"');
387
+ expect(query).toContain('SET');
388
+ expect(query).toContain('WHERE');
389
+ expect(query).toContain('RETURNING');
390
+ expect(values).toContain(1);
391
+ expect(values).toContain('updated@test.com');
392
+ expect(result.email).toEqual('updated@test.com');
393
+ });
394
+
395
+ test('uses custom "on" column for WHERE clause', async () => {
396
+ const db = mockPgpDb({
397
+ many: jest.fn().mockResolvedValue([makeRow(1, 'x@y.com')])
398
+ });
399
+ const orm = create({ entities: orderEntities, db });
400
+ const order = new Order({ id: 1, email: 'x@y.com' });
401
+ await orm.update(order, { on: 'email' });
402
+ const [query, values] = db.many.mock.calls[0];
403
+ expect(query).toContain('WHERE');
404
+ expect(query).toContain('"order".email');
405
+ expect(values[values.length - 1]).toEqual('x@y.com');
406
+ });
407
+ });
408
+
409
+ describe('delete (DELETE)', () => {
410
+ test('builds DELETE query with id', async () => {
411
+ const db = mockPgpDb({
412
+ none: jest.fn().mockResolvedValue(null)
413
+ });
414
+ const orm = create({ entities: orderEntities, db });
415
+ const order = new Order({ id: 42 });
416
+ await orm.delete(order);
417
+ expect(db.none).toHaveBeenCalledTimes(1);
418
+ const [query, values] = db.none.mock.calls[0];
419
+ expect(query).toContain('DELETE FROM "order"');
420
+ expect(query).toContain('"order".id = $(id)');
421
+ expect(values).toEqual({ id: 42 });
422
+ });
423
+ });
424
+
425
+ describe('deleteMatching', () => {
426
+ test('builds DELETE query with WHERE clause from model properties', async () => {
427
+ const db = mockPgpDb({
428
+ none: jest.fn().mockResolvedValue(null)
429
+ });
430
+ const orm = create({ entities: orderEntities, db });
431
+ const order = new Order({ id: 10, email: 'del@test.com' });
432
+ await orm.deleteMatching(order);
433
+ expect(db.none).toHaveBeenCalledTimes(1);
434
+ const [query, values] = db.none.mock.calls[0];
435
+ expect(query).toContain('DELETE FROM "order"');
436
+ expect(query).toContain('WHERE');
437
+ expect(query).toContain('"order"."id" = $1');
438
+ expect(query).toContain('"order"."email" = $2');
439
+ expect(values).toEqual([10, 'del@test.com']);
440
+ });
441
+ });
442
+
443
+ describe('getMatching', () => {
444
+ test('builds SELECT query with WHERE clause and returns one model', async () => {
445
+ const db = mockPgpDb({
446
+ many: jest.fn().mockResolvedValue([makeRow(1, 'found@test.com')])
447
+ });
448
+ const orm = create({ entities: orderEntities, db });
449
+ const order = new Order({ id: 1 });
450
+ const result = await orm.getMatching(order);
451
+ expect(db.many).toHaveBeenCalledTimes(1);
452
+ const [query, values] = db.many.mock.calls[0];
453
+ expect(query).toContain('SELECT');
454
+ expect(query).toContain('FROM "order"');
455
+ expect(query).toContain('WHERE');
456
+ expect(query).toContain('"order"."id" = $1');
457
+ expect(values).toEqual([1]);
458
+ expect(result.id).toEqual(1);
459
+ expect(result.email).toEqual('found@test.com');
460
+ });
461
+ });
462
+
463
+ describe('getOneOrNoneMatching', () => {
464
+ test('returns a model when one exists', async () => {
465
+ const db = mockPgpDb({
466
+ any: jest.fn().mockResolvedValue([makeRow(1, 'one@test.com')])
467
+ });
468
+ const orm = create({ entities: orderEntities, db });
469
+ const order = new Order({ id: 1 });
470
+ const result = await orm.getOneOrNoneMatching(order);
471
+ expect(db.any).toHaveBeenCalledTimes(1);
472
+ expect(result).toBeDefined();
473
+ expect(result!.id).toEqual(1);
474
+ });
475
+
476
+ test('returns undefined when none exist', async () => {
477
+ const db = mockPgpDb({
478
+ any: jest.fn().mockResolvedValue([])
479
+ });
480
+ const orm = create({ entities: orderEntities, db });
481
+ const order = new Order({ id: 999 });
482
+ const result = await orm.getOneOrNoneMatching(order);
483
+ expect(result).toBeUndefined();
484
+ });
485
+ });
486
+
487
+ describe('getAnyMatching', () => {
488
+ test('returns collection when results exist', async () => {
489
+ const db = mockPgpDb({
490
+ result: jest.fn().mockResolvedValue({
491
+ rows: [makeRow(1, 'a@test.com'), makeRow(2, 'b@test.com')],
492
+ fields: [{ name: 'order#id' }]
493
+ })
494
+ });
495
+ const orm = create({ entities: orderEntities, db });
496
+ const order = new Order({ id: 1 });
497
+ const result: any = await orm.getAnyMatching(order);
498
+ expect(db.result).toHaveBeenCalledTimes(1);
499
+ expect(result.models.length).toEqual(2);
500
+ });
501
+
502
+ test('returns empty collection when no results', async () => {
503
+ const db = mockPgpDb({
504
+ result: jest.fn().mockResolvedValue({
505
+ rows: [],
506
+ fields: [{ name: 'order#id' }]
507
+ })
508
+ });
509
+ const orm = create({ entities: orderEntities, db });
510
+ const order = new Order({ id: 999 });
511
+ const result: any = await orm.getAnyMatching(order);
512
+ expect(result.models.length).toEqual(0);
513
+ });
514
+ });
515
+
516
+ describe('getAllMatching', () => {
517
+ test('returns collection when results exist', async () => {
518
+ const db = mockPgpDb({
519
+ any: jest.fn().mockResolvedValue([makeRow(1, 'a@test.com')])
520
+ });
521
+ const orm = create({ entities: orderEntities, db });
522
+ const order = new Order({ id: 1 });
523
+ const result: any = await orm.getAllMatching(order);
524
+ expect(db.any).toHaveBeenCalledTimes(1);
525
+ expect(result.models.length).toEqual(1);
526
+ });
527
+
528
+ test('throws when no results exist', async () => {
529
+ const db = mockPgpDb({
530
+ any: jest.fn().mockResolvedValue([])
531
+ });
532
+ const orm = create({ entities: orderEntities, db });
533
+ const order = new Order({ id: 999 });
534
+ await expect(orm.getAllMatching(order)).rejects.toThrow();
535
+ });
536
+ });
98
537
  });
99
538
 
100
- test('getSqlColumnForPropertyName', () => {
101
- const orm = create({
102
- entities: orderEntities,
103
- db: { $config: { pgp: true } }
104
- });
105
- const order = new orderEntities[0].Model({ id: 1 });
106
- expect(orm.getSqlColumnForPropertyName(order, 'id')).toEqual('id');
107
- expect(orm.getSqlColumnForPropertyName(order, 'utmSourceId')).toEqual(
108
- 'utm_source_id'
109
- );
110
- expect(orm.getSqlColumnForPropertyName(order, 'browserIP')).toEqual(
111
- 'browser_ip'
112
- );
539
+ /* -------------------------------------------------------------------------*/
540
+ /* ORM exposes core methods ------------------------------------------------*/
541
+ /* -------------------------------------------------------------------------*/
542
+
543
+ describe('ORM exposes core methods', () => {
544
+ test('exposes createFromDatabase', () => {
545
+ const orm = create({
546
+ entities: orderEntities,
547
+ db: mockPgpDb()
548
+ });
549
+ expect(typeof orm.createFromDatabase).toBe('function');
550
+ });
551
+
552
+ test('exposes createOneFromDatabase', () => {
553
+ const orm = create({
554
+ entities: orderEntities,
555
+ db: mockPgpDb()
556
+ });
557
+ expect(typeof orm.createOneFromDatabase).toBe('function');
558
+ });
559
+
560
+ test('exposes createOneOrNoneFromDatabase', () => {
561
+ const orm = create({
562
+ entities: orderEntities,
563
+ db: mockPgpDb()
564
+ });
565
+ expect(typeof orm.createOneOrNoneFromDatabase).toBe('function');
566
+ });
567
+
568
+ test('exposes createManyFromDatabase', () => {
569
+ const orm = create({
570
+ entities: orderEntities,
571
+ db: mockPgpDb()
572
+ });
573
+ expect(typeof orm.createManyFromDatabase).toBe('function');
574
+ });
575
+
576
+ test('exposes createAnyFromDatabase', () => {
577
+ const orm = create({
578
+ entities: orderEntities,
579
+ db: mockPgpDb()
580
+ });
581
+ expect(typeof orm.createAnyFromDatabase).toBe('function');
582
+ });
583
+
584
+ test('exposes tables', () => {
585
+ const orm = create({
586
+ entities: orderEntities,
587
+ db: mockPgpDb()
588
+ });
589
+ expect(orm.tables).toBeDefined();
590
+ expect(Object.keys(orm.tables).length).toEqual(5);
591
+ });
592
+
593
+ test('exposes getEntityByModel', () => {
594
+ const orm = create({
595
+ entities: orderEntities,
596
+ db: mockPgpDb()
597
+ });
598
+ expect(typeof orm.getEntityByModel).toBe('function');
599
+ const order = new Order({ id: 1 });
600
+ expect(orm.getEntityByModel(order).tableName).toEqual('order');
601
+ });
602
+
603
+ test('exposes getEntityByTableName', () => {
604
+ const orm = create({
605
+ entities: orderEntities,
606
+ db: mockPgpDb()
607
+ });
608
+ expect(typeof orm.getEntityByTableName).toBe('function');
609
+ expect(orm.getEntityByTableName('order').tableName).toEqual('order');
610
+ });
611
+
612
+ test('exposes db reference', () => {
613
+ const db = mockPgpDb();
614
+ const orm = create({ entities: orderEntities, db });
615
+ expect(orm.db).toBe(db);
616
+ });
113
617
  });