@powersync/service-module-postgres-storage 0.0.0-dev-20250825132649 → 0.0.0-dev-20250827091123

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.
@@ -1,6 +1,13 @@
1
1
  import { afterAll, beforeAll, describe, expect, it } from 'vitest';
2
2
  import { POSTGRES_REPORT_STORAGE_FACTORY } from './util.js';
3
3
  import { event_types } from '@powersync/service-types';
4
+ import { register, ReportUserData } from '@powersync/service-core-tests';
5
+ import { PostgresReportStorageFactory } from '../../src/storage/PostgresReportStorageFactory.js';
6
+ import { DateTimeValue } from '@powersync/service-sync-rules';
7
+
8
+ const factory = await POSTGRES_REPORT_STORAGE_FACTORY();
9
+ const userData = register.REPORT_TEST_USERS;
10
+ const dates = register.REPORT_TEST_DATES;
4
11
 
5
12
  function removeVolatileFields(sdks: event_types.ClientConnection[]): Partial<event_types.ClientConnection>[] {
6
13
  return sdks.map((sdk) => {
@@ -11,107 +18,8 @@ function removeVolatileFields(sdks: event_types.ClientConnection[]): Partial<eve
11
18
  });
12
19
  }
13
20
 
14
- describe('Connection report storage', async () => {
15
- const factory = await POSTGRES_REPORT_STORAGE_FACTORY();
16
- const now = new Date();
17
- const nowAdd5minutes = new Date(
18
- now.getFullYear(),
19
- now.getMonth(),
20
- now.getDate(),
21
- now.getHours(),
22
- now.getMinutes() + 5
23
- );
24
- const nowLess5minutes = new Date(
25
- now.getFullYear(),
26
- now.getMonth(),
27
- now.getDate(),
28
- now.getHours(),
29
- now.getMinutes() - 5
30
- );
31
- const dayAgo = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1, now.getHours());
32
- const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
33
- const weekAgo = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7);
34
- const monthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());
35
- const user_one = {
36
- user_id: 'user_one',
37
- client_id: 'client_one',
38
- connected_at: now.toISOString(),
39
- sdk: 'powersync-dart/1.6.4',
40
- user_agent: 'powersync-dart/1.6.4 Dart (flutter-web) Chrome/128 android',
41
- jwt_exp: nowAdd5minutes.toISOString(),
42
- id: '1'
43
- };
44
- const user_two = {
45
- user_id: 'user_two',
46
- client_id: 'client_two',
47
- connected_at: nowLess5minutes.toISOString(),
48
- sdk: 'powersync-js/1.21.1',
49
- user_agent: 'powersync-js/1.21.0 powersync-web Chromium/138 linux',
50
- jwt_exp: nowAdd5minutes.toISOString(),
51
- id: '2'
52
- };
53
- const user_three = {
54
- user_id: 'user_three',
55
- client_id: 'client_three',
56
- connected_at: yesterday.toISOString(),
57
- sdk: 'powersync-js/1.21.2',
58
- user_agent: 'powersync-js/1.21.0 powersync-web Firefox/141 linux',
59
- disconnected_at: yesterday.toISOString(),
60
- id: '3'
61
- };
62
-
63
- const user_four = {
64
- user_id: 'user_four',
65
- client_id: 'client_four',
66
- connected_at: now.toISOString(),
67
- sdk: 'powersync-js/1.21.4',
68
- user_agent: 'powersync-js/1.21.0 powersync-web Firefox/141 linux',
69
- jwt_exp: nowLess5minutes.toISOString(),
70
- id: '4'
71
- };
72
-
73
- const user_old = {
74
- user_id: 'user_one',
75
- client_id: '',
76
- connected_at: now.toISOString(),
77
- sdk: 'unknown',
78
- user_agent: 'powersync-dart/1.6.4 Dart (flutter-web) Chrome/128 android',
79
- jwt_exp: nowAdd5minutes.toISOString(),
80
- id: '5'
81
- };
82
-
83
- const user_week = {
84
- user_id: 'user_week',
85
- client_id: 'client_week',
86
- connected_at: weekAgo.toISOString(),
87
- sdk: 'powersync-js/1.24.5',
88
- user_agent: 'powersync-js/1.21.0 powersync-web Firefox/141 linux',
89
- disconnected_at: weekAgo.toISOString(),
90
- id: 'week'
91
- };
92
-
93
- const user_month = {
94
- user_id: 'user_month',
95
- client_id: 'client_month',
96
- connected_at: monthAgo.toISOString(),
97
- sdk: 'powersync-js/1.23.6',
98
- user_agent: 'powersync-js/1.23.0 powersync-web Firefox/141 linux',
99
- disconnected_at: monthAgo.toISOString(),
100
- id: 'month'
101
- };
102
-
103
- const user_expired = {
104
- user_id: 'user_expired',
105
- client_id: 'client_expired',
106
- connected_at: monthAgo.toISOString(),
107
- sdk: 'powersync-js/1.23.7',
108
- user_agent: 'powersync-js/1.23.0 powersync-web Firefox/141 linux',
109
- jwt_exp: monthAgo.toISOString(),
110
- id: 'expired'
111
- };
112
-
113
- async function loadData() {
114
- await factory.db.sql`
21
+ async function loadData(userData: ReportUserData, factory: PostgresReportStorageFactory) {
22
+ await factory.db.sql`
115
23
  INSERT INTO
116
24
  connection_report_events (
117
25
  user_id,
@@ -125,148 +33,131 @@ describe('Connection report storage', async () => {
125
33
  )
126
34
  VALUES
127
35
  (
128
- ${{ type: 'varchar', value: user_one.user_id }},
129
- ${{ type: 'varchar', value: user_one.client_id }},
130
- ${{ type: 1184, value: user_one.connected_at }},
131
- ${{ type: 'varchar', value: user_one.sdk }},
132
- ${{ type: 'varchar', value: user_one.user_agent }},
133
- ${{ type: 1184, value: user_one.jwt_exp }},
134
- ${{ type: 'varchar', value: user_one.id }},
36
+ ${{ type: 'varchar', value: userData.user_one.user_id }},
37
+ ${{ type: 'varchar', value: userData.user_one.client_id }},
38
+ ${{ type: 1184, value: userData.user_one.connected_at.toISOString() }},
39
+ ${{ type: 'varchar', value: userData.user_one.sdk }},
40
+ ${{ type: 'varchar', value: userData.user_one.user_agent }},
41
+ ${{ type: 1184, value: userData.user_one.jwt_exp.toISOString() }},
42
+ ${{ type: 'varchar', value: '1' }},
135
43
  NULL
136
44
  ),
137
45
  (
138
- ${{ type: 'varchar', value: user_two.user_id }},
139
- ${{ type: 'varchar', value: user_two.client_id }},
140
- ${{ type: 1184, value: user_two.connected_at }},
141
- ${{ type: 'varchar', value: user_two.sdk }},
142
- ${{ type: 'varchar', value: user_two.user_agent }},
143
- ${{ type: 1184, value: user_two.jwt_exp }},
144
- ${{ type: 'varchar', value: user_two.id }},
46
+ ${{ type: 'varchar', value: userData.user_two.user_id }},
47
+ ${{ type: 'varchar', value: userData.user_two.client_id }},
48
+ ${{ type: 1184, value: userData.user_two.connected_at.toISOString() }},
49
+ ${{ type: 'varchar', value: userData.user_two.sdk }},
50
+ ${{ type: 'varchar', value: userData.user_two.user_agent }},
51
+ ${{ type: 1184, value: userData.user_two.jwt_exp.toISOString() }},
52
+ ${{ type: 'varchar', value: '2' }},
145
53
  NULL
146
54
  ),
147
55
  (
148
- ${{ type: 'varchar', value: user_four.user_id }},
149
- ${{ type: 'varchar', value: user_four.client_id }},
150
- ${{ type: 1184, value: user_four.connected_at }},
151
- ${{ type: 'varchar', value: user_four.sdk }},
152
- ${{ type: 'varchar', value: user_four.user_agent }},
153
- ${{ type: 1184, value: user_four.jwt_exp }},
154
- ${{ type: 'varchar', value: user_four.id }},
56
+ ${{ type: 'varchar', value: userData.user_four.user_id }},
57
+ ${{ type: 'varchar', value: userData.user_four.client_id }},
58
+ ${{ type: 1184, value: userData.user_four.connected_at.toISOString() }},
59
+ ${{ type: 'varchar', value: userData.user_four.sdk }},
60
+ ${{ type: 'varchar', value: userData.user_four.user_agent }},
61
+ ${{ type: 1184, value: userData.user_four.jwt_exp.toISOString() }},
62
+ ${{ type: 'varchar', value: '4' }},
155
63
  NULL
156
64
  ),
157
65
  (
158
- ${{ type: 'varchar', value: user_old.user_id }},
159
- ${{ type: 'varchar', value: user_old.client_id }},
160
- ${{ type: 1184, value: user_old.connected_at }},
161
- ${{ type: 'varchar', value: user_old.sdk }},
162
- ${{ type: 'varchar', value: user_old.user_agent }},
163
- ${{ type: 1184, value: user_old.jwt_exp }},
164
- ${{ type: 'varchar', value: user_old.id }},
66
+ ${{ type: 'varchar', value: userData.user_old.user_id }},
67
+ ${{ type: 'varchar', value: userData.user_old.client_id }},
68
+ ${{ type: 1184, value: userData.user_old.connected_at.toISOString() }},
69
+ ${{ type: 'varchar', value: userData.user_old.sdk }},
70
+ ${{ type: 'varchar', value: userData.user_old.user_agent }},
71
+ ${{ type: 1184, value: userData.user_old.jwt_exp.toISOString() }},
72
+ ${{ type: 'varchar', value: '5' }},
165
73
  NULL
166
74
  ),
167
75
  (
168
- ${{ type: 'varchar', value: user_three.user_id }},
169
- ${{ type: 'varchar', value: user_three.client_id }},
170
- ${{ type: 1184, value: user_three.connected_at }},
171
- ${{ type: 'varchar', value: user_three.sdk }},
172
- ${{ type: 'varchar', value: user_three.user_agent }},
76
+ ${{ type: 'varchar', value: userData.user_three.user_id }},
77
+ ${{ type: 'varchar', value: userData.user_three.client_id }},
78
+ ${{ type: 1184, value: userData.user_three.connected_at.toISOString() }},
79
+ ${{ type: 'varchar', value: userData.user_three.sdk }},
80
+ ${{ type: 'varchar', value: userData.user_three.user_agent }},
173
81
  NULL,
174
- ${{ type: 'varchar', value: user_three.id }},
175
- ${{ type: 1184, value: user_three.disconnected_at }}
82
+ ${{ type: 'varchar', value: '3' }},
83
+ ${{ type: 1184, value: userData.user_three.disconnected_at.toISOString() }}
176
84
  ),
177
85
  (
178
- ${{ type: 'varchar', value: user_week.user_id }},
179
- ${{ type: 'varchar', value: user_week.client_id }},
180
- ${{ type: 1184, value: user_week.connected_at }},
181
- ${{ type: 'varchar', value: user_week.sdk }},
182
- ${{ type: 'varchar', value: user_week.user_agent }},
86
+ ${{ type: 'varchar', value: userData.user_week.user_id }},
87
+ ${{ type: 'varchar', value: userData.user_week.client_id }},
88
+ ${{ type: 1184, value: userData.user_week.connected_at.toISOString() }},
89
+ ${{ type: 'varchar', value: userData.user_week.sdk }},
90
+ ${{ type: 'varchar', value: userData.user_week.user_agent }},
183
91
  NULL,
184
- ${{ type: 'varchar', value: user_week.id }},
185
- ${{ type: 1184, value: user_week.disconnected_at }}
92
+ ${{ type: 'varchar', value: 'week' }},
93
+ ${{ type: 1184, value: userData.user_week.disconnected_at.toISOString() }}
186
94
  ),
187
95
  (
188
- ${{ type: 'varchar', value: user_month.user_id }},
189
- ${{ type: 'varchar', value: user_month.client_id }},
190
- ${{ type: 1184, value: user_month.connected_at }},
191
- ${{ type: 'varchar', value: user_month.sdk }},
192
- ${{ type: 'varchar', value: user_month.user_agent }},
96
+ ${{ type: 'varchar', value: userData.user_month.user_id }},
97
+ ${{ type: 'varchar', value: userData.user_month.client_id }},
98
+ ${{ type: 1184, value: userData.user_month.connected_at.toISOString() }},
99
+ ${{ type: 'varchar', value: userData.user_month.sdk }},
100
+ ${{ type: 'varchar', value: userData.user_month.user_agent }},
193
101
  NULL,
194
- ${{ type: 'varchar', value: user_month.id }},
195
- ${{ type: 1184, value: user_month.disconnected_at }}
102
+ ${{ type: 'varchar', value: 'month' }},
103
+ ${{ type: 1184, value: userData.user_month.disconnected_at.toISOString() }}
196
104
  ),
197
105
  (
198
- ${{ type: 'varchar', value: user_expired.user_id }},
199
- ${{ type: 'varchar', value: user_expired.client_id }},
200
- ${{ type: 1184, value: user_expired.connected_at }},
201
- ${{ type: 'varchar', value: user_expired.sdk }},
202
- ${{ type: 'varchar', value: user_expired.user_agent }},
203
- ${{ type: 1184, value: user_expired.jwt_exp }},
204
- ${{ type: 'varchar', value: user_expired.id }},
106
+ ${{ type: 'varchar', value: userData.user_expired.user_id }},
107
+ ${{ type: 'varchar', value: userData.user_expired.client_id }},
108
+ ${{ type: 1184, value: userData.user_expired.connected_at.toISOString() }},
109
+ ${{ type: 'varchar', value: userData.user_expired.sdk }},
110
+ ${{ type: 'varchar', value: userData.user_expired.user_agent }},
111
+ ${{ type: 1184, value: userData.user_expired.jwt_exp.toISOString() }},
112
+ ${{ type: 'varchar', value: 'expired' }},
205
113
  NULL
206
114
  )
207
115
  `.execute();
208
- }
116
+ }
209
117
 
210
- function deleteData() {
211
- return factory.db.sql`TRUNCATE TABLE connection_report_events`.execute();
212
- }
118
+ async function deleteData(factory: PostgresReportStorageFactory) {
119
+ await factory.db.sql`TRUNCATE TABLE connection_report_events`.execute();
120
+ }
213
121
 
214
- beforeAll(async () => {
215
- await loadData();
216
- });
122
+ beforeAll(async () => {
123
+ await loadData(userData, factory);
124
+ });
125
+ afterAll(async () => {
126
+ await deleteData(factory);
127
+ });
217
128
 
218
- afterAll(async () => {
219
- await deleteData();
220
- });
221
- it('Should show connected users with start range', async () => {
222
- const current = await factory.getConnectedClients();
223
- expect(current).toMatchSnapshot();
224
- });
225
- it('Should show connection report data for user over the past month', async () => {
226
- const sdk = await factory.getClientConnectionReports({
227
- start: monthAgo,
228
- end: now
229
- });
230
- expect(sdk).toMatchSnapshot();
231
- });
232
- it('Should show connection report data for user over the past week', async () => {
233
- const sdk = await factory.getClientConnectionReports({
234
- start: weekAgo,
235
- end: now
236
- });
237
- expect(sdk).toMatchSnapshot();
238
- });
239
- it('Should show connection report data for user over the past day', async () => {
240
- const sdk = await factory.getClientConnectionReports({
241
- start: dayAgo,
242
- end: now
243
- });
244
- expect(sdk).toMatchSnapshot();
245
- });
129
+ describe('Report storage tests', async () => {
130
+ await register.registerReportTests(factory);
131
+ });
246
132
 
133
+ describe('Connection report storage', async () => {
247
134
  it('Should update a connection event if its within a day', async () => {
248
135
  const newConnectAt = new Date(
249
- now.getFullYear(),
250
- now.getMonth(),
251
- now.getDate(),
252
- now.getHours(),
253
- now.getMinutes() + 20
136
+ dates.now.getFullYear(),
137
+ dates.now.getMonth(),
138
+ dates.now.getDate(),
139
+ dates.now.getHours(),
140
+ dates.now.getMinutes() + 20
254
141
  );
255
142
  const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
256
143
  await factory.reportClientConnection({
257
- sdk: user_one.sdk,
144
+ sdk: userData.user_one.sdk,
258
145
  connected_at: newConnectAt,
259
146
  jwt_exp: jwtExp,
260
- client_id: user_one.client_id,
261
- user_id: user_one.user_id,
262
- user_agent: user_one.user_agent
147
+ client_id: userData.user_one.client_id,
148
+ user_id: userData.user_one.user_id,
149
+ user_agent: userData.user_one.user_agent
263
150
  });
264
151
 
265
152
  const sdk = await factory.db
266
- .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: user_one.user_id }} AND client_id = ${{ type: 'varchar', value: user_one.client_id }}`.rows<event_types.ClientConnection>();
153
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: userData.user_one.user_id }} AND client_id = ${{ type: 'varchar', value: userData.user_one.client_id }}`.rows<event_types.ClientConnection>();
267
154
  expect(sdk).toHaveLength(1);
268
- expect(new Date(sdk[0].connected_at).toISOString()).toEqual(newConnectAt.toISOString());
269
- expect(new Date(sdk[0].jwt_exp!).toISOString()).toEqual(jwtExp.toISOString());
155
+ expect(new Date((sdk[0].connected_at as unknown as DateTimeValue).iso8601Representation).toISOString()).toEqual(
156
+ newConnectAt.toISOString()
157
+ );
158
+ expect(new Date((sdk[0].jwt_exp! as unknown as DateTimeValue).iso8601Representation).toISOString()).toEqual(
159
+ jwtExp.toISOString()
160
+ );
270
161
  expect(sdk[0].disconnected_at).toBeNull();
271
162
  const cleaned = removeVolatileFields(sdk);
272
163
  expect(cleaned).toMatchSnapshot();
@@ -274,60 +165,68 @@ describe('Connection report storage', async () => {
274
165
 
275
166
  it('Should update a connection event and make it disconnected', async () => {
276
167
  const disconnectAt = new Date(
277
- now.getFullYear(),
278
- now.getMonth(),
279
- now.getDate(),
280
- now.getHours(),
281
- now.getMinutes() + 20
168
+ dates.now.getFullYear(),
169
+ dates.now.getMonth(),
170
+ dates.now.getDate(),
171
+ dates.now.getHours(),
172
+ dates.now.getMinutes() + 20
282
173
  );
283
174
  const jwtExp = new Date(disconnectAt.getFullYear(), disconnectAt.getMonth(), disconnectAt.getDate() + 1);
284
175
 
285
176
  await factory.reportClientDisconnection({
286
177
  disconnected_at: disconnectAt,
287
178
  jwt_exp: jwtExp,
288
- client_id: user_three.client_id,
289
- user_id: user_three.user_id,
290
- user_agent: user_three.user_agent,
291
- connected_at: yesterday
179
+ client_id: userData.user_three.client_id,
180
+ user_id: userData.user_three.user_id,
181
+ user_agent: userData.user_three.user_agent,
182
+ connected_at: dates.yesterday
292
183
  });
293
184
 
294
185
  const sdk = await factory.db
295
- .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: user_three.user_id }}`.rows<event_types.ClientConnection>();
186
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: userData.user_three.user_id }}`.rows<event_types.ClientConnection>();
296
187
  expect(sdk).toHaveLength(1);
297
- expect(new Date(sdk[0].disconnected_at!).toISOString()).toEqual(disconnectAt.toISOString());
188
+ console.log(sdk[0]);
189
+ expect(new Date((sdk[0].disconnected_at! as unknown as DateTimeValue).iso8601Representation).toISOString()).toEqual(
190
+ disconnectAt.toISOString()
191
+ );
298
192
  const cleaned = removeVolatileFields(sdk);
299
193
  expect(cleaned).toMatchSnapshot();
300
194
  });
301
195
 
302
196
  it('Should create a connection event if its after a day', async () => {
303
- const newConnectAt = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, now.getHours());
197
+ const newConnectAt = new Date(
198
+ dates.now.getFullYear(),
199
+ dates.now.getMonth(),
200
+ dates.now.getDate() + 1,
201
+ dates.now.getHours()
202
+ );
304
203
  const jwtExp = new Date(newConnectAt.getFullYear(), newConnectAt.getMonth(), newConnectAt.getDate() + 1);
305
204
 
306
205
  await factory.reportClientConnection({
307
- sdk: user_week.sdk,
206
+ sdk: userData.user_week.sdk,
308
207
  connected_at: newConnectAt,
309
208
  jwt_exp: jwtExp,
310
- client_id: user_week.client_id,
311
- user_id: user_week.user_id,
312
- user_agent: user_week.user_agent
209
+ client_id: userData.user_week.client_id,
210
+ user_id: userData.user_week.user_id,
211
+ user_agent: userData.user_week.user_agent
313
212
  });
314
213
 
315
214
  const sdk = await factory.db
316
- .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: user_week.user_id }}`.rows<event_types.ClientConnection>();
215
+ .sql`SELECT * FROM connection_report_events WHERE user_id = ${{ type: 'varchar', value: userData.user_week.user_id }}`.rows<event_types.ClientConnection>();
317
216
  expect(sdk).toHaveLength(2);
318
217
  const cleaned = removeVolatileFields(sdk);
319
218
  expect(cleaned).toMatchSnapshot();
320
219
  });
321
220
 
322
221
  it('Should delete rows older than specified range', async () => {
323
- await deleteData();
324
- await loadData();
222
+ await deleteData(factory);
223
+ await loadData(userData, factory);
325
224
  await factory.deleteOldConnectionData({
326
- date: weekAgo
225
+ date: dates.weekAgo
327
226
  });
328
227
  const sdk = await factory.getClientConnectionReports({
329
- start: monthAgo,
330
- end: now
228
+ start: dates.monthAgo,
229
+ end: dates.now
331
230
  });
332
231
  expect(sdk).toMatchSnapshot();
333
232
  });