@expo/entity 0.55.0 → 0.57.0

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 (163) hide show
  1. package/build/src/AuthorizationResultBasedEntityAssociationLoader.d.ts +1 -1
  2. package/build/src/AuthorizationResultBasedEntityAssociationLoader.js.map +1 -1
  3. package/build/src/AuthorizationResultBasedEntityLoader.d.ts +18 -24
  4. package/build/src/AuthorizationResultBasedEntityLoader.js +37 -56
  5. package/build/src/AuthorizationResultBasedEntityLoader.js.map +1 -1
  6. package/build/src/AuthorizationResultBasedEntityMutator.js +26 -19
  7. package/build/src/AuthorizationResultBasedEntityMutator.js.map +1 -1
  8. package/build/src/EnforcingEntityCreator.d.ts +1 -1
  9. package/build/src/EnforcingEntityCreator.js +1 -1
  10. package/build/src/EnforcingEntityLoader.d.ts +1 -58
  11. package/build/src/EnforcingEntityLoader.js +0 -65
  12. package/build/src/EnforcingEntityLoader.js.map +1 -1
  13. package/build/src/Entity.d.ts +6 -0
  14. package/build/src/Entity.js +6 -0
  15. package/build/src/Entity.js.map +1 -1
  16. package/build/src/EntityCompanion.d.ts +2 -2
  17. package/build/src/EntityCompanion.js.map +1 -1
  18. package/build/src/EntityCompanionProvider.d.ts +1 -1
  19. package/build/src/EntityCompanionProvider.js +4 -4
  20. package/build/src/EntityConfiguration.d.ts +1 -1
  21. package/build/src/EntityConfiguration.js +1 -2
  22. package/build/src/EntityConfiguration.js.map +1 -1
  23. package/build/src/{EntityLoaderUtils.d.ts → EntityConstructionUtils.d.ts} +15 -29
  24. package/build/src/EntityConstructionUtils.js +118 -0
  25. package/build/src/EntityConstructionUtils.js.map +1 -0
  26. package/build/src/EntityDatabaseAdapter.d.ts +10 -108
  27. package/build/src/EntityDatabaseAdapter.js +14 -76
  28. package/build/src/EntityDatabaseAdapter.js.map +1 -1
  29. package/build/src/EntityFieldDefinition.d.ts +1 -1
  30. package/build/src/EntityInvalidationUtils.d.ts +41 -0
  31. package/build/src/EntityInvalidationUtils.js +71 -0
  32. package/build/src/EntityInvalidationUtils.js.map +1 -0
  33. package/build/src/EntityLoader.d.ts +0 -6
  34. package/build/src/EntityLoader.js +0 -7
  35. package/build/src/EntityLoader.js.map +1 -1
  36. package/build/src/EntityLoaderFactory.d.ts +4 -0
  37. package/build/src/EntityLoaderFactory.js +10 -3
  38. package/build/src/EntityLoaderFactory.js.map +1 -1
  39. package/build/src/EntityPrivacyPolicy.d.ts +27 -0
  40. package/build/src/EntityPrivacyPolicy.js +22 -1
  41. package/build/src/EntityPrivacyPolicy.js.map +1 -1
  42. package/build/src/EntitySecondaryCacheLoader.d.ts +14 -3
  43. package/build/src/EntitySecondaryCacheLoader.js +21 -4
  44. package/build/src/EntitySecondaryCacheLoader.js.map +1 -1
  45. package/build/src/ReadonlyEntity.d.ts +4 -5
  46. package/build/src/ReadonlyEntity.js +7 -8
  47. package/build/src/ReadonlyEntity.js.map +1 -1
  48. package/build/src/ViewerContext.d.ts +6 -6
  49. package/build/src/ViewerContext.js +8 -8
  50. package/build/src/ViewerScopedEntityCompanion.d.ts +1 -1
  51. package/build/src/ViewerScopedEntityCompanion.js.map +1 -1
  52. package/build/src/ViewerScopedEntityLoaderFactory.d.ts +4 -0
  53. package/build/src/ViewerScopedEntityLoaderFactory.js +6 -0
  54. package/build/src/ViewerScopedEntityLoaderFactory.js.map +1 -1
  55. package/build/src/errors/EntityDatabaseAdapterError.d.ts +4 -0
  56. package/build/src/errors/EntityDatabaseAdapterError.js +13 -1
  57. package/build/src/errors/EntityDatabaseAdapterError.js.map +1 -1
  58. package/build/src/errors/EntityError.d.ts +2 -1
  59. package/build/src/errors/EntityError.js +1 -0
  60. package/build/src/errors/EntityError.js.map +1 -1
  61. package/build/src/index.d.ts +2 -1
  62. package/build/src/index.js +2 -1
  63. package/build/src/index.js.map +1 -1
  64. package/build/src/internal/EntityDataManager.d.ts +8 -16
  65. package/build/src/internal/EntityDataManager.js +8 -18
  66. package/build/src/internal/EntityDataManager.js.map +1 -1
  67. package/build/src/internal/EntityFieldTransformationUtils.js.map +1 -1
  68. package/build/src/internal/EntityLoadInterfaces.d.ts +2 -0
  69. package/build/src/internal/EntityLoadInterfaces.js +2 -0
  70. package/build/src/internal/EntityLoadInterfaces.js.map +1 -1
  71. package/build/src/internal/EntityTableDataCoordinator.d.ts +2 -0
  72. package/build/src/internal/EntityTableDataCoordinator.js +4 -0
  73. package/build/src/internal/EntityTableDataCoordinator.js.map +1 -1
  74. package/build/src/metrics/EntityMetricsUtils.d.ts +1 -0
  75. package/build/src/metrics/EntityMetricsUtils.js +15 -1
  76. package/build/src/metrics/EntityMetricsUtils.js.map +1 -1
  77. package/build/src/metrics/IEntityMetricsAdapter.d.ts +4 -1
  78. package/build/src/metrics/IEntityMetricsAdapter.js +3 -0
  79. package/build/src/metrics/IEntityMetricsAdapter.js.map +1 -1
  80. package/build/src/rules/AllowIfAllSubRulesAllowPrivacyPolicyRule.d.ts +2 -2
  81. package/build/src/rules/AllowIfAnySubRuleAllowsPrivacyPolicyRule.d.ts +2 -2
  82. package/build/src/rules/EvaluateIfEntityFieldPredicatePrivacyPolicyRule.d.ts +2 -2
  83. package/build/src/rules/PrivacyPolicyRule.d.ts +2 -2
  84. package/build/src/utils/EntityPrivacyUtils.js +11 -20
  85. package/build/src/utils/EntityPrivacyUtils.js.map +1 -1
  86. package/build/src/utils/collections/maps.d.ts +2 -2
  87. package/build/src/utils/collections/maps.js +2 -2
  88. package/package.json +4 -4
  89. package/src/AuthorizationResultBasedEntityAssociationLoader.ts +4 -7
  90. package/src/AuthorizationResultBasedEntityLoader.ts +58 -88
  91. package/src/AuthorizationResultBasedEntityMutator.ts +35 -20
  92. package/src/EnforcingEntityCreator.ts +1 -1
  93. package/src/EnforcingEntityLoader.ts +1 -95
  94. package/src/Entity.ts +6 -0
  95. package/src/EntityCompanion.ts +2 -2
  96. package/src/EntityCompanionProvider.ts +4 -4
  97. package/src/EntityConfiguration.ts +8 -5
  98. package/src/EntityConstructionUtils.ts +168 -0
  99. package/src/EntityDatabaseAdapter.ts +32 -222
  100. package/src/EntityFieldDefinition.ts +1 -1
  101. package/src/{EntityLoaderUtils.ts → EntityInvalidationUtils.ts} +5 -96
  102. package/src/EntityLoader.ts +0 -16
  103. package/src/EntityLoaderFactory.ts +50 -10
  104. package/src/EntityPrivacyPolicy.ts +44 -1
  105. package/src/EntitySecondaryCacheLoader.ts +54 -3
  106. package/src/ReadonlyEntity.ts +9 -11
  107. package/src/ViewerContext.ts +10 -10
  108. package/src/ViewerScopedEntityCompanion.ts +1 -1
  109. package/src/ViewerScopedEntityLoaderFactory.ts +37 -0
  110. package/src/__tests__/AuthorizationResultBasedEntityLoader-constructor-test.ts +3 -5
  111. package/src/__tests__/AuthorizationResultBasedEntityLoader-test.ts +34 -419
  112. package/src/__tests__/ComposedCacheAdapter-test.ts +3 -3
  113. package/src/__tests__/EnforcingEntityLoader-test.ts +2 -134
  114. package/src/__tests__/EntityCompanion-test.ts +18 -0
  115. package/src/__tests__/EntityConfiguration-test.ts +4 -4
  116. package/src/__tests__/EntityDatabaseAdapter-test.ts +33 -68
  117. package/src/__tests__/EntityEdges-test.ts +10 -10
  118. package/src/__tests__/EntityLoader-test.ts +6 -4
  119. package/src/__tests__/EntityMutator-test.ts +27 -15
  120. package/src/__tests__/EntityPrivacyPolicy-test.ts +102 -0
  121. package/src/__tests__/EntityQueryContext-test.ts +11 -11
  122. package/src/__tests__/EntitySecondaryCacheLoader-test.ts +10 -5
  123. package/src/__tests__/EntitySelfReferentialEdges-test.ts +6 -6
  124. package/src/__tests__/GenericEntityCacheAdapter-test.ts +18 -15
  125. package/src/__tests__/GenericSecondaryEntityCache-test.ts +27 -5
  126. package/src/__tests__/ReadonlyEntity-test.ts +6 -4
  127. package/src/__tests__/ViewerContext-test.ts +4 -4
  128. package/src/__tests__/ViewerScopedEntityCompanion-test.ts +1 -0
  129. package/src/__tests__/cases/TwoEntitySameTableDisjointRows-test.ts +0 -17
  130. package/src/errors/EntityDatabaseAdapterError.ts +14 -0
  131. package/src/errors/EntityError.ts +1 -0
  132. package/src/errors/__tests__/EntityDatabaseAdapterError-test.ts +9 -0
  133. package/src/errors/__tests__/EntityError-test.ts +13 -5
  134. package/src/index.ts +2 -1
  135. package/src/internal/EntityDataManager.ts +19 -54
  136. package/src/internal/EntityFieldTransformationUtils.ts +5 -5
  137. package/src/internal/EntityLoadInterfaces.ts +2 -0
  138. package/src/internal/EntityTableDataCoordinator.ts +2 -2
  139. package/src/internal/__tests__/CompositeFieldHolder-test.ts +8 -2
  140. package/src/internal/__tests__/EntityDataManager-test.ts +71 -202
  141. package/src/internal/__tests__/ReadThroughEntityCache-test.ts +39 -24
  142. package/src/metrics/EntityMetricsUtils.ts +23 -0
  143. package/src/metrics/IEntityMetricsAdapter.ts +3 -0
  144. package/src/metrics/__tests__/EntityMetricsUtils-test.ts +120 -0
  145. package/src/rules/AllowIfAllSubRulesAllowPrivacyPolicyRule.ts +2 -2
  146. package/src/rules/AllowIfAnySubRuleAllowsPrivacyPolicyRule.ts +2 -2
  147. package/src/rules/EvaluateIfEntityFieldPredicatePrivacyPolicyRule.ts +2 -2
  148. package/src/rules/PrivacyPolicyRule.ts +2 -2
  149. package/src/rules/__tests__/AllowIfAllSubRulesAllowPrivacyPolicyRule-test.ts +4 -4
  150. package/src/rules/__tests__/AllowIfAnySubRuleAllowsPrivacyPolicyRule-test.ts +4 -4
  151. package/src/rules/__tests__/AllowIfInParentCascadeDeletionPrivacyPolicyRule-test.ts +11 -1
  152. package/src/rules/__tests__/AlwaysAllowPrivacyPolicyRule-test.ts +2 -2
  153. package/src/rules/__tests__/AlwaysDenyPrivacyPolicyRule-test.ts +2 -2
  154. package/src/rules/__tests__/AlwaysSkipPrivacyPolicyRule-test.ts +2 -2
  155. package/src/rules/__tests__/EvaluateIfEntityFieldPredicatePrivacyPolicyRule-test.ts +3 -3
  156. package/src/utils/EntityPrivacyUtils.ts +18 -29
  157. package/src/utils/__testfixtures__/PrivacyPolicyRuleTestUtils.ts +2 -2
  158. package/src/utils/__testfixtures__/StubDatabaseAdapter.ts +13 -101
  159. package/src/utils/__tests__/EntityCreationUtils-test.ts +6 -6
  160. package/src/utils/__tests__/EntityPrivacyUtils-test.ts +2 -2
  161. package/src/utils/collections/maps.ts +2 -2
  162. package/build/src/EntityLoaderUtils.js +0 -147
  163. package/build/src/EntityLoaderUtils.js.map +0 -1
