@@ -102,6 +102,7 @@ def initialize_database(cratedb_config):
102
102
"melty.array_number" ,
103
103
"melty.array_string" ,
104
104
"melty.array_timestamp" ,
105
+ "melty.commits" ,
105
106
"melty.foo" ,
106
107
"melty.object_mixed" ,
107
108
"melty.test_new_array_column" ,
@@ -258,10 +259,11 @@ def test_special_chars_in_attributes(cratedb_target):
258
259
singer_file_to_target (file_name , cratedb_target )
259
260
260
261
261
- # TODO test that data is correctly set
262
- def test_optional_attributes (cratedb_target ):
262
+ def test_optional_attributes (cratedb_target , helper ):
263
263
file_name = "optional_attributes.singer"
264
264
singer_file_to_target (file_name , cratedb_target )
265
+ row = {"id" : 1 , "optional" : "This is optional" }
266
+ helper .verify_data ("test_optional_attributes" , 4 , "id" , row )
265
267
266
268
267
269
def test_schema_no_properties (cratedb_target ):
@@ -270,106 +272,114 @@ def test_schema_no_properties(cratedb_target):
270
272
singer_file_to_target (file_name , cratedb_target )
271
273
272
274
273
- # TODO test that data is correct
274
- # @pytest.mark.skip("ADD COLUMN a5 ARRAY(OBJECT) is made, but ARRAY(STRING) would be needed")
275
- def test_schema_updates (cratedb_target ):
275
+ def test_schema_updates (cratedb_target , helper ):
276
276
file_name = "schema_updates.singer"
277
277
singer_file_to_target (file_name , cratedb_target )
278
+ row = {
279
+ "id" : 1 ,
280
+ "a1" : 101 , # Decimal("101"),
281
+ "a2" : "string1" ,
282
+ "a3" : None ,
283
+ "a4" : None ,
284
+ "a5" : None ,
285
+ "a6" : None ,
286
+ }
287
+ helper .verify_data ("test_schema_updates" , 6 , "id" , row )
278
288
279
289
280
- # TODO test that data is correct
281
- def test_multiple_state_messages (cratedb_target ):
290
+ def test_multiple_state_messages (cratedb_target , helper ):
282
291
file_name = "multiple_state_messages.singer"
283
292
singer_file_to_target (file_name , cratedb_target )
293
+ row = {"id" : 1 , "metric" : 100 }
294
+ helper .verify_data ("test_multiple_state_messages_a" , 6 , "id" , row )
295
+ row = {"id" : 1 , "metric" : 110 }
296
+ helper .verify_data ("test_multiple_state_messages_b" , 6 , "id" , row )
297
+
298
+
299
+ # TODO test that data is correct
300
+ def test_multiple_schema_messages (cratedb_target , caplog ):
301
+ """Test multiple identical schema messages.
302
+
303
+ Multiple schema messages with the same schema should not cause 'schema has changed'
304
+ logging statements. See: https://github.com/MeltanoLabs/target-postgres/issues/124
305
+
306
+ Caplog docs: https://docs.pytest.org/en/latest/how-to/logging.html#caplog-fixture
307
+ """
308
+ file_name = "multiple_schema_messages.singer"
309
+ singer_file_to_target (file_name , cratedb_target )
310
+ assert "Schema has changed for stream" not in caplog .text
284
311
285
312
286
313
@pytest .mark .skip ("Upserts do not work yet" )
287
314
def test_relational_data (cratedb_target , helper ):
288
- engine = create_engine (cratedb_target )
289
315
file_name = "user_location_data.singer"
290
316
singer_file_to_target (file_name , cratedb_target )
291
317
292
318
file_name = "user_location_upsert_data.singer"
293
319
singer_file_to_target (file_name , cratedb_target )
294
320
295
- schema_name = cratedb_target .config ["default_target_schema" ]
321
+ users = [
322
+ {"id" : 1 , "name" : "Johny" },
323
+ {"id" : 2 , "name" : "George" },
324
+ {"id" : 3 , "name" : "Jacob" },
325
+ {"id" : 4 , "name" : "Josh" },
326
+ {"id" : 5 , "name" : "Jim" },
327
+ {"id" : 8 , "name" : "Thomas" },
328
+ {"id" : 12 , "name" : "Paul" },
329
+ {"id" : 13 , "name" : "Mary" },
330
+ ]
331
+ locations = [
332
+ {"id" : 1 , "name" : "Philly" },
333
+ {"id" : 2 , "name" : "NY" },
334
+ {"id" : 3 , "name" : "San Francisco" },
335
+ {"id" : 6 , "name" : "Colorado" },
336
+ {"id" : 8 , "name" : "Boston" },
337
+ ]
338
+ user_in_location = [
339
+ {
340
+ "id" : 1 ,
341
+ "user_id" : 1 ,
342
+ "location_id" : 4 ,
343
+ "info" : {"weather" : "rainy" , "mood" : "sad" },
344
+ },
345
+ {
346
+ "id" : 2 ,
347
+ "user_id" : 2 ,
348
+ "location_id" : 3 ,
349
+ "info" : {"weather" : "sunny" , "mood" : "satisfied" },
350
+ },
351
+ {
352
+ "id" : 3 ,
353
+ "user_id" : 1 ,
354
+ "location_id" : 3 ,
355
+ "info" : {"weather" : "sunny" , "mood" : "happy" },
356
+ },
357
+ {
358
+ "id" : 6 ,
359
+ "user_id" : 3 ,
360
+ "location_id" : 2 ,
361
+ "info" : {"weather" : "sunny" , "mood" : "happy" },
362
+ },
363
+ {
364
+ "id" : 14 ,
365
+ "user_id" : 4 ,
366
+ "location_id" : 1 ,
367
+ "info" : {"weather" : "cloudy" , "mood" : "ok" },
368
+ },
369
+ ]
296
370
297
- with engine .connect () as connection :
298
- expected_test_users = [
299
- {"id" : 1 , "name" : "Johny" },
300
- {"id" : 2 , "name" : "George" },
301
- {"id" : 3 , "name" : "Jacob" },
302
- {"id" : 4 , "name" : "Josh" },
303
- {"id" : 5 , "name" : "Jim" },
304
- {"id" : 8 , "name" : "Thomas" },
305
- {"id" : 12 , "name" : "Paul" },
306
- {"id" : 13 , "name" : "Mary" },
307
- ]
308
-
309
- full_table_name = f"{ schema_name } .test_users"
310
- result = connection .execute (sqlalchemy .text (f"SELECT * FROM { full_table_name } ORDER BY id" ))
311
- result_dict = [helper .remove_metadata_columns (row ._asdict ()) for row in result .all ()]
312
- assert result_dict == expected_test_users
313
-
314
- expected_test_locations = [
315
- {"id" : 1 , "name" : "Philly" },
316
- {"id" : 2 , "name" : "NY" },
317
- {"id" : 3 , "name" : "San Francisco" },
318
- {"id" : 6 , "name" : "Colorado" },
319
- {"id" : 8 , "name" : "Boston" },
320
- ]
321
-
322
- full_table_name = f"{ schema_name } .test_locations"
323
- result = connection .execute (sqlalchemy .text (f"SELECT * FROM { full_table_name } ORDER BY id" ))
324
- result_dict = [helper .remove_metadata_columns (row ._asdict ()) for row in result .all ()]
325
- assert result_dict == expected_test_locations
326
-
327
- expected_test_user_in_location = [
328
- {
329
- "id" : 1 ,
330
- "user_id" : 1 ,
331
- "location_id" : 4 ,
332
- "info" : {"weather" : "rainy" , "mood" : "sad" },
333
- },
334
- {
335
- "id" : 2 ,
336
- "user_id" : 2 ,
337
- "location_id" : 3 ,
338
- "info" : {"weather" : "sunny" , "mood" : "satisfied" },
339
- },
340
- {
341
- "id" : 3 ,
342
- "user_id" : 1 ,
343
- "location_id" : 3 ,
344
- "info" : {"weather" : "sunny" , "mood" : "happy" },
345
- },
346
- {
347
- "id" : 6 ,
348
- "user_id" : 3 ,
349
- "location_id" : 2 ,
350
- "info" : {"weather" : "sunny" , "mood" : "happy" },
351
- },
352
- {
353
- "id" : 14 ,
354
- "user_id" : 4 ,
355
- "location_id" : 1 ,
356
- "info" : {"weather" : "cloudy" , "mood" : "ok" },
357
- },
358
- ]
359
-
360
- full_table_name = f"{ schema_name } .test_user_in_location"
361
- result = connection .execute (sqlalchemy .text (f"SELECT * FROM { full_table_name } ORDER BY id" ))
362
- result_dict = [helper .remove_metadata_columns (row ._asdict ()) for row in result .all ()]
363
- assert result_dict == expected_test_user_in_location
364
-
365
-
366
- def test_no_primary_keys (cratedb_target ):
371
+ helper .verify_data ("test_users" , 8 , "id" , users )
372
+ helper .verify_data ("test_locations" , 5 , "id" , locations )
373
+ helper .verify_data ("test_user_in_location" , 5 , "id" , user_in_location )
374
+
375
+
376
+ def test_no_primary_keys (cratedb_target , helper ):
367
377
"""We run both of these tests twice just to ensure that no records are removed and append only works properly"""
368
378
engine = create_engine (cratedb_target )
369
379
table_name = "test_no_pk"
370
380
full_table_name = cratedb_target .config ["default_target_schema" ] + "." + table_name
371
381
with engine .connect () as connection , connection .begin ():
372
- result = connection .execute (sqlalchemy .text (f"DROP TABLE IF EXISTS { full_table_name } " ))
382
+ connection .execute (sqlalchemy .text (f"DROP TABLE IF EXISTS { full_table_name } " ))
373
383
file_name = f"{ table_name } .singer"
374
384
singer_file_to_target (file_name , cratedb_target )
375
385
@@ -382,28 +392,20 @@ def test_no_primary_keys(cratedb_target):
382
392
file_name = f"{ table_name } _append.singer"
383
393
singer_file_to_target (file_name , cratedb_target )
384
394
385
- # Will populate us with 22 records, we run this twice
386
- with engine .connect () as connection :
387
- result = connection .execute (sqlalchemy .text (f"SELECT * FROM { full_table_name } " ))
388
- assert result .rowcount == 16
395
+ # Will populate 22 records, we run this twice.
396
+ helper .verify_data (table_name , 16 )
389
397
390
398
391
399
def test_no_type (cratedb_target ):
392
400
file_name = "test_no_type.singer"
393
401
singer_file_to_target (file_name , cratedb_target )
394
402
395
403
396
- # TODO test that data is correct
397
- def test_duplicate_records (cratedb_target ):
404
+ def test_duplicate_records (cratedb_target , helper ):
398
405
file_name = "duplicate_records.singer"
399
406
singer_file_to_target (file_name , cratedb_target )
400
-
401
-
402
- # TODO test that data is correct
403
- @pytest .mark .skip ('Renders as `"fruits" ARRAY(OBJECT(DYNAMIC))`, but needs to be `ARRAY(STRING)`' )
404
- def test_array_data (cratedb_target ):
405
- file_name = "array_data.singer"
406
- singer_file_to_target (file_name , cratedb_target )
407
+ row = {"id" : 1 , "metric" : 100 }
408
+ helper .verify_data ("test_duplicate_records" , 2 , "id" , row )
407
409
408
410
409
411
def test_array_boolean (cratedb_target , helper ):
@@ -506,8 +508,7 @@ def test_object_mixed(cratedb_target, helper):
506
508
)
507
509
508
510
509
- # TODO test that data is correct
510
- def test_encoded_string_data (cratedb_target ):
511
+ def test_encoded_string_data (cratedb_target , helper ):
511
512
"""
512
513
We removed NUL characters from the original encoded_strings.singer as postgres doesn't allow them.
513
514
https://www.postgresql.org/docs/current/functions-string.html#:~:text=chr(0)%20is%20disallowed%20because%20text%20data%20types%20cannot%20store%20that%20character.
@@ -521,6 +522,12 @@ def test_encoded_string_data(cratedb_target):
521
522
522
523
file_name = "encoded_strings.singer"
523
524
singer_file_to_target (file_name , cratedb_target )
525
+ row = {"id" : 1 , "info" : "simple string 2837" }
526
+ helper .verify_data ("test_strings" , 11 , "id" , row )
527
+ row = {"id" : 1 , "info" : {"name" : "simple" , "value" : "simple string 2837" }}
528
+ helper .verify_data ("test_strings_in_objects" , 11 , "id" , row )
529
+ row = {"id" : 1 , "strings" : ["simple string" , "απλή συμβολοσειρά" , "简单的字串" ]}
530
+ helper .verify_data ("test_strings_in_arrays" , 6 , "id" , row )
524
531
525
532
526
533
@pytest .mark .skip ("Fails with: SQLParseException[Limit of total fields [1000] in index [melty.aapl] has been exceeded]" )
0 commit comments