@expo/entity 0.20.0 → 0.21.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.
- package/build/EntityQueryContext.d.ts +12 -1
- package/build/EntityQueryContext.js +26 -1
- package/build/EntityQueryContext.js.map +1 -1
- package/build/EntityQueryContextProvider.js +1 -0
- package/build/EntityQueryContextProvider.js.map +1 -1
- package/build/__tests__/EntityQueryContext-test.d.ts +1 -0
- package/build/__tests__/EntityQueryContext-test.js +56 -0
- package/build/__tests__/EntityQueryContext-test.js.map +1 -0
- package/package.json +1 -1
- package/src/EntityQueryContext.ts +35 -1
- package/src/EntityQueryContextProvider.ts +1 -0
- package/src/__tests__/EntityQueryContext-test.ts +82 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import EntityQueryContextProvider from './EntityQueryContextProvider';
|
|
2
2
|
export declare type PostCommitCallback = (...args: any) => Promise<any>;
|
|
3
|
+
export declare type PreCommitCallback = (queryContext: EntityTransactionalQueryContext, ...args: any) => Promise<any>;
|
|
3
4
|
/**
|
|
4
5
|
* Entity framework representation of transactional and non-transactional database
|
|
5
6
|
* query execution units.
|
|
@@ -23,10 +24,19 @@ export declare class EntityNonTransactionalQueryContext extends EntityQueryConte
|
|
|
23
24
|
export declare class EntityTransactionalQueryContext extends EntityQueryContext {
|
|
24
25
|
private readonly postCommitInvalidationCallbacks;
|
|
25
26
|
private readonly postCommitCallbacks;
|
|
27
|
+
private readonly preCommitCallbacks;
|
|
28
|
+
/**
|
|
29
|
+
* Schedule a pre-commit callback. These will be run within the transaction right before it is
|
|
30
|
+
* committed, and will be run in the order specified. Ordering of callbacks scheduled with the
|
|
31
|
+
* same value for the order parameter is undefined within that ordering group.
|
|
32
|
+
* @param callback - callback to schedule
|
|
33
|
+
* @param order - order in which this should be run relative to other scheduled pre-commit callbacks,
|
|
34
|
+
* with higher numbers running later than lower numbers.
|
|
35
|
+
*/
|
|
36
|
+
appendPreCommitCallback(callback: PreCommitCallback, order: number): void;
|
|
26
37
|
/**
|
|
27
38
|
* Schedule a post-commit cache invalidation callback. These are run before normal
|
|
28
39
|
* post-commit callbacks in order to have cache consistency in normal post-commit callbacks.
|
|
29
|
-
*
|
|
30
40
|
* @param callback - callback to schedule
|
|
31
41
|
*/
|
|
32
42
|
appendPostCommitInvalidationCallback(callback: PostCommitCallback): void;
|
|
@@ -36,6 +46,7 @@ export declare class EntityTransactionalQueryContext extends EntityQueryContext
|
|
|
36
46
|
* @param callback - callback to schedule
|
|
37
47
|
*/
|
|
38
48
|
appendPostCommitCallback(callback: PostCommitCallback): void;
|
|
49
|
+
runPreCommitCallbacksAsync(): Promise<void>;
|
|
39
50
|
runPostCommitCallbacksAsync(): Promise<void>;
|
|
40
51
|
isInTransaction(): boolean;
|
|
41
52
|
runInTransactionIfNotInTransactionAsync<T>(transactionScope: (queryContext: EntityTransactionalQueryContext) => Promise<T>): Promise<T>;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.EntityTransactionalQueryContext = exports.EntityNonTransactionalQueryContext = exports.EntityQueryContext = void 0;
|
|
7
|
+
const assert_1 = __importDefault(require("assert"));
|
|
4
8
|
/**
|
|
5
9
|
* Entity framework representation of transactional and non-transactional database
|
|
6
10
|
* query execution units.
|
|
@@ -35,11 +39,23 @@ class EntityTransactionalQueryContext extends EntityQueryContext {
|
|
|
35
39
|
super(...arguments);
|
|
36
40
|
this.postCommitInvalidationCallbacks = [];
|
|
37
41
|
this.postCommitCallbacks = [];
|
|
42
|
+
this.preCommitCallbacks = [];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Schedule a pre-commit callback. These will be run within the transaction right before it is
|
|
46
|
+
* committed, and will be run in the order specified. Ordering of callbacks scheduled with the
|
|
47
|
+
* same value for the order parameter is undefined within that ordering group.
|
|
48
|
+
* @param callback - callback to schedule
|
|
49
|
+
* @param order - order in which this should be run relative to other scheduled pre-commit callbacks,
|
|
50
|
+
* with higher numbers running later than lower numbers.
|
|
51
|
+
*/
|
|
52
|
+
appendPreCommitCallback(callback, order) {
|
|
53
|
+
(0, assert_1.default)(order >= Number.MIN_SAFE_INTEGER && order <= Number.MAX_SAFE_INTEGER, `Invalid order specified: ${order}`);
|
|
54
|
+
this.preCommitCallbacks.push({ callback, order });
|
|
38
55
|
}
|
|
39
56
|
/**
|
|
40
57
|
* Schedule a post-commit cache invalidation callback. These are run before normal
|
|
41
58
|
* post-commit callbacks in order to have cache consistency in normal post-commit callbacks.
|
|
42
|
-
*
|
|
43
59
|
* @param callback - callback to schedule
|
|
44
60
|
*/
|
|
45
61
|
appendPostCommitInvalidationCallback(callback) {
|
|
@@ -53,6 +69,15 @@ class EntityTransactionalQueryContext extends EntityQueryContext {
|
|
|
53
69
|
appendPostCommitCallback(callback) {
|
|
54
70
|
this.postCommitCallbacks.push(callback);
|
|
55
71
|
}
|
|
72
|
+
async runPreCommitCallbacksAsync() {
|
|
73
|
+
const callbacks = [...this.preCommitCallbacks]
|
|
74
|
+
.sort((a, b) => a.order - b.order)
|
|
75
|
+
.map((c) => c.callback);
|
|
76
|
+
this.preCommitCallbacks.length = 0;
|
|
77
|
+
for (const callback of callbacks) {
|
|
78
|
+
await callback(this);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
56
81
|
async runPostCommitCallbacksAsync() {
|
|
57
82
|
const invalidationCallbacks = [...this.postCommitInvalidationCallbacks];
|
|
58
83
|
this.postCommitInvalidationCallbacks.length = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityQueryContext.js","sourceRoot":"","sources":["../src/EntityQueryContext.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EntityQueryContext.js","sourceRoot":"","sources":["../src/EntityQueryContext.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,6 +19,7 @@ class EntityQueryContextProvider {
|
|
|
19
19
|
const [returnedValue, queryContext] = await this.createTransactionRunner()(async (queryInterface) => {
|
|
20
20
|
const queryContext = new EntityQueryContext_1.EntityTransactionalQueryContext(queryInterface);
|
|
21
21
|
const result = await transactionScope(queryContext);
|
|
22
|
+
await queryContext.runPreCommitCallbacksAsync();
|
|
22
23
|
return [result, queryContext];
|
|
23
24
|
});
|
|
24
25
|
await queryContext.runPostCommitCallbacksAsync();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityQueryContextProvider.js","sourceRoot":"","sources":["../src/EntityQueryContextProvider.ts"],"names":[],"mappings":";;AAAA,6DAG8B;AAE9B;;GAEG;AACH,MAA8B,0BAA0B;IACtD;;OAEG;IACI,eAAe;QACpB,OAAO,IAAI,uDAAkC,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,IAAI,CAAC,CAAC;IAChF,CAAC;IAcD;;;OAGG;IACH,KAAK,CAAC,qBAAqB,CACzB,gBAA+E;QAE/E,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC,GAAG,MAAM,IAAI,CAAC,uBAAuB,EAErE,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE;YAC3B,MAAM,YAAY,GAAG,IAAI,oDAA+B,CAAC,cAAc,CAAC,CAAC;YACzE,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,YAAY,CAAC,CAAC;YACpD,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,MAAM,YAAY,CAAC,2BAA2B,EAAE,CAAC;QACjD,OAAO,aAAa,CAAC;IACvB,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"EntityQueryContextProvider.js","sourceRoot":"","sources":["../src/EntityQueryContextProvider.ts"],"names":[],"mappings":";;AAAA,6DAG8B;AAE9B;;GAEG;AACH,MAA8B,0BAA0B;IACtD;;OAEG;IACI,eAAe;QACpB,OAAO,IAAI,uDAAkC,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,IAAI,CAAC,CAAC;IAChF,CAAC;IAcD;;;OAGG;IACH,KAAK,CAAC,qBAAqB,CACzB,gBAA+E;QAE/E,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC,GAAG,MAAM,IAAI,CAAC,uBAAuB,EAErE,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE;YAC3B,MAAM,YAAY,GAAG,IAAI,oDAA+B,CAAC,cAAc,CAAC,CAAC;YACzE,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,YAAY,CAAC,CAAC;YACpD,MAAM,YAAY,CAAC,0BAA0B,EAAE,CAAC;YAChD,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,MAAM,YAAY,CAAC,2BAA2B,EAAE,CAAC;QACjD,OAAO,aAAa,CAAC;IACvB,CAAC;CACF;AAtCD,6CAsCC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const invariant_1 = __importDefault(require("invariant"));
|
|
7
|
+
const EntityQueryContext_1 = require("../EntityQueryContext");
|
|
8
|
+
const ViewerContext_1 = __importDefault(require("../ViewerContext"));
|
|
9
|
+
const createUnitTestEntityCompanionProvider_1 = require("../utils/testing/createUnitTestEntityCompanionProvider");
|
|
10
|
+
describe(EntityQueryContext_1.EntityQueryContext, () => {
|
|
11
|
+
describe('callbacks', () => {
|
|
12
|
+
it('calls all callbacks, and calls invalidation first', async () => {
|
|
13
|
+
const companionProvider = (0, createUnitTestEntityCompanionProvider_1.createUnitTestEntityCompanionProvider)();
|
|
14
|
+
const viewerContext = new ViewerContext_1.default(companionProvider);
|
|
15
|
+
const preCommitFirstCallback = jest.fn(async () => { });
|
|
16
|
+
const preCommitSecondCallback = jest.fn(async () => { });
|
|
17
|
+
const postCommitInvalidationCallback = jest.fn(async () => {
|
|
18
|
+
(0, invariant_1.default)(preCommitFirstCallback.mock.calls.length === 1, 'preCommit should be called before postCommitInvalidation');
|
|
19
|
+
(0, invariant_1.default)(preCommitSecondCallback.mock.calls.length === 1, 'preCommit should be called before postCommitInvalidation');
|
|
20
|
+
});
|
|
21
|
+
const postCommitCallback = jest.fn(async () => {
|
|
22
|
+
(0, invariant_1.default)(preCommitFirstCallback.mock.calls.length === 1, 'preCommit should be called before postCommit');
|
|
23
|
+
(0, invariant_1.default)(preCommitSecondCallback.mock.calls.length === 1, 'preCommit should be called before postCommit');
|
|
24
|
+
(0, invariant_1.default)(postCommitInvalidationCallback.mock.calls.length === 1, 'postCommitInvalidation should be called before postCommit');
|
|
25
|
+
});
|
|
26
|
+
await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync('postgres', async (queryContext) => {
|
|
27
|
+
queryContext.appendPostCommitCallback(postCommitCallback);
|
|
28
|
+
queryContext.appendPostCommitInvalidationCallback(postCommitInvalidationCallback);
|
|
29
|
+
queryContext.appendPreCommitCallback(preCommitSecondCallback, 2);
|
|
30
|
+
queryContext.appendPreCommitCallback(preCommitFirstCallback, 1);
|
|
31
|
+
});
|
|
32
|
+
expect(preCommitFirstCallback).toHaveBeenCalledTimes(1);
|
|
33
|
+
expect(preCommitSecondCallback).toHaveBeenCalledTimes(1);
|
|
34
|
+
expect(postCommitCallback).toHaveBeenCalledTimes(1);
|
|
35
|
+
expect(postCommitInvalidationCallback).toHaveBeenCalledTimes(1);
|
|
36
|
+
});
|
|
37
|
+
it('prevents transaction from finishing when precommit throws (post commit callbacks are not called)', async () => {
|
|
38
|
+
const companionProvider = (0, createUnitTestEntityCompanionProvider_1.createUnitTestEntityCompanionProvider)();
|
|
39
|
+
const viewerContext = new ViewerContext_1.default(companionProvider);
|
|
40
|
+
const preCommitCallback = jest.fn(async () => {
|
|
41
|
+
throw new Error('wat');
|
|
42
|
+
});
|
|
43
|
+
const postCommitInvalidationCallback = jest.fn(async () => { });
|
|
44
|
+
const postCommitCallback = jest.fn(async () => { });
|
|
45
|
+
await expect(viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync('postgres', async (queryContext) => {
|
|
46
|
+
queryContext.appendPostCommitCallback(postCommitCallback);
|
|
47
|
+
queryContext.appendPostCommitInvalidationCallback(postCommitInvalidationCallback);
|
|
48
|
+
queryContext.appendPreCommitCallback(preCommitCallback, 0);
|
|
49
|
+
})).rejects.toThrowError('wat');
|
|
50
|
+
expect(preCommitCallback).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(postCommitCallback).toHaveBeenCalledTimes(0);
|
|
52
|
+
expect(postCommitInvalidationCallback).toHaveBeenCalledTimes(0);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=EntityQueryContext-test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityQueryContext-test.js","sourceRoot":"","sources":["../../src/__tests__/EntityQueryContext-test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import assert from 'assert';
|
|
2
|
+
|
|
1
3
|
import EntityQueryContextProvider from './EntityQueryContextProvider';
|
|
2
4
|
|
|
3
5
|
export type PostCommitCallback = (...args: any) => Promise<any>;
|
|
6
|
+
export type PreCommitCallback = (
|
|
7
|
+
queryContext: EntityTransactionalQueryContext,
|
|
8
|
+
...args: any
|
|
9
|
+
) => Promise<any>;
|
|
4
10
|
|
|
5
11
|
/**
|
|
6
12
|
* Entity framework representation of transactional and non-transactional database
|
|
@@ -46,10 +52,27 @@ export class EntityTransactionalQueryContext extends EntityQueryContext {
|
|
|
46
52
|
private readonly postCommitInvalidationCallbacks: PostCommitCallback[] = [];
|
|
47
53
|
private readonly postCommitCallbacks: PostCommitCallback[] = [];
|
|
48
54
|
|
|
55
|
+
private readonly preCommitCallbacks: { callback: PreCommitCallback; order: number }[] = [];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Schedule a pre-commit callback. These will be run within the transaction right before it is
|
|
59
|
+
* committed, and will be run in the order specified. Ordering of callbacks scheduled with the
|
|
60
|
+
* same value for the order parameter is undefined within that ordering group.
|
|
61
|
+
* @param callback - callback to schedule
|
|
62
|
+
* @param order - order in which this should be run relative to other scheduled pre-commit callbacks,
|
|
63
|
+
* with higher numbers running later than lower numbers.
|
|
64
|
+
*/
|
|
65
|
+
public appendPreCommitCallback(callback: PreCommitCallback, order: number): void {
|
|
66
|
+
assert(
|
|
67
|
+
order >= Number.MIN_SAFE_INTEGER && order <= Number.MAX_SAFE_INTEGER,
|
|
68
|
+
`Invalid order specified: ${order}`
|
|
69
|
+
);
|
|
70
|
+
this.preCommitCallbacks.push({ callback, order });
|
|
71
|
+
}
|
|
72
|
+
|
|
49
73
|
/**
|
|
50
74
|
* Schedule a post-commit cache invalidation callback. These are run before normal
|
|
51
75
|
* post-commit callbacks in order to have cache consistency in normal post-commit callbacks.
|
|
52
|
-
*
|
|
53
76
|
* @param callback - callback to schedule
|
|
54
77
|
*/
|
|
55
78
|
public appendPostCommitInvalidationCallback(callback: PostCommitCallback): void {
|
|
@@ -65,6 +88,17 @@ export class EntityTransactionalQueryContext extends EntityQueryContext {
|
|
|
65
88
|
this.postCommitCallbacks.push(callback);
|
|
66
89
|
}
|
|
67
90
|
|
|
91
|
+
public async runPreCommitCallbacksAsync(): Promise<void> {
|
|
92
|
+
const callbacks = [...this.preCommitCallbacks]
|
|
93
|
+
.sort((a, b) => a.order - b.order)
|
|
94
|
+
.map((c) => c.callback);
|
|
95
|
+
this.preCommitCallbacks.length = 0;
|
|
96
|
+
|
|
97
|
+
for (const callback of callbacks) {
|
|
98
|
+
await callback(this);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
68
102
|
public async runPostCommitCallbacksAsync(): Promise<void> {
|
|
69
103
|
const invalidationCallbacks = [...this.postCommitInvalidationCallbacks];
|
|
70
104
|
this.postCommitInvalidationCallbacks.length = 0;
|
|
@@ -38,6 +38,7 @@ export default abstract class EntityQueryContextProvider {
|
|
|
38
38
|
>()(async (queryInterface) => {
|
|
39
39
|
const queryContext = new EntityTransactionalQueryContext(queryInterface);
|
|
40
40
|
const result = await transactionScope(queryContext);
|
|
41
|
+
await queryContext.runPreCommitCallbacksAsync();
|
|
41
42
|
return [result, queryContext];
|
|
42
43
|
});
|
|
43
44
|
await queryContext.runPostCommitCallbacksAsync();
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import invariant from 'invariant';
|
|
2
|
+
|
|
3
|
+
import { EntityQueryContext } from '../EntityQueryContext';
|
|
4
|
+
import ViewerContext from '../ViewerContext';
|
|
5
|
+
import { createUnitTestEntityCompanionProvider } from '../utils/testing/createUnitTestEntityCompanionProvider';
|
|
6
|
+
|
|
7
|
+
describe(EntityQueryContext, () => {
|
|
8
|
+
describe('callbacks', () => {
|
|
9
|
+
it('calls all callbacks, and calls invalidation first', async () => {
|
|
10
|
+
const companionProvider = createUnitTestEntityCompanionProvider();
|
|
11
|
+
const viewerContext = new ViewerContext(companionProvider);
|
|
12
|
+
|
|
13
|
+
const preCommitFirstCallback = jest.fn(async (): Promise<void> => {});
|
|
14
|
+
const preCommitSecondCallback = jest.fn(async (): Promise<void> => {});
|
|
15
|
+
const postCommitInvalidationCallback = jest.fn(async (): Promise<void> => {
|
|
16
|
+
invariant(
|
|
17
|
+
preCommitFirstCallback.mock.calls.length === 1,
|
|
18
|
+
'preCommit should be called before postCommitInvalidation'
|
|
19
|
+
);
|
|
20
|
+
invariant(
|
|
21
|
+
preCommitSecondCallback.mock.calls.length === 1,
|
|
22
|
+
'preCommit should be called before postCommitInvalidation'
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
const postCommitCallback = jest.fn(async (): Promise<void> => {
|
|
26
|
+
invariant(
|
|
27
|
+
preCommitFirstCallback.mock.calls.length === 1,
|
|
28
|
+
'preCommit should be called before postCommit'
|
|
29
|
+
);
|
|
30
|
+
invariant(
|
|
31
|
+
preCommitSecondCallback.mock.calls.length === 1,
|
|
32
|
+
'preCommit should be called before postCommit'
|
|
33
|
+
);
|
|
34
|
+
invariant(
|
|
35
|
+
postCommitInvalidationCallback.mock.calls.length === 1,
|
|
36
|
+
'postCommitInvalidation should be called before postCommit'
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
|
|
41
|
+
'postgres',
|
|
42
|
+
async (queryContext) => {
|
|
43
|
+
queryContext.appendPostCommitCallback(postCommitCallback);
|
|
44
|
+
queryContext.appendPostCommitInvalidationCallback(postCommitInvalidationCallback);
|
|
45
|
+
queryContext.appendPreCommitCallback(preCommitSecondCallback, 2);
|
|
46
|
+
queryContext.appendPreCommitCallback(preCommitFirstCallback, 1);
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(preCommitFirstCallback).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(preCommitSecondCallback).toHaveBeenCalledTimes(1);
|
|
52
|
+
expect(postCommitCallback).toHaveBeenCalledTimes(1);
|
|
53
|
+
expect(postCommitInvalidationCallback).toHaveBeenCalledTimes(1);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('prevents transaction from finishing when precommit throws (post commit callbacks are not called)', async () => {
|
|
57
|
+
const companionProvider = createUnitTestEntityCompanionProvider();
|
|
58
|
+
const viewerContext = new ViewerContext(companionProvider);
|
|
59
|
+
|
|
60
|
+
const preCommitCallback = jest.fn(async (): Promise<void> => {
|
|
61
|
+
throw new Error('wat');
|
|
62
|
+
});
|
|
63
|
+
const postCommitInvalidationCallback = jest.fn(async (): Promise<void> => {});
|
|
64
|
+
const postCommitCallback = jest.fn(async (): Promise<void> => {});
|
|
65
|
+
|
|
66
|
+
await expect(
|
|
67
|
+
viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
|
|
68
|
+
'postgres',
|
|
69
|
+
async (queryContext) => {
|
|
70
|
+
queryContext.appendPostCommitCallback(postCommitCallback);
|
|
71
|
+
queryContext.appendPostCommitInvalidationCallback(postCommitInvalidationCallback);
|
|
72
|
+
queryContext.appendPreCommitCallback(preCommitCallback, 0);
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
).rejects.toThrowError('wat');
|
|
76
|
+
|
|
77
|
+
expect(preCommitCallback).toHaveBeenCalledTimes(1);
|
|
78
|
+
expect(postCommitCallback).toHaveBeenCalledTimes(0);
|
|
79
|
+
expect(postCommitInvalidationCallback).toHaveBeenCalledTimes(0);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|