@@ -11,6 +11,7 @@ import {
11
11
  EntityPrivacyPolicyEvaluationContext,
12
12
  EntityPrivacyPolicyEvaluationMode,
13
13
  EntityPrivacyPolicyEvaluator,
14
+ EntityPrivacyPolicyRuleEvaluationContext,
14
15
  } from '../EntityPrivacyPolicy';
15
16
  import { EntityQueryContext } from '../EntityQueryContext';
16
17
  import { ViewerContext } from '../ViewerContext';
@@ -273,6 +274,47 @@ class EmptyPolicy extends EntityPrivacyPolicy<BlahFields, 'id', ViewerContext, B
273
274
  protected override readonly deleteRules = [];
274
275
  }
275
276
 
277
+ class ContextCapturingRule extends PrivacyPolicyRule<BlahFields, 'id', ViewerContext, BlahEntity> {
278
+ public capturedContext: EntityPrivacyPolicyRuleEvaluationContext<
279
+ BlahFields,
280
+ 'id',
281
+ ViewerContext,
282
+ BlahEntity
283
+ > | null = null;
284
+
285
+ async evaluateAsync(
286
+ _viewerContext: ViewerContext,
287
+ _queryContext: EntityQueryContext,
288
+ evaluationContext: EntityPrivacyPolicyRuleEvaluationContext<
289
+ BlahFields,
290
+ 'id',
291
+ ViewerContext,
292
+ BlahEntity
293
+ >,
294
+ _entity: BlahEntity,
295
+ ): Promise<RuleEvaluationResult> {
296
+ this.capturedContext = evaluationContext;
297
+ return RuleEvaluationResult.ALLOW;
298
+ }
299
+ }
300
+
301
+ class ContextCapturingPolicy extends EntityPrivacyPolicy<
302
+ BlahFields,
303
+ 'id',
304
+ ViewerContext,
305
+ BlahEntity
306
+ > {
307
+ public readonly createRule = new ContextCapturingRule();
308
+ public readonly readRule = new ContextCapturingRule();
309
+ public readonly updateRule = new ContextCapturingRule();
310
+ public readonly deleteRule = new ContextCapturingRule();
311
+
312
+ protected override readonly createRules = [this.createRule];
313
+ protected override readonly readRules = [this.readRule];
314
+ protected override readonly updateRules = [this.updateRule];
315
+ protected override readonly deleteRules = [this.deleteRule];
316
+ }
317
+
276
318
  describe(EntityPrivacyPolicy, () => {
277
319
  describe(EntityPrivacyPolicyEvaluationMode.ENFORCE.toString(), () => {
278
320
  it('throws EntityNotAuthorizedError when deny', async () => {
@@ -718,4 +760,64 @@ describe(EntityPrivacyPolicy, () => {
718
760
  verify(metricsAdapterMock.logAuthorizationEvent(anything())).never();
719
761
  });
720
762
  });
763
+
764
+ describe('EntityPrivacyPolicyRuleEvaluationContext', () => {
765
+ it('passes correct authorization action to rules for each CRUD operation', async () => {
766
+ const viewerContext = instance(mock(ViewerContext));
767
+ const queryContext = instance(mock(EntityQueryContext));
768
+ const privacyPolicyEvaluationContext: EntityPrivacyPolicyEvaluationContext<
769
+ BlahFields,
770
+ 'id',
771
+ ViewerContext,
772
+ BlahEntity
773
+ > = {
774
+ previousValue: null,
775
+ cascadingDeleteCause: null,
776
+ };
777
+ const metricsAdapter = instance(mock<IEntityMetricsAdapter>());
778
+ const entity = new BlahEntity({
779
+ viewerContext,
780
+ id: '1',
781
+ databaseFields: { id: '1' },
782
+ selectedFields: { id: '1' },
783
+ });
784
+ const policy = new ContextCapturingPolicy();
785
+
786
+ await policy.authorizeCreateAsync(
787
+ viewerContext,
788
+ queryContext,
789
+ privacyPolicyEvaluationContext,
790
+ entity,
791
+ metricsAdapter,
792
+ );
793
+ expect(policy.createRule.capturedContext?.action).toBe(EntityAuthorizationAction.CREATE);
794
+
795
+ await policy.authorizeReadAsync(
796
+ viewerContext,
797
+ queryContext,
798
+ privacyPolicyEvaluationContext,
799
+ entity,
800
+ metricsAdapter,
801
+ );
802
+ expect(policy.readRule.capturedContext?.action).toBe(EntityAuthorizationAction.READ);
803
+
804
+ await policy.authorizeUpdateAsync(
805
+ viewerContext,
806
+ queryContext,
807
+ privacyPolicyEvaluationContext,
808
+ entity,
809
+ metricsAdapter,
810
+ );
811
+ expect(policy.updateRule.capturedContext?.action).toBe(EntityAuthorizationAction.UPDATE);
812
+
813
+ await policy.authorizeDeleteAsync(
814
+ viewerContext,
815
+ queryContext,
816
+ privacyPolicyEvaluationContext,
817
+ entity,
818
+ metricsAdapter,
819
+ );
820
+ expect(policy.deleteRule.capturedContext?.action).toBe(EntityAuthorizationAction.DELETE);
821
+ });
822
+ });
721
823
  });
@@ -49,7 +49,7 @@ describe(EntityQueryContext, () => {
49
49
  );
50
50
  });
51
51
 
52
- await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
52
+ await viewerContext.runInTransactionForDatabaseAdapterFlavorAsync(
53
53
  'postgres',
54
54
  async (queryContext) => {
55
55
  queryContext.appendPostCommitCallback(postCommitCallback);
@@ -76,7 +76,7 @@ describe(EntityQueryContext, () => {
76
76
  const postCommitCallback = jest.fn(async (): Promise<void> => {});
77
77
 
78
78
  await expect(
79
- viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
79
+ viewerContext.runInTransactionForDatabaseAdapterFlavorAsync(
80
80
  'postgres',
81
81
  async (queryContext) => {
82
82
  queryContext.appendPostCommitCallback(postCommitCallback);
@@ -105,7 +105,7 @@ describe(EntityQueryContext, () => {
105
105
  const postCommitCallback = jest.fn(async (): Promise<void> => {});
106
106
  const postCommitNestedCallback = jest.fn(async (): Promise<void> => {});
107
107
 
108
- await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
108
+ await viewerContext.runInTransactionForDatabaseAdapterFlavorAsync(
109
109
  'postgres',
110
110
  async (queryContext) => {
111
111
  queryContext.appendPostCommitCallback(postCommitCallback);
@@ -152,13 +152,13 @@ describe(EntityQueryContext, () => {
152
152
  const viewerContext = new ViewerContext(companionProvider);
153
153
 
154
154
  const queryContextProvider =
155
- companionProvider.getQueryContextProviderForDatabaseAdaptorFlavor('postgres');
155
+ companionProvider.getQueryContextProviderForDatabaseAdapterFlavor('postgres');
156
156
  const queryContextProviderSpy = jest.spyOn(queryContextProvider, 'runInTransactionAsync');
157
157
 
158
158
  const transactionScopeFn = async (): Promise<any> => {};
159
159
  const transactionConfig = { isolationLevel: TransactionIsolationLevel.SERIALIZABLE };
160
160
 
161
- await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
161
+ await viewerContext.runInTransactionForDatabaseAdapterFlavorAsync(
162
162
  'postgres',
163
163
  transactionScopeFn,
164
164
  transactionConfig,
@@ -171,7 +171,7 @@ describe(EntityQueryContext, () => {
171
171
  const companionProvider = createUnitTestEntityCompanionProvider();
172
172
  const viewerContext = new ViewerContext(companionProvider);
173
173
 
174
- await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
174
+ await viewerContext.runInTransactionForDatabaseAdapterFlavorAsync(
175
175
  'postgres',
176
176
  async (queryContext) => {
177
177
  assert(queryContext.isInTransaction());
@@ -182,7 +182,7 @@ describe(EntityQueryContext, () => {
182
182
  { transactionalDataLoaderMode: TransactionalDataLoaderMode.DISABLED },
183
183
  );
184
184
 
185
- await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
185
+ await viewerContext.runInTransactionForDatabaseAdapterFlavorAsync(
186
186
  'postgres',
187
187
  async (queryContext) => {
188
188
  assert(queryContext.isInTransaction());
@@ -193,7 +193,7 @@ describe(EntityQueryContext, () => {
193
193
  { transactionalDataLoaderMode: TransactionalDataLoaderMode.ENABLED_BATCH_ONLY },
194
194
  );
195
195
 
196
- await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
196
+ await viewerContext.runInTransactionForDatabaseAdapterFlavorAsync(
197
197
  'postgres',
198
198
  async (queryContext) => {
199
199
  assert(queryContext.isInTransaction());
@@ -252,7 +252,7 @@ describe(EntityQueryContext, () => {
252
252
  );
253
253
  const viewerContext = new ViewerContext(companionProvider);
254
254
 
255
- await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
255
+ await viewerContext.runInTransactionForDatabaseAdapterFlavorAsync(
256
256
  'postgres',
257
257
  async (queryContext) => {
258
258
  assert(queryContext.isInTransaction());
@@ -263,7 +263,7 @@ describe(EntityQueryContext, () => {
263
263
  { transactionalDataLoaderMode: TransactionalDataLoaderMode.DISABLED },
264
264
  );
265
265
 
266
- await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
266
+ await viewerContext.runInTransactionForDatabaseAdapterFlavorAsync(
267
267
  'postgres',
268
268
  async (queryContext) => {
269
269
  assert(queryContext.isInTransaction());
@@ -274,7 +274,7 @@ describe(EntityQueryContext, () => {
274
274
  { transactionalDataLoaderMode: TransactionalDataLoaderMode.ENABLED_BATCH_ONLY },
275
275
  );
276
276
 
277
- await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
277
+ await viewerContext.runInTransactionForDatabaseAdapterFlavorAsync(
278
278
  'postgres',
279
279
  async (queryContext) => {
280
280
  assert(queryContext.isInTransaction());
@@ -46,7 +46,7 @@ describe(EntitySecondaryCacheLoader, () => {
46
46
 
47
47
  const secondaryCacheLoader = new TestSecondaryRedisCacheLoader(
48
48
  secondaryEntityCache,
49
- SimpleTestEntity.loaderWithAuthorizationResults(vc1),
49
+ EntitySecondaryCacheLoader.getConstructionUtilsForEntityClass(SimpleTestEntity, vc1),
50
50
  );
51
51
 
52
52
  await secondaryCacheLoader.loadManyAsync([loadParams]);
@@ -70,8 +70,11 @@ describe(EntitySecondaryCacheLoader, () => {
70
70
  const secondaryEntityCache = instance(secondaryEntityCacheMock);
71
71
 
72
72
  const loader = SimpleTestEntity.loaderWithAuthorizationResults(vc1);
73
- const spiedPrivacyPolicy = spy(loader.utils['privacyPolicy']);
74
- const secondaryCacheLoader = new TestSecondaryRedisCacheLoader(secondaryEntityCache, loader);
73
+ const spiedPrivacyPolicy = spy(loader['constructionUtils']['privacyPolicy']);
74
+ const secondaryCacheLoader = new TestSecondaryRedisCacheLoader(
75
+ secondaryEntityCache,
76
+ EntitySecondaryCacheLoader.getConstructionUtilsForEntityClass(SimpleTestEntity, vc1),
77
+ );
75
78
 
76
79
  const result = await secondaryCacheLoader.loadManyAsync([loadParams]);
77
80
  expect(result.get(loadParams)?.enforceValue().getID()).toEqual(createdEntity.getID());
@@ -98,8 +101,10 @@ describe(EntitySecondaryCacheLoader, () => {
98
101
  const secondaryEntityCacheMock =
99
102
  mock<ISecondaryEntityCache<SimpleTestFields, TestLoadParams>>();
100
103
  const secondaryEntityCache = instance(secondaryEntityCacheMock);
101
- const loader = SimpleTestEntity.loaderWithAuthorizationResults(vc1);
102
- const secondaryCacheLoader = new TestSecondaryRedisCacheLoader(secondaryEntityCache, loader);
104
+ const secondaryCacheLoader = new TestSecondaryRedisCacheLoader(
105
+ secondaryEntityCache,
106
+ EntitySecondaryCacheLoader.getConstructionUtilsForEntityClass(SimpleTestEntity, vc1),
107
+ );
103
108
  await secondaryCacheLoader.invalidateManyAsync([loadParams]);
104
109
 
105
110
  verify(secondaryEntityCacheMock.invalidateManyAsync(deepEqual([loadParams]))).once();
@@ -250,7 +250,7 @@ describe('EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE', () => {
250
250
  'cacheAdapter'
251
251
  ] as InMemoryFullCacheStubCacheAdapter<CategoryFields, 'id'>;
252
252
  const subCategoryCachedBefore = await categoryCacheAdapter.loadManyAsync(
253
- new SingleFieldHolder('parent_category_id'),
253
+ new SingleFieldHolder<CategoryFields, 'id', 'parent_category_id'>('parent_category_id'),
254
254
  [new SingleFieldValueHolder(parentCategory.getID())],
255
255
  );
256
256
  expect(
@@ -258,7 +258,7 @@ describe('EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE', () => {
258
258
  ).toEqual(CacheStatus.HIT);
259
259
 
260
260
  const subSubCategoryCachedBefore = await categoryCacheAdapter.loadManyAsync(
261
- new SingleFieldHolder('parent_category_id'),
261
+ new SingleFieldHolder<CategoryFields, 'id', 'parent_category_id'>('parent_category_id'),
262
262
  [new SingleFieldValueHolder(subCategory.getID())],
263
263
  );
264
264
  expect(
@@ -268,7 +268,7 @@ describe('EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE', () => {
268
268
  await CategoryEntity.deleter(parentCategory).deleteAsync();
269
269
 
270
270
  const subCategoryCachedAfter = await categoryCacheAdapter.loadManyAsync(
271
- new SingleFieldHolder('parent_category_id'),
271
+ new SingleFieldHolder<CategoryFields, 'id', 'parent_category_id'>('parent_category_id'),
272
272
  [new SingleFieldValueHolder(parentCategory.getID())],
273
273
  );
274
274
  expect(
@@ -276,7 +276,7 @@ describe('EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE', () => {
276
276
  ).toEqual(CacheStatus.MISS);
277
277
 
278
278
  const subSubCategoryCachedAfter = await categoryCacheAdapter.loadManyAsync(
279
- new SingleFieldHolder('parent_category_id'),
279
+ new SingleFieldHolder<CategoryFields, 'id', 'parent_category_id'>('parent_category_id'),
280
280
  [new SingleFieldValueHolder(subCategory.getID())],
281
281
  );
282
282
  expect(
@@ -329,7 +329,7 @@ describe('EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE', () => {
329
329
  'cacheAdapter'
330
330
  ] as InMemoryFullCacheStubCacheAdapter<CategoryFields, 'id'>;
331
331
  const categoriesCachedBefore = await categoryCacheAdapter.loadManyAsync(
332
- new SingleFieldHolder('parent_category_id'),
332
+ new SingleFieldHolder<CategoryFields, 'id', 'parent_category_id'>('parent_category_id'),
333
333
  [
334
334
  new SingleFieldValueHolder(categoryA.getID()),
335
335
  new SingleFieldValueHolder(categoryB.getID()),
@@ -345,7 +345,7 @@ describe('EntityEdgeDeletionBehavior.CASCADE_DELETE_INVALIDATE_CACHE', () => {
345
345
  await CategoryEntity.deleter(categoryA).deleteAsync();
346
346
 
347
347
  const categoriesCachedAfter = await categoryCacheAdapter.loadManyAsync(
348
- new SingleFieldHolder('parent_category_id'),
348
+ new SingleFieldHolder<CategoryFields, 'id', 'parent_category_id'>('parent_category_id'),
349
349
  [
350
350
  new SingleFieldValueHolder(categoryA.getID()),
351
351
  new SingleFieldValueHolder(categoryB.getID()),
@@ -22,7 +22,7 @@ describe(GenericEntityCacheAdapter, () => {
22
22
  const mockGenericCacher = mock<IEntityGenericCacher<BlahFields, 'id'>>();
23
23
  when(
24
24
  mockGenericCacher.makeCacheKeyForStorage(
25
- deepEqualEntityAware(new SingleFieldHolder('id')),
25
+ deepEqualEntityAware(new SingleFieldHolder<BlahFields, 'id', 'id'>('id')),
26
26
  anything(),
27
27
  ),
28
28
  ).thenCall((fieldHolder, fieldValueHolder) => {
@@ -38,11 +38,14 @@ describe(GenericEntityCacheAdapter, () => {
38
38
 
39
39
  const cacheAdapter = new GenericEntityCacheAdapter(instance(mockGenericCacher));
40
40
 
41
- const results = await cacheAdapter.loadManyAsync(new SingleFieldHolder('id'), [
42
- new SingleFieldValueHolder('wat'),
43
- new SingleFieldValueHolder('who'),
44
- new SingleFieldValueHolder('why'),
45
- ]);
41
+ const results = await cacheAdapter.loadManyAsync(
42
+ new SingleFieldHolder<BlahFields, 'id', 'id'>('id'),
43
+ [
44
+ new SingleFieldValueHolder('wat'),
45
+ new SingleFieldValueHolder('who'),
46
+ new SingleFieldValueHolder('why'),
47
+ ],
48
+ );
46
49
  expect(results.get(new SingleFieldValueHolder('wat'))).toMatchObject({
47
50
  status: CacheStatus.HIT,
48
51
  item: { id: 'wat' },
@@ -74,7 +77,7 @@ describe(GenericEntityCacheAdapter, () => {
74
77
  const mockGenericCacher = mock<IEntityGenericCacher<BlahFields, 'id'>>();
75
78
  when(
76
79
  mockGenericCacher.makeCacheKeyForStorage(
77
- deepEqualEntityAware(new SingleFieldHolder('id')),
80
+ deepEqualEntityAware(new SingleFieldHolder<BlahFields, 'id', 'id'>('id')),
78
81
  anything(),
79
82
  ),
80
83
  ).thenCall((fieldHolder, fieldValueHolder) => {
@@ -85,7 +88,7 @@ describe(GenericEntityCacheAdapter, () => {
85
88
 
86
89
  const cacheAdapter = new GenericEntityCacheAdapter(instance(mockGenericCacher));
87
90
  await expect(
88
- cacheAdapter.loadManyAsync(new SingleFieldHolder('id'), [
91
+ cacheAdapter.loadManyAsync(new SingleFieldHolder<BlahFields, 'id', 'id'>('id'), [
89
92
  new SingleFieldValueHolder('wat'),
90
93
  ]),
91
94
  ).rejects.toThrow(expectedError);
@@ -99,7 +102,7 @@ describe(GenericEntityCacheAdapter, () => {
99
102
  const mockGenericCacher = mock<IEntityGenericCacher<BlahFields, 'id'>>();
100
103
  when(
101
104
  mockGenericCacher.makeCacheKeyForStorage(
102
- deepEqualEntityAware(new SingleFieldHolder('id')),
105
+ deepEqualEntityAware(new SingleFieldHolder<BlahFields, 'id', 'id'>('id')),
103
106
  anything(),
104
107
  ),
105
108
  ).thenCall((fieldHolder, fieldValueHolder) => {
@@ -108,7 +111,7 @@ describe(GenericEntityCacheAdapter, () => {
108
111
 
109
112
  const cacheAdapter = new GenericEntityCacheAdapter(instance(mockGenericCacher));
110
113
  await cacheAdapter.cacheManyAsync(
111
- new SingleFieldHolder('id'),
114
+ new SingleFieldHolder<BlahFields, 'id', 'id'>('id'),
112
115
  new Map([[new SingleFieldValueHolder('wat'), { id: 'wat' }]]),
113
116
  );
114
117
 
@@ -123,7 +126,7 @@ describe(GenericEntityCacheAdapter, () => {
123
126
  const mockGenericCacher = mock<IEntityGenericCacher<BlahFields, 'id'>>();
124
127
  when(
125
128
  mockGenericCacher.makeCacheKeyForStorage(
126
- deepEqualEntityAware(new SingleFieldHolder('id')),
129
+ deepEqualEntityAware(new SingleFieldHolder<BlahFields, 'id', 'id'>('id')),
127
130
  anything(),
128
131
  ),
129
132
  ).thenCall((fieldHolder, fieldValueHolder) => {
@@ -131,7 +134,7 @@ describe(GenericEntityCacheAdapter, () => {
131
134
  });
132
135
 
133
136
  const cacheAdapter = new GenericEntityCacheAdapter(instance(mockGenericCacher));
134
- await cacheAdapter.cacheDBMissesAsync(new SingleFieldHolder('id'), [
137
+ await cacheAdapter.cacheDBMissesAsync(new SingleFieldHolder<BlahFields, 'id', 'id'>('id'), [
135
138
  new SingleFieldValueHolder('wat'),
136
139
  ]);
137
140
 
@@ -144,7 +147,7 @@ describe(GenericEntityCacheAdapter, () => {
144
147
  const mockGenericCacher = mock<IEntityGenericCacher<BlahFields, 'id'>>();
145
148
  when(
146
149
  mockGenericCacher.makeCacheKeysForInvalidation(
147
- deepEqualEntityAware(new SingleFieldHolder('id')),
150
+ deepEqualEntityAware(new SingleFieldHolder<BlahFields, 'id', 'id'>('id')),
148
151
  anything(),
149
152
  ),
150
153
  ).thenCall((fieldHolder, fieldValueHolder) => {
@@ -152,7 +155,7 @@ describe(GenericEntityCacheAdapter, () => {
152
155
  });
153
156
 
154
157
  const cacheAdapter = new GenericEntityCacheAdapter(instance(mockGenericCacher));
155
- await cacheAdapter.invalidateManyAsync(new SingleFieldHolder('id'), [
158
+ await cacheAdapter.invalidateManyAsync(new SingleFieldHolder<BlahFields, 'id', 'id'>('id'), [
156
159
  new SingleFieldValueHolder('wat'),
157
160
  ]);
158
161
 
@@ -163,7 +166,7 @@ describe(GenericEntityCacheAdapter, () => {
163
166
  const mockGenericCacher = mock<IEntityGenericCacher<BlahFields, 'id'>>();
164
167
  when(
165
168
  mockGenericCacher.makeCacheKeysForInvalidation(
166
- deepEqualEntityAware(new SingleFieldHolder('id')),
169
+ deepEqualEntityAware(new SingleFieldHolder<BlahFields, 'id', 'id'>('id')),
167
170
  anything(),
168
171
  ),
169
172
  ).thenCall((fieldHolder, fieldValueHolder) => {
@@ -1,6 +1,8 @@
1
1
  import { describe, it, expect } from '@jest/globals';
2
2
  import nullthrows from 'nullthrows';
3
3
 
4
+ import { AuthorizationResultBasedEntityLoader } from '../AuthorizationResultBasedEntityLoader';
5
+ import { EntityConstructionUtils } from '../EntityConstructionUtils';
4
6
  import { EntitySecondaryCacheLoader } from '../EntitySecondaryCacheLoader';
5
7
  import { GenericSecondaryEntityCache } from '../GenericSecondaryEntityCache';
6
8
  import { IEntityGenericCacher } from '../IEntityGenericCacher';
@@ -100,6 +102,28 @@ class TestSecondaryCacheLoader extends EntitySecondaryCacheLoader<
100
102
  > {
101
103
  public databaseLoadCount = 0;
102
104
 
105
+ constructor(
106
+ secondaryEntityCache: TestSecondaryEntityCache<TestFields, 'customIdField', TestLoadParams>,
107
+ constructionUtils: EntityConstructionUtils<
108
+ TestFields,
109
+ 'customIdField',
110
+ ViewerContext,
111
+ TestEntity,
112
+ TestEntityPrivacyPolicy,
113
+ keyof TestFields
114
+ >,
115
+ private readonly entityLoader: AuthorizationResultBasedEntityLoader<
116
+ TestFields,
117
+ 'customIdField',
118
+ ViewerContext,
119
+ TestEntity,
120
+ TestEntityPrivacyPolicy,
121
+ keyof TestFields
122
+ >,
123
+ ) {
124
+ super(secondaryEntityCache, constructionUtils);
125
+ }
126
+
103
127
  protected override async fetchObjectsFromDatabaseAsync(
104
128
  loadParamsArray: readonly Readonly<TestLoadParams>[],
105
129
  ): Promise<ReadonlyMap<Readonly<Readonly<TestLoadParams>>, Readonly<TestFields> | null>> {
@@ -108,11 +132,7 @@ class TestSecondaryCacheLoader extends EntitySecondaryCacheLoader<
108
132
  const emptyMap = new Map(loadParamsArray.map((p) => [p, null]));
109
133
  return await mapMapAsync(emptyMap, async (_value, loadParams) => {
110
134
  return (
111
- (
112
- await this.entityLoader.loadManyByFieldEqualityConjunctionAsync([
113
- { fieldName: 'intField', fieldValue: loadParams.intValue },
114
- ])
115
- )[0]
135
+ (await this.entityLoader.loadManyByFieldEqualingAsync('intField', loadParams.intValue))[0]
116
136
  ?.enforceValue()
117
137
  ?.getAllFields() ?? null
118
138
  );
@@ -133,6 +153,7 @@ describe(GenericSecondaryEntityCache, () => {
133
153
  new TestGenericCacher(),
134
154
  (params) => `intValue.${params.intValue}`,
135
155
  ),
156
+ EntitySecondaryCacheLoader.getConstructionUtilsForEntityClass(TestEntity, viewerContext),
136
157
  TestEntity.loaderWithAuthorizationResults(viewerContext),
137
158
  );
138
159
 
@@ -169,6 +190,7 @@ describe(GenericSecondaryEntityCache, () => {
169
190
  new TestGenericCacher(),
170
191
  (params) => `intValue.${params.intValue}`,
171
192
  ),
193
+ EntitySecondaryCacheLoader.getConstructionUtilsForEntityClass(TestEntity, viewerContext),
172
194
  TestEntity.loaderWithAuthorizationResults(viewerContext),
173
195
  );
174
196
 
@@ -5,7 +5,7 @@ import { AuthorizationResultBasedEntityAssociationLoader } from '../Authorizatio
5
5
  import { AuthorizationResultBasedEntityLoader } from '../AuthorizationResultBasedEntityLoader';
6
6
  import { EnforcingEntityAssociationLoader } from '../EnforcingEntityAssociationLoader';
7
7
  import { EnforcingEntityLoader } from '../EnforcingEntityLoader';
8
- import { EntityLoaderUtils } from '../EntityLoaderUtils';
8
+ import { EntityInvalidationUtils } from '../EntityInvalidationUtils';
9
9
  import { ReadonlyEntity } from '../ReadonlyEntity';
10
10
  import { ViewerContext } from '../ViewerContext';
11
11
  import { SimpleTestEntity } from '../utils/__testfixtures__/SimpleTestEntity';
@@ -214,11 +214,13 @@ describe(ReadonlyEntity, () => {
214
214
  });
215
215
  });
216
216
 
217
- describe('loaderUtils', () => {
218
- it('creates a new EntityLoaderUtils', async () => {
217
+ describe('invalidationUtils', () => {
218
+ it('creates a new EntityInvalidationUtils', async () => {
219
219
  const companionProvider = createUnitTestEntityCompanionProvider();
220
220
  const viewerContext = new ViewerContext(companionProvider);
221
- expect(SimpleTestEntity.loaderUtils(viewerContext)).toBeInstanceOf(EntityLoaderUtils);
221
+ expect(SimpleTestEntity.invalidationUtils(viewerContext)).toBeInstanceOf(
222
+ EntityInvalidationUtils,
223
+ );
222
224
  });
223
225
  });
224
226
  });
@@ -5,22 +5,22 @@ import { ViewerContext } from '../ViewerContext';
5
5
  import { createUnitTestEntityCompanionProvider } from '../utils/__testfixtures__/createUnitTestEntityCompanionProvider';
6
6
 
7
7
  describe(ViewerContext, () => {
8
- describe('getQueryContextForDatabaseAdaptorFlavor', () => {
8
+ describe('getQueryContextForDatabaseAdapterFlavor', () => {
9
9
  it('creates a new regular query context', () => {
10
10
  const companionProvider = createUnitTestEntityCompanionProvider();
11
11
  const viewerContext = new ViewerContext(companionProvider);
12
- const queryContext = viewerContext.getQueryContextForDatabaseAdaptorFlavor('postgres');
12
+ const queryContext = viewerContext.getQueryContextForDatabaseAdapterFlavor('postgres');
13
13
  expect(queryContext).toBeInstanceOf(EntityQueryContext);
14
14
  expect(queryContext.isInTransaction()).toBe(false);
15
15
  });
16
16
  });
17
17
 
18
- describe('runInTransactionForDatabaseAdaptorFlavorAsync', () => {
18
+ describe('runInTransactionForDatabaseAdapterFlavorAsync', () => {
19
19
  it('creates a new transactional query context', async () => {
20
20
  const companionProvider = createUnitTestEntityCompanionProvider();
21
21
  const viewerContext = new ViewerContext(companionProvider);
22
22
  const didCreateTransaction =
23
- await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
23
+ await viewerContext.runInTransactionForDatabaseAdapterFlavorAsync(
24
24
  'postgres',
25
25
  async (queryContext) => {
26
26
  return queryContext.isInTransaction();
@@ -33,5 +33,6 @@ describe(ViewerScopedEntityCompanion, () => {
33
33
  expect(viewerScopedEntityCompanion.getMutatorFactory()).toBeInstanceOf(
34
34
  ViewerScopedEntityMutatorFactory,
35
35
  );
36
+ expect(viewerScopedEntityCompanion.getMetricsAdapter()).toBeDefined();
36
37
  });
37
38
  });
@@ -50,23 +50,6 @@ describe('Two entities backed by the same table', () => {
50
50
  expect(failedManyResults[0]!.enforceError().message).toEqual(
51
51
  'OneTestEntity must be instantiated with one data',
52
52
  );
53
-
54
- const fieldEqualityConjunctionResults = await OneTestEntity.loaderWithAuthorizationResults(
55
- viewerContext,
56
- ).loadManyByFieldEqualityConjunctionAsync([
57
- {
58
- fieldName: 'common_other_field',
59
- fieldValue: 'wat',
60
- },
61
- ]);
62
- const successfulfieldEqualityConjunctionResultsResults = successfulResults(
63
- fieldEqualityConjunctionResults,
64
- );
65
- const failedfieldEqualityConjunctionResultsResults = failedResults(
66
- fieldEqualityConjunctionResults,
67
- );
68
- expect(successfulfieldEqualityConjunctionResultsResults).toHaveLength(1);
69
- expect(failedfieldEqualityConjunctionResultsResults).toHaveLength(1);
70
53
  });
71
54
  });
72
55
 
@@ -231,3 +231,17 @@ export class EntityDatabaseAdapterExcessiveDeleteResultError extends EntityDatab
231
231
  return EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_EXCESSIVE_DELETE_RESULT;
232
232
  }
233
233
  }
234
+
235
+ export class EntityDatabaseAdapterPaginationCursorInvalidError extends EntityDatabaseAdapterError {
236
+ static {
237
+ this.prototype.name = 'EntityDatabaseAdapterPaginationCursorError';
238
+ }
239
+
240
+ get state(): EntityErrorState.PERMANENT {
241
+ return EntityErrorState.PERMANENT;
242
+ }
243
+
244
+ get code(): EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID {
245
+ return EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID;
246
+ }
247
+ }
@@ -27,6 +27,7 @@ export enum EntityErrorCode {
27
27
  ERR_ENTITY_DATABASE_ADAPTER_EMPTY_UPDATE_RESULT = 'ERR_ENTITY_DATABASE_ADAPTER_EMPTY_UPDATE_RESULT',
28
28
  ERR_ENTITY_DATABASE_ADAPTER_EXCESSIVE_DELETE_RESULT = 'ERR_ENTITY_DATABASE_ADAPTER_EXCESSIVE_DELETE_RESULT',
29
29
  ERR_ENTITY_CACHE_ADAPTER_TRANSIENT = 'ERR_ENTITY_CACHE_ADAPTER_TRANSIENT',
30
+ ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID = 'ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID',
30
31
  }
31
32
 
32
33
  /**
@@ -11,6 +11,7 @@ import {
11
11
  EntityDatabaseAdapterExclusionConstraintError,
12
12
  EntityDatabaseAdapterForeignKeyConstraintError,
13
13
  EntityDatabaseAdapterNotNullConstraintError,
14
+ EntityDatabaseAdapterPaginationCursorInvalidError,
14
15
  EntityDatabaseAdapterTransientError,
15
16
  EntityDatabaseAdapterUniqueConstraintError,
16
17
  EntityDatabaseAdapterUnknownError,
@@ -82,5 +83,13 @@ describe(EntityDatabaseAdapterError, () => {
82
83
  expect(excessiveDeleteError.code).toBe(
83
84
  EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_EXCESSIVE_DELETE_RESULT,
84
85
  );
86
+
87
+ const paginationCursorInvalidError = new EntityDatabaseAdapterPaginationCursorInvalidError(
88
+ 'test',
89
+ );
90
+ expect(paginationCursorInvalidError.state).toBe(EntityErrorState.PERMANENT);
91
+ expect(paginationCursorInvalidError.code).toBe(
92
+ EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID,
93
+ );
85
94
  });
86
95
  });
@@ -1,5 +1,8 @@
1
1
  import { describe, expect, it } from '@jest/globals';
2
+ import { instance, mock } from 'ts-mockito';
2
3
 
4
+ import { ViewerContext } from '../../ViewerContext';
5
+ import { SimpleTestEntity } from '../../utils/__testfixtures__/SimpleTestEntity';
3
6
  import { EntityCacheAdapterTransientError } from '../EntityCacheAdapterError';
4
7
  import { EntityErrorCode, EntityErrorState } from '../EntityError';
5
8
  import { EntityInvalidFieldValueError } from '../EntityInvalidFieldValueError';
@@ -14,16 +17,21 @@ describe('EntityError subclasses', () => {
14
17
  });
15
18
 
16
19
  it('EntityNotAuthorizedError has correct state and code', () => {
17
- const mockEntity = { constructor: { name: 'TestEntity' }, toString: () => 'TestEntity' } as any;
18
- const mockViewerContext = { toString: () => 'TestViewer' } as any;
19
- const error = new EntityNotAuthorizedError(mockEntity, mockViewerContext, 0, 0);
20
+ const viewerContext = instance(mock(ViewerContext));
21
+ const data = { id: '1' };
22
+ const testEntity = new SimpleTestEntity({
23
+ viewerContext,
24
+ id: 'what',
25
+ databaseFields: data,
26
+ selectedFields: data,
27
+ });
28
+ const error = new EntityNotAuthorizedError(testEntity, viewerContext, 0, 0);
20
29
  expect(error.state).toBe(EntityErrorState.PERMANENT);
21
30
  expect(error.code).toBe(EntityErrorCode.ERR_ENTITY_NOT_AUTHORIZED);
22
31
  });
23
32
 
24
33
  it('EntityInvalidFieldValueError has correct state and code', () => {
25
- const mockEntityClass = { name: 'TestEntity' } as any;
26
- const error = new EntityInvalidFieldValueError(mockEntityClass, 'testField', 'badValue');
34
+ const error = new EntityInvalidFieldValueError(SimpleTestEntity, 'id', 'badValue');
27
35
  expect(error.state).toBe(EntityErrorState.PERMANENT);
28
36
  expect(error.code).toBe(EntityErrorCode.ERR_ENTITY_INVALID_FIELD_VALUE);
29
37
  });