@powersync/service-module-mongodb-storage 0.12.7 → 0.12.9
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/CHANGELOG.md +33 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/migrations/db/migrations/1752661449910-connection-reporting.d.ts +3 -0
- package/dist/migrations/db/migrations/1752661449910-connection-reporting.js +36 -0
- package/dist/migrations/db/migrations/1752661449910-connection-reporting.js.map +1 -0
- package/dist/migrations/db/migrations/1760433882550-bucket-state-index2.d.ts +3 -0
- package/dist/migrations/db/migrations/1760433882550-bucket-state-index2.js +25 -0
- package/dist/migrations/db/migrations/1760433882550-bucket-state-index2.js.map +1 -0
- package/dist/storage/MongoBucketStorage.js +1 -1
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/MongoReportStorage.d.ts +17 -0
- package/dist/storage/MongoReportStorage.js +152 -0
- package/dist/storage/MongoReportStorage.js.map +1 -0
- package/dist/storage/implementation/MongoBucketBatch.js +1 -1
- package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
- package/dist/storage/implementation/MongoCompactor.d.ts +13 -3
- package/dist/storage/implementation/MongoCompactor.js +86 -90
- package/dist/storage/implementation/MongoCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoStorageProvider.d.ts +1 -1
- package/dist/storage/implementation/MongoStorageProvider.js +7 -3
- package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +2 -2
- package/dist/storage/implementation/MongoSyncBucketStorage.js +16 -5
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js +6 -2
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
- package/dist/storage/implementation/PersistedBatch.js +1 -1
- package/dist/storage/implementation/PersistedBatch.js.map +1 -1
- package/dist/storage/implementation/db.d.ts +10 -1
- package/dist/storage/implementation/db.js +25 -0
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +3 -0
- package/dist/storage/storage-index.d.ts +3 -2
- package/dist/storage/storage-index.js +3 -2
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/utils/test-utils.d.ts +13 -0
- package/dist/utils/test-utils.js +40 -0
- package/dist/utils/test-utils.js.map +1 -0
- package/dist/{storage/implementation → utils}/util.d.ts +2 -7
- package/dist/{storage/implementation → utils}/util.js +1 -16
- package/dist/utils/util.js.map +1 -0
- package/dist/utils/utils-index.d.ts +2 -0
- package/dist/utils/utils-index.js +3 -0
- package/dist/utils/utils-index.js.map +1 -0
- package/package.json +9 -9
- package/src/index.ts +1 -0
- package/src/migrations/db/migrations/1752661449910-connection-reporting.ts +58 -0
- package/src/migrations/db/migrations/1760433882550-bucket-state-index2.ts +34 -0
- package/src/storage/MongoBucketStorage.ts +1 -1
- package/src/storage/MongoReportStorage.ts +174 -0
- package/src/storage/implementation/MongoBucketBatch.ts +1 -1
- package/src/storage/implementation/MongoCompactor.ts +100 -96
- package/src/storage/implementation/MongoStorageProvider.ts +9 -4
- package/src/storage/implementation/MongoSyncBucketStorage.ts +20 -6
- package/src/storage/implementation/MongoWriteCheckpointAPI.ts +6 -2
- package/src/storage/implementation/PersistedBatch.ts +1 -1
- package/src/storage/implementation/db.ts +30 -0
- package/src/storage/implementation/models.ts +3 -0
- package/src/storage/storage-index.ts +3 -2
- package/src/utils/test-utils.ts +57 -0
- package/src/{storage/implementation → utils}/util.ts +3 -19
- package/src/utils/utils-index.ts +2 -0
- package/test/src/__snapshots__/connection-report-storage.test.ts.snap +215 -0
- package/test/src/connection-report-storage.test.ts +133 -0
- package/test/src/storage.test.ts +3 -51
- package/test/src/storage_compacting.test.ts +17 -2
- package/test/src/util.ts +6 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/storage/implementation/MongoTestStorageFactoryGenerator.d.ts +0 -9
- package/dist/storage/implementation/MongoTestStorageFactoryGenerator.js +0 -19
- package/dist/storage/implementation/MongoTestStorageFactoryGenerator.js.map +0 -1
- package/dist/storage/implementation/util.js.map +0 -1
- package/src/storage/implementation/MongoTestStorageFactoryGenerator.ts +0 -31
|
@@ -7,8 +7,9 @@ export * from './implementation/MongoPersistedSyncRulesContent.js';
|
|
|
7
7
|
export * from './implementation/MongoStorageProvider.js';
|
|
8
8
|
export * from './implementation/MongoSyncBucketStorage.js';
|
|
9
9
|
export * from './implementation/MongoSyncRulesLock.js';
|
|
10
|
-
export * from './implementation/MongoTestStorageFactoryGenerator.js';
|
|
11
10
|
export * from './implementation/OperationBatch.js';
|
|
12
11
|
export * from './implementation/PersistedBatch.js';
|
|
13
|
-
export * from '
|
|
12
|
+
export * from '../utils/util.js';
|
|
14
13
|
export * from './MongoBucketStorage.js';
|
|
14
|
+
export * from './MongoReportStorage.js';
|
|
15
|
+
export * as test_utils from '../utils/test-utils.js';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
+
import { PowerSyncMongo } from '../storage/implementation/db.js';
|
|
3
|
+
import { TestStorageOptions } from '@powersync/service-core';
|
|
4
|
+
import { MongoReportStorage } from '../storage/MongoReportStorage.js';
|
|
5
|
+
import { MongoBucketStorage } from '../storage/MongoBucketStorage.js';
|
|
6
|
+
import { MongoSyncBucketStorageOptions } from '../storage/implementation/MongoSyncBucketStorage.js';
|
|
7
|
+
|
|
8
|
+
export type MongoTestStorageOptions = {
|
|
9
|
+
url: string;
|
|
10
|
+
isCI: boolean;
|
|
11
|
+
internalOptions?: MongoSyncBucketStorageOptions;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function mongoTestStorageFactoryGenerator(factoryOptions: MongoTestStorageOptions) {
|
|
15
|
+
return async (options?: TestStorageOptions) => {
|
|
16
|
+
const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
|
|
17
|
+
|
|
18
|
+
// None of the tests insert data into this collection, so it was never created
|
|
19
|
+
if (!(await db.db.listCollections({ name: db.bucket_parameters.collectionName }).hasNext())) {
|
|
20
|
+
await db.db.createCollection('bucket_parameters');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Full migrations are not currently run for tests, so we manually create this
|
|
24
|
+
await db.createCheckpointEventsCollection();
|
|
25
|
+
|
|
26
|
+
if (!options?.doNotClear) {
|
|
27
|
+
await db.clear();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }, factoryOptions.internalOptions);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function mongoTestReportStorageFactoryGenerator(factoryOptions: MongoTestStorageOptions) {
|
|
35
|
+
return async (options?: TestStorageOptions) => {
|
|
36
|
+
const db = connectMongoForTests(factoryOptions.url, factoryOptions.isCI);
|
|
37
|
+
|
|
38
|
+
await db.createConnectionReportingCollection();
|
|
39
|
+
|
|
40
|
+
if (!options?.doNotClear) {
|
|
41
|
+
await db.clear();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return new MongoReportStorage(db);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const connectMongoForTests = (url: string, isCI: boolean) => {
|
|
49
|
+
// Short timeout for tests, to fail fast when the server is not available.
|
|
50
|
+
// Slightly longer timeouts for CI, to avoid arbitrary test failures
|
|
51
|
+
const client = new mongo.MongoClient(url, {
|
|
52
|
+
connectTimeoutMS: isCI ? 15_000 : 5_000,
|
|
53
|
+
socketTimeoutMS: isCI ? 15_000 : 5_000,
|
|
54
|
+
serverSelectionTimeoutMS: isCI ? 15_000 : 2_500
|
|
55
|
+
});
|
|
56
|
+
return new PowerSyncMongo(client);
|
|
57
|
+
};
|
|
@@ -3,11 +3,9 @@ import * as crypto from 'crypto';
|
|
|
3
3
|
import * as uuid from 'uuid';
|
|
4
4
|
|
|
5
5
|
import { mongo } from '@powersync/lib-service-mongodb';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
import { PowerSyncMongo } from './db.js';
|
|
9
|
-
import { BucketDataDocument } from './models.js';
|
|
6
|
+
import { storage, utils } from '@powersync/service-core';
|
|
10
7
|
import { ServiceAssertionError } from '@powersync/lib-services-framework';
|
|
8
|
+
import { BucketDataDocument } from '../storage/implementation/models.js';
|
|
11
9
|
|
|
12
10
|
export function idPrefixFilter<T>(prefix: Partial<T>, rest: (keyof T)[]): mongo.Condition<T> {
|
|
13
11
|
let filter = {
|
|
@@ -41,7 +39,7 @@ export function generateSlotName(prefix: string, sync_rules_id: number) {
|
|
|
41
39
|
* However, that makes `has_more` detection very difficult, since the cursor is always closed
|
|
42
40
|
* after the first batch. Instead, we do a workaround to only fetch a single batch below.
|
|
43
41
|
*
|
|
44
|
-
* For this to be effective, set batchSize = limit in the find command.
|
|
42
|
+
* For this to be effective, set batchSize = limit + 1 in the find command.
|
|
45
43
|
*/
|
|
46
44
|
export async function readSingleBatch<T>(cursor: mongo.AbstractCursor<T>): Promise<{ data: T[]; hasMore: boolean }> {
|
|
47
45
|
try {
|
|
@@ -105,20 +103,6 @@ export function replicaIdToSubkey(table: bson.ObjectId, id: storage.ReplicaId):
|
|
|
105
103
|
}
|
|
106
104
|
}
|
|
107
105
|
|
|
108
|
-
/**
|
|
109
|
-
* Helper for unit tests
|
|
110
|
-
*/
|
|
111
|
-
export const connectMongoForTests = (url: string, isCI: boolean) => {
|
|
112
|
-
// Short timeout for tests, to fail fast when the server is not available.
|
|
113
|
-
// Slightly longer timeouts for CI, to avoid arbitrary test failures
|
|
114
|
-
const client = new mongo.MongoClient(url, {
|
|
115
|
-
connectTimeoutMS: isCI ? 15_000 : 5_000,
|
|
116
|
-
socketTimeoutMS: isCI ? 15_000 : 5_000,
|
|
117
|
-
serverSelectionTimeoutMS: isCI ? 15_000 : 2_500
|
|
118
|
-
});
|
|
119
|
-
return new PowerSyncMongo(client);
|
|
120
|
-
};
|
|
121
|
-
|
|
122
106
|
export function setSessionSnapshotTime(session: mongo.ClientSession, time: bson.Timestamp) {
|
|
123
107
|
// This is a workaround for the lack of direct support for snapshot reads in the MongoDB driver.
|
|
124
108
|
if (!session.snapshotEnabled) {
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`Connection reporting storage > Should create a connection report if its after a day 1`] = `
|
|
4
|
+
[
|
|
5
|
+
{
|
|
6
|
+
"client_id": "client_week",
|
|
7
|
+
"sdk": "powersync-js/1.24.5",
|
|
8
|
+
"user_agent": "powersync-js/1.21.0 powersync-web Firefox/141 linux",
|
|
9
|
+
"user_id": "user_week",
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"client_id": "client_week",
|
|
13
|
+
"sdk": "powersync-js/1.24.5",
|
|
14
|
+
"user_agent": "powersync-js/1.21.0 powersync-web Firefox/141 linux",
|
|
15
|
+
"user_id": "user_week",
|
|
16
|
+
},
|
|
17
|
+
]
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
exports[`Connection reporting storage > Should delete rows older than specified range 1`] = `
|
|
21
|
+
{
|
|
22
|
+
"sdks": [
|
|
23
|
+
{
|
|
24
|
+
"clients": 1,
|
|
25
|
+
"sdk": "powersync-dart/1.6.4",
|
|
26
|
+
"users": 1,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"clients": 1,
|
|
30
|
+
"sdk": "powersync-js/1.21.1",
|
|
31
|
+
"users": 1,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"clients": 1,
|
|
35
|
+
"sdk": "powersync-js/1.21.2",
|
|
36
|
+
"users": 1,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"clients": 1,
|
|
40
|
+
"sdk": "powersync-js/1.21.4",
|
|
41
|
+
"users": 1,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"clients": 1,
|
|
45
|
+
"sdk": "powersync-js/1.24.5",
|
|
46
|
+
"users": 1,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"clients": 1,
|
|
50
|
+
"sdk": "unknown",
|
|
51
|
+
"users": 1,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
"users": 5,
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
exports[`Connection reporting storage > Should update a connected connection report and make it disconnected 1`] = `
|
|
59
|
+
[
|
|
60
|
+
{
|
|
61
|
+
"client_id": "client_three",
|
|
62
|
+
"sdk": "powersync-js/1.21.2",
|
|
63
|
+
"user_agent": "powersync-js/1.21.0 powersync-web Firefox/141 linux",
|
|
64
|
+
"user_id": "user_three",
|
|
65
|
+
},
|
|
66
|
+
]
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
exports[`Connection reporting storage > Should update a connection report if its within a day 1`] = `
|
|
70
|
+
[
|
|
71
|
+
{
|
|
72
|
+
"client_id": "client_one",
|
|
73
|
+
"sdk": "powersync-dart/1.6.4",
|
|
74
|
+
"user_agent": "powersync-dart/1.6.4 Dart (flutter-web) Chrome/128 android",
|
|
75
|
+
"user_id": "user_one",
|
|
76
|
+
},
|
|
77
|
+
]
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
exports[`Report storage tests > Should show connection report data for user over the past day 1`] = `
|
|
81
|
+
{
|
|
82
|
+
"sdks": [
|
|
83
|
+
{
|
|
84
|
+
"clients": 1,
|
|
85
|
+
"sdk": "powersync-dart/1.6.4",
|
|
86
|
+
"users": 1,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"clients": 1,
|
|
90
|
+
"sdk": "powersync-js/1.21.1",
|
|
91
|
+
"users": 1,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"clients": 1,
|
|
95
|
+
"sdk": "powersync-js/1.21.4",
|
|
96
|
+
"users": 1,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"clients": 1,
|
|
100
|
+
"sdk": "unknown",
|
|
101
|
+
"users": 1,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
"users": 3,
|
|
105
|
+
}
|
|
106
|
+
`;
|
|
107
|
+
|
|
108
|
+
exports[`Report storage tests > Should show connection report data for user over the past month 1`] = `
|
|
109
|
+
{
|
|
110
|
+
"sdks": [
|
|
111
|
+
{
|
|
112
|
+
"clients": 1,
|
|
113
|
+
"sdk": "powersync-dart/1.6.4",
|
|
114
|
+
"users": 1,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"clients": 1,
|
|
118
|
+
"sdk": "powersync-js/1.21.1",
|
|
119
|
+
"users": 1,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"clients": 1,
|
|
123
|
+
"sdk": "powersync-js/1.21.2",
|
|
124
|
+
"users": 1,
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"clients": 1,
|
|
128
|
+
"sdk": "powersync-js/1.21.4",
|
|
129
|
+
"users": 1,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"clients": 1,
|
|
133
|
+
"sdk": "powersync-js/1.23.6",
|
|
134
|
+
"users": 1,
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"clients": 1,
|
|
138
|
+
"sdk": "powersync-js/1.23.7",
|
|
139
|
+
"users": 1,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"clients": 1,
|
|
143
|
+
"sdk": "powersync-js/1.24.5",
|
|
144
|
+
"users": 1,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"clients": 1,
|
|
148
|
+
"sdk": "unknown",
|
|
149
|
+
"users": 1,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
"users": 7,
|
|
153
|
+
}
|
|
154
|
+
`;
|
|
155
|
+
|
|
156
|
+
exports[`Report storage tests > Should show connection report data for user over the past week 1`] = `
|
|
157
|
+
{
|
|
158
|
+
"sdks": [
|
|
159
|
+
{
|
|
160
|
+
"clients": 1,
|
|
161
|
+
"sdk": "powersync-dart/1.6.4",
|
|
162
|
+
"users": 1,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"clients": 1,
|
|
166
|
+
"sdk": "powersync-js/1.21.1",
|
|
167
|
+
"users": 1,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"clients": 1,
|
|
171
|
+
"sdk": "powersync-js/1.21.2",
|
|
172
|
+
"users": 1,
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"clients": 1,
|
|
176
|
+
"sdk": "powersync-js/1.21.4",
|
|
177
|
+
"users": 1,
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"clients": 1,
|
|
181
|
+
"sdk": "powersync-js/1.24.5",
|
|
182
|
+
"users": 1,
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"clients": 1,
|
|
186
|
+
"sdk": "unknown",
|
|
187
|
+
"users": 1,
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
"users": 5,
|
|
191
|
+
}
|
|
192
|
+
`;
|
|
193
|
+
|
|
194
|
+
exports[`Report storage tests > Should show currently connected users 1`] = `
|
|
195
|
+
{
|
|
196
|
+
"sdks": [
|
|
197
|
+
{
|
|
198
|
+
"clients": 1,
|
|
199
|
+
"sdk": "powersync-dart/1.6.4",
|
|
200
|
+
"users": 1,
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"clients": 1,
|
|
204
|
+
"sdk": "powersync-js/1.21.1",
|
|
205
|
+
"users": 1,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"clients": 1,
|
|
209
|
+
"sdk": "unknown",
|
|
210
|
+
"users": 1,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
"users": 2,
|
|
214
|
+
}
|
|
215
|
+
`;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
2
|
+
import { INITIALIZED_MONGO_REPORT_STORAGE_FACTORY } from './util.js';
|
|
3
|
+
import { register, ReportUserData } from '@powersync/service-core-tests';
|
|
4
|
+
import { event_types } from '@powersync/service-types';
|
|
5
|
+
import { MongoReportStorage } from '@module/storage/MongoReportStorage.js';
|
|
6
|
+
|
|
7
|
+
const userData = register.REPORT_TEST_USERS;
|
|
8
|
+
const dates = register.REPORT_TEST_DATES;
|
|
9
|
+
const factory = await INITIALIZED_MONGO_REPORT_STORAGE_FACTORY();
|
|
10
|
+
|
|
11
|
+
function removeVolatileFields(
|
|
12
|
+
connections: event_types.ClientConnection[]
|
|
13
|
+
): Partial<event_types.ClientConnection & { _id: string }>[] {
|
|
14
|
+
return connections.map((sdk: Partial<event_types.ClientConnection & { _id: string }>) => {
|
|
15
|
+
const { _id, disconnected_at, connected_at, jwt_exp, ...rest } = sdk;
|
|
16
|
+
return {
|
|
17
|
+
...rest
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function loadData(data: ReportUserData, factory: MongoReportStorage) {
|
|
23
|
+
await factory.db.connection_report_events.insertMany(Object.values(data));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function deleteData(factory: MongoReportStorage) {
|
|
27
|
+
await factory.db.connection_report_events.deleteMany();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
beforeAll(async () => {
|
|
31
|
+
await loadData(userData, factory);
|
|
32
|
+
});
|
|
33
|
+
afterAll(async () => {
|
|
34
|
+
await deleteData(factory);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('Report storage tests', async () => {
|
|
38
|
+
await register.registerReportTests(factory);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('Connection reporting storage', async () => {
|
|
42
|
+
it('Should create a connection report if its after a day', async () => {
|
|
43
|
+
const newConnectAt = new Date(
|
|
44
|
+
dates.now.getFullYear(),
|
|
45
|
+
dates.now.getMonth(),
|
|
46
|
+
dates.now.getDate() + 1,
|
|
47
|
+
dates.now.getHours()
|
|
48
|
+
);
|
|
49
|
+
const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
|
|
50
|
+
|
|
51
|
+
await factory.reportClientConnection({
|
|
52
|
+
sdk: userData.user_week.sdk,
|
|
53
|
+
connected_at: newConnectAt,
|
|
54
|
+
jwt_exp: jwtExp,
|
|
55
|
+
client_id: userData.user_week.client_id,
|
|
56
|
+
user_id: userData.user_week.user_id,
|
|
57
|
+
user_agent: userData.user_week.user_agent
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const connection = await factory.db.connection_report_events.find({ user_id: userData.user_week.user_id }).toArray();
|
|
61
|
+
expect(connection).toHaveLength(2);
|
|
62
|
+
const cleaned = removeVolatileFields(connection);
|
|
63
|
+
expect(cleaned).toMatchSnapshot();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('Should update a connection report if its within a day', async () => {
|
|
67
|
+
const newConnectAt = new Date(
|
|
68
|
+
dates.now.getFullYear(),
|
|
69
|
+
dates.now.getMonth(),
|
|
70
|
+
dates.now.getDate(),
|
|
71
|
+
dates.now.getHours(),
|
|
72
|
+
dates.now.getMinutes() + 20
|
|
73
|
+
);
|
|
74
|
+
const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
|
|
75
|
+
await factory.reportClientConnection({
|
|
76
|
+
sdk: userData.user_one.sdk,
|
|
77
|
+
connected_at: newConnectAt,
|
|
78
|
+
jwt_exp: jwtExp,
|
|
79
|
+
client_id: userData.user_one.client_id,
|
|
80
|
+
user_id: userData.user_one.user_id,
|
|
81
|
+
user_agent: userData.user_one.user_agent
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const connection = await factory.db.connection_report_events
|
|
85
|
+
.find({ user_id: userData.user_one.user_id, client_id: userData.user_one.client_id })
|
|
86
|
+
.toArray();
|
|
87
|
+
expect(connection).toHaveLength(1);
|
|
88
|
+
expect(new Date(connection[0].connected_at)).toEqual(newConnectAt);
|
|
89
|
+
expect(new Date(connection[0].jwt_exp!)).toEqual(jwtExp);
|
|
90
|
+
expect(connection[0].disconnected_at).toBeUndefined();
|
|
91
|
+
const cleaned = removeVolatileFields(connection);
|
|
92
|
+
expect(cleaned).toMatchSnapshot();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('Should update a connected connection report and make it disconnected', async () => {
|
|
96
|
+
const disconnectAt = new Date(
|
|
97
|
+
dates.now.getFullYear(),
|
|
98
|
+
dates.now.getMonth(),
|
|
99
|
+
dates.now.getDate(),
|
|
100
|
+
dates.now.getHours(),
|
|
101
|
+
dates.now.getMinutes() + 20
|
|
102
|
+
);
|
|
103
|
+
const jwtExp = new Date(disconnectAt.getFullYear(), disconnectAt.getMonth(), disconnectAt.getDate() + 1);
|
|
104
|
+
|
|
105
|
+
await factory.reportClientDisconnection({
|
|
106
|
+
disconnected_at: disconnectAt,
|
|
107
|
+
jwt_exp: jwtExp,
|
|
108
|
+
client_id: userData.user_three.client_id,
|
|
109
|
+
user_id: userData.user_three.user_id,
|
|
110
|
+
user_agent: userData.user_three.user_agent,
|
|
111
|
+
connected_at: userData.user_three.connected_at
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const connection = await factory.db.connection_report_events.find({ user_id: userData.user_three.user_id }).toArray();
|
|
115
|
+
expect(connection).toHaveLength(1);
|
|
116
|
+
expect(new Date(connection[0].disconnected_at!)).toEqual(disconnectAt);
|
|
117
|
+
const cleaned = removeVolatileFields(connection);
|
|
118
|
+
expect(cleaned).toMatchSnapshot();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('Should delete rows older than specified range', async () => {
|
|
122
|
+
await deleteData(factory);
|
|
123
|
+
await loadData(userData, factory);
|
|
124
|
+
await factory.deleteOldConnectionData({
|
|
125
|
+
date: dates.weekAgo
|
|
126
|
+
});
|
|
127
|
+
const connection = await factory.getClientConnectionReports({
|
|
128
|
+
start: dates.monthAgo,
|
|
129
|
+
end: dates.now
|
|
130
|
+
});
|
|
131
|
+
expect(connection).toMatchSnapshot();
|
|
132
|
+
});
|
|
133
|
+
});
|
package/test/src/storage.test.ts
CHANGED
|
@@ -2,8 +2,7 @@ import { register } from '@powersync/service-core-tests';
|
|
|
2
2
|
import { describe } from 'vitest';
|
|
3
3
|
import { INITIALIZED_MONGO_STORAGE_FACTORY } from './util.js';
|
|
4
4
|
import { env } from './env.js';
|
|
5
|
-
import {
|
|
6
|
-
import { MongoChecksumOptions } from '@module/storage/implementation/MongoChecksums.js';
|
|
5
|
+
import { mongoTestStorageFactoryGenerator } from '@module/utils/test-utils.js';
|
|
7
6
|
|
|
8
7
|
describe('Mongo Sync Bucket Storage - Parameters', () =>
|
|
9
8
|
register.registerDataStorageParameterTests(INITIALIZED_MONGO_STORAGE_FACTORY));
|
|
@@ -18,7 +17,7 @@ describe('Sync Bucket Validation', register.registerBucketValidationTests);
|
|
|
18
17
|
|
|
19
18
|
describe('Mongo Sync Bucket Storage - split operations', () =>
|
|
20
19
|
register.registerDataStorageDataTests(
|
|
21
|
-
|
|
20
|
+
mongoTestStorageFactoryGenerator({
|
|
22
21
|
url: env.MONGO_TEST_URL,
|
|
23
22
|
isCI: env.CI,
|
|
24
23
|
internalOptions: {
|
|
@@ -32,7 +31,7 @@ describe('Mongo Sync Bucket Storage - split operations', () =>
|
|
|
32
31
|
|
|
33
32
|
describe('Mongo Sync Bucket Storage - split buckets', () =>
|
|
34
33
|
register.registerDataStorageDataTests(
|
|
35
|
-
|
|
34
|
+
mongoTestStorageFactoryGenerator({
|
|
36
35
|
url: env.MONGO_TEST_URL,
|
|
37
36
|
isCI: env.CI,
|
|
38
37
|
internalOptions: {
|
|
@@ -43,50 +42,3 @@ describe('Mongo Sync Bucket Storage - split buckets', () =>
|
|
|
43
42
|
}
|
|
44
43
|
})
|
|
45
44
|
));
|
|
46
|
-
|
|
47
|
-
describe('Mongo Sync Bucket Storage - checksum calculations', () => {
|
|
48
|
-
// This test tests 4 buckets x 4 operations in each.
|
|
49
|
-
// We specifically use operationBatchLimit that does not have factors in common with 4,
|
|
50
|
-
// as well some that do.
|
|
51
|
-
const params: MongoChecksumOptions[] = [
|
|
52
|
-
{
|
|
53
|
-
bucketBatchLimit: 100,
|
|
54
|
-
operationBatchLimit: 3
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
{
|
|
58
|
-
bucketBatchLimit: 10,
|
|
59
|
-
operationBatchLimit: 7
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
{
|
|
63
|
-
bucketBatchLimit: 3,
|
|
64
|
-
operationBatchLimit: 1
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
bucketBatchLimit: 1,
|
|
68
|
-
operationBatchLimit: 3
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
bucketBatchLimit: 2,
|
|
72
|
-
operationBatchLimit: 4
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
bucketBatchLimit: 4,
|
|
76
|
-
operationBatchLimit: 12
|
|
77
|
-
}
|
|
78
|
-
];
|
|
79
|
-
for (let options of params) {
|
|
80
|
-
describe(`${options.bucketBatchLimit}|${options.operationBatchLimit}`, () => {
|
|
81
|
-
register.testChecksumBatching(
|
|
82
|
-
MongoTestStorageFactoryGenerator({
|
|
83
|
-
url: env.MONGO_TEST_URL,
|
|
84
|
-
isCI: env.CI,
|
|
85
|
-
internalOptions: {
|
|
86
|
-
checksumOptions: options
|
|
87
|
-
}
|
|
88
|
-
})
|
|
89
|
-
);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
});
|
|
@@ -97,10 +97,25 @@ bucket_definitions:
|
|
|
97
97
|
await populate(bucketStorage);
|
|
98
98
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
// Default is to small small numbers - should be a no-op
|
|
101
|
+
const result0 = await bucketStorage.populatePersistentChecksumCache({
|
|
102
|
+
maxOpId: checkpoint
|
|
103
|
+
});
|
|
104
|
+
expect(result0.buckets).toEqual(0);
|
|
105
|
+
|
|
106
|
+
// This should cache the checksums for the two buckets
|
|
107
|
+
const result1 = await bucketStorage.populatePersistentChecksumCache({
|
|
108
|
+
maxOpId: checkpoint,
|
|
109
|
+
minBucketChanges: 1
|
|
110
|
+
});
|
|
111
|
+
expect(result1.buckets).toEqual(2);
|
|
112
|
+
|
|
113
|
+
// This should be a no-op, as the checksums are already cached
|
|
114
|
+
const result2 = await bucketStorage.populatePersistentChecksumCache({
|
|
101
115
|
maxOpId: checkpoint,
|
|
102
|
-
|
|
116
|
+
minBucketChanges: 1
|
|
103
117
|
});
|
|
118
|
+
expect(result2.buckets).toEqual(0);
|
|
104
119
|
|
|
105
120
|
const checksumAfter = await bucketStorage.getChecksums(checkpoint, ['by_user2["u1"]', 'by_user2["u2"]']);
|
|
106
121
|
expect(checksumAfter.get('by_user2["u1"]')).toEqual({
|
package/test/src/util.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { env } from './env.js';
|
|
2
|
+
import { mongoTestReportStorageFactoryGenerator, mongoTestStorageFactoryGenerator } from '@module/utils/test-utils.js';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
export const INITIALIZED_MONGO_STORAGE_FACTORY = mongoTestStorageFactoryGenerator({
|
|
5
|
+
url: env.MONGO_TEST_URL,
|
|
6
|
+
isCI: env.CI
|
|
7
|
+
});
|
|
4
8
|
|
|
5
|
-
export const
|
|
9
|
+
export const INITIALIZED_MONGO_REPORT_STORAGE_FACTORY = mongoTestReportStorageFactoryGenerator({
|
|
6
10
|
url: env.MONGO_TEST_URL,
|
|
7
11
|
isCI: env.CI
|
|
8
12
|
});
|