|
1 | 1 | import { MongoRouteAPIAdapter } from '@module/api/MongoRouteAPIAdapter.js'; |
2 | 2 | import { ChangeStream } from '@module/replication/ChangeStream.js'; |
3 | 3 | import { constructAfterRecord } from '@module/replication/MongoRelation.js'; |
4 | | -import { SqliteRow } from '@powersync/service-sync-rules'; |
| 4 | +import { SqliteRow, SqlSyncRules } from '@powersync/service-sync-rules'; |
5 | 5 | import * as mongo from 'mongodb'; |
6 | 6 | import { describe, expect, test } from 'vitest'; |
7 | 7 | import { clearTestDb, connectMongoData, TEST_CONNECTION_OPTIONS } from './util.js'; |
| 8 | +import { PostImagesOption } from '@module/types/types.js'; |
8 | 9 |
|
9 | 10 | describe('mongo data types', () => { |
10 | 11 | async function setupTable(db: mongo.Db) { |
@@ -245,58 +246,191 @@ describe('mongo data types', () => { |
245 | 246 | }); |
246 | 247 |
|
247 | 248 | test('connection schema', async () => { |
248 | | - const adapter = new MongoRouteAPIAdapter({ |
| 249 | + await using adapter = new MongoRouteAPIAdapter({ |
249 | 250 | type: 'mongodb', |
250 | 251 | ...TEST_CONNECTION_OPTIONS |
251 | 252 | }); |
252 | | - try { |
253 | | - const db = adapter.db; |
254 | | - await clearTestDb(db); |
| 253 | + const db = adapter.db; |
| 254 | + await clearTestDb(db); |
255 | 255 |
|
256 | | - const collection = db.collection('test_data'); |
257 | | - await setupTable(db); |
258 | | - await insert(collection); |
| 256 | + const collection = db.collection('test_data'); |
| 257 | + await setupTable(db); |
| 258 | + await insert(collection); |
| 259 | + |
| 260 | + const schema = await adapter.getConnectionSchema(); |
| 261 | + const dbSchema = schema.filter((s) => s.name == TEST_CONNECTION_OPTIONS.database)[0]; |
| 262 | + expect(dbSchema).not.toBeNull(); |
| 263 | + expect(dbSchema.tables).toMatchObject([ |
| 264 | + { |
| 265 | + name: 'test_data', |
| 266 | + columns: [ |
| 267 | + { name: '_id', sqlite_type: 4, internal_type: 'Integer' }, |
| 268 | + { name: 'bool', sqlite_type: 4, internal_type: 'Boolean' }, |
| 269 | + { name: 'bytea', sqlite_type: 1, internal_type: 'Binary' }, |
| 270 | + { name: 'date', sqlite_type: 2, internal_type: 'Date' }, |
| 271 | + { name: 'decimal', sqlite_type: 2, internal_type: 'Decimal' }, |
| 272 | + { name: 'float', sqlite_type: 8, internal_type: 'Double' }, |
| 273 | + { name: 'int2', sqlite_type: 4, internal_type: 'Integer' }, |
| 274 | + { name: 'int4', sqlite_type: 4, internal_type: 'Integer' }, |
| 275 | + { name: 'int8', sqlite_type: 4, internal_type: 'Long' }, |
| 276 | + // We can fix these later |
| 277 | + { name: 'js', sqlite_type: 2, internal_type: 'Object' }, |
| 278 | + { name: 'js2', sqlite_type: 2, internal_type: 'Object' }, |
| 279 | + { name: 'maxKey', sqlite_type: 0, internal_type: 'MaxKey' }, |
| 280 | + { name: 'minKey', sqlite_type: 0, internal_type: 'MinKey' }, |
| 281 | + { name: 'nested', sqlite_type: 2, internal_type: 'Object' }, |
| 282 | + { name: 'null', sqlite_type: 0, internal_type: 'Null' }, |
| 283 | + { name: 'objectId', sqlite_type: 2, internal_type: 'ObjectId' }, |
| 284 | + // We can fix these later |
| 285 | + { name: 'pointer', sqlite_type: 2, internal_type: 'Object' }, |
| 286 | + { name: 'pointer2', sqlite_type: 2, internal_type: 'Object' }, |
| 287 | + { name: 'regexp', sqlite_type: 2, internal_type: 'RegExp' }, |
| 288 | + // Can fix this later |
| 289 | + { name: 'symbol', sqlite_type: 2, internal_type: 'String' }, |
| 290 | + { name: 'text', sqlite_type: 2, internal_type: 'String' }, |
| 291 | + { name: 'timestamp', sqlite_type: 4, internal_type: 'Timestamp' }, |
| 292 | + { name: 'undefined', sqlite_type: 0, internal_type: 'Null' }, |
| 293 | + { name: 'uuid', sqlite_type: 2, internal_type: 'UUID' } |
| 294 | + ] |
| 295 | + } |
| 296 | + ]); |
| 297 | + }); |
| 298 | + |
| 299 | + test('validate postImages', async () => { |
| 300 | + await using adapter = new MongoRouteAPIAdapter({ |
| 301 | + type: 'mongodb', |
| 302 | + ...TEST_CONNECTION_OPTIONS, |
| 303 | + postImages: PostImagesOption.READ_ONLY |
| 304 | + }); |
| 305 | + const db = adapter.db; |
| 306 | + await clearTestDb(db); |
| 307 | + |
| 308 | + const collection = db.collection('test_data'); |
| 309 | + await setupTable(db); |
| 310 | + await insert(collection); |
| 311 | + |
| 312 | + const rules = SqlSyncRules.fromYaml( |
| 313 | + ` |
| 314 | +bucket_definitions: |
| 315 | + global: |
| 316 | + data: |
| 317 | + - select _id as id, * from test_data |
259 | 318 |
|
260 | | - const schema = await adapter.getConnectionSchema(); |
261 | | - const dbSchema = schema.filter((s) => s.name == TEST_CONNECTION_OPTIONS.database)[0]; |
262 | | - expect(dbSchema).not.toBeNull(); |
263 | | - expect(dbSchema.tables).toMatchObject([ |
| 319 | + `, |
| 320 | + { |
| 321 | + ...adapter.getParseSyncRulesOptions(), |
| 322 | + // No schema-based validation at this point |
| 323 | + schema: undefined |
| 324 | + } |
| 325 | + ); |
| 326 | + const source_table_patterns = rules.getSourceTables(); |
| 327 | + const results = await adapter.getDebugTablesInfo(source_table_patterns, rules); |
| 328 | + |
| 329 | + const result = results[0]; |
| 330 | + expect(result).not.toBeNull(); |
| 331 | + expect(result.table).toMatchObject({ |
| 332 | + schema: 'powersync_test_data', |
| 333 | + name: 'test_data', |
| 334 | + replication_id: ['_id'], |
| 335 | + data_queries: true, |
| 336 | + parameter_queries: false, |
| 337 | + errors: [ |
264 | 338 | { |
265 | | - name: 'test_data', |
266 | | - columns: [ |
267 | | - { name: '_id', sqlite_type: 4, internal_type: 'Integer' }, |
268 | | - { name: 'bool', sqlite_type: 4, internal_type: 'Boolean' }, |
269 | | - { name: 'bytea', sqlite_type: 1, internal_type: 'Binary' }, |
270 | | - { name: 'date', sqlite_type: 2, internal_type: 'Date' }, |
271 | | - { name: 'decimal', sqlite_type: 2, internal_type: 'Decimal' }, |
272 | | - { name: 'float', sqlite_type: 8, internal_type: 'Double' }, |
273 | | - { name: 'int2', sqlite_type: 4, internal_type: 'Integer' }, |
274 | | - { name: 'int4', sqlite_type: 4, internal_type: 'Integer' }, |
275 | | - { name: 'int8', sqlite_type: 4, internal_type: 'Long' }, |
276 | | - // We can fix these later |
277 | | - { name: 'js', sqlite_type: 2, internal_type: 'Object' }, |
278 | | - { name: 'js2', sqlite_type: 2, internal_type: 'Object' }, |
279 | | - { name: 'maxKey', sqlite_type: 0, internal_type: 'MaxKey' }, |
280 | | - { name: 'minKey', sqlite_type: 0, internal_type: 'MinKey' }, |
281 | | - { name: 'nested', sqlite_type: 2, internal_type: 'Object' }, |
282 | | - { name: 'null', sqlite_type: 0, internal_type: 'Null' }, |
283 | | - { name: 'objectId', sqlite_type: 2, internal_type: 'ObjectId' }, |
284 | | - // We can fix these later |
285 | | - { name: 'pointer', sqlite_type: 2, internal_type: 'Object' }, |
286 | | - { name: 'pointer2', sqlite_type: 2, internal_type: 'Object' }, |
287 | | - { name: 'regexp', sqlite_type: 2, internal_type: 'RegExp' }, |
288 | | - // Can fix this later |
289 | | - { name: 'symbol', sqlite_type: 2, internal_type: 'String' }, |
290 | | - { name: 'text', sqlite_type: 2, internal_type: 'String' }, |
291 | | - { name: 'timestamp', sqlite_type: 4, internal_type: 'Timestamp' }, |
292 | | - { name: 'undefined', sqlite_type: 0, internal_type: 'Null' }, |
293 | | - { name: 'uuid', sqlite_type: 2, internal_type: 'UUID' } |
294 | | - ] |
| 339 | + level: 'fatal', |
| 340 | + message: 'changeStreamPreAndPostImages not enabled on powersync_test_data.test_data' |
295 | 341 | } |
296 | | - ]); |
297 | | - } finally { |
298 | | - await adapter.shutdown(); |
299 | | - } |
| 342 | + ] |
| 343 | + }); |
| 344 | + }); |
| 345 | + |
| 346 | + test('validate postImages - auto-configure', async () => { |
| 347 | + await using adapter = new MongoRouteAPIAdapter({ |
| 348 | + type: 'mongodb', |
| 349 | + ...TEST_CONNECTION_OPTIONS, |
| 350 | + postImages: PostImagesOption.AUTO_CONFIGURE |
| 351 | + }); |
| 352 | + const db = adapter.db; |
| 353 | + await clearTestDb(db); |
| 354 | + |
| 355 | + const collection = db.collection('test_data'); |
| 356 | + await setupTable(db); |
| 357 | + await insert(collection); |
| 358 | + |
| 359 | + const rules = SqlSyncRules.fromYaml( |
| 360 | + ` |
| 361 | +bucket_definitions: |
| 362 | + global: |
| 363 | + data: |
| 364 | + - select _id as id, * from test_data |
| 365 | +
|
| 366 | + `, |
| 367 | + { |
| 368 | + ...adapter.getParseSyncRulesOptions(), |
| 369 | + // No schema-based validation at this point |
| 370 | + schema: undefined |
| 371 | + } |
| 372 | + ); |
| 373 | + const source_table_patterns = rules.getSourceTables(); |
| 374 | + const results = await adapter.getDebugTablesInfo(source_table_patterns, rules); |
| 375 | + |
| 376 | + const result = results[0]; |
| 377 | + expect(result).not.toBeNull(); |
| 378 | + expect(result.table).toMatchObject({ |
| 379 | + schema: 'powersync_test_data', |
| 380 | + name: 'test_data', |
| 381 | + replication_id: ['_id'], |
| 382 | + data_queries: true, |
| 383 | + parameter_queries: false, |
| 384 | + errors: [ |
| 385 | + { |
| 386 | + level: 'warning', |
| 387 | + message: |
| 388 | + 'changeStreamPreAndPostImages not enabled on powersync_test_data.test_data, will be enabled automatically' |
| 389 | + } |
| 390 | + ] |
| 391 | + }); |
| 392 | + }); |
| 393 | + |
| 394 | + test('validate postImages - off', async () => { |
| 395 | + await using adapter = new MongoRouteAPIAdapter({ |
| 396 | + type: 'mongodb', |
| 397 | + ...TEST_CONNECTION_OPTIONS, |
| 398 | + postImages: PostImagesOption.OFF |
| 399 | + }); |
| 400 | + const db = adapter.db; |
| 401 | + await clearTestDb(db); |
| 402 | + |
| 403 | + const collection = db.collection('test_data'); |
| 404 | + await setupTable(db); |
| 405 | + await insert(collection); |
| 406 | + |
| 407 | + const rules = SqlSyncRules.fromYaml( |
| 408 | + ` |
| 409 | +bucket_definitions: |
| 410 | + global: |
| 411 | + data: |
| 412 | + - select _id as id, * from test_data |
| 413 | +
|
| 414 | + `, |
| 415 | + { |
| 416 | + ...adapter.getParseSyncRulesOptions(), |
| 417 | + // No schema-based validation at this point |
| 418 | + schema: undefined |
| 419 | + } |
| 420 | + ); |
| 421 | + const source_table_patterns = rules.getSourceTables(); |
| 422 | + const results = await adapter.getDebugTablesInfo(source_table_patterns, rules); |
| 423 | + |
| 424 | + const result = results[0]; |
| 425 | + expect(result).not.toBeNull(); |
| 426 | + expect(result.table).toMatchObject({ |
| 427 | + schema: 'powersync_test_data', |
| 428 | + name: 'test_data', |
| 429 | + replication_id: ['_id'], |
| 430 | + data_queries: true, |
| 431 | + parameter_queries: false, |
| 432 | + errors: [] |
| 433 | + }); |
300 | 434 | }); |
301 | 435 | }); |
302 | 436 |
|
|
0 commit comments