@@ -245,6 +245,94 @@ def test_export_datasets_default_export_path(simple_dataset):
245245 assert run_id in result
246246
247247
248+ def test_export_datasets_handles_export_failure ():
249+ """Test that the function handles export failures gracefully"""
250+ with tempfile .TemporaryDirectory () as temp_dir :
251+ source_db_path = Path (temp_dir ) / "source.db"
252+ target_db_path = Path (temp_dir ) / "target.db"
253+ export_path = Path (temp_dir ) / "exports"
254+
255+ # Create source database
256+ source_conn = connect (source_db_path )
257+ exp = load_or_create_experiment (
258+ experiment_name = "test_exp" ,
259+ sample_name = "test_sample" ,
260+ conn = source_conn
261+ )
262+
263+ # Create interdependencies with problematic data that might fail export
264+ x = ParamSpec ("x" , "text" , unit = "" ) # Text data might be harder to export
265+ y = ParamSpec ("y" , "numeric" , unit = "A" )
266+ interdeps = InterDependencies_ (dependencies = {y : (x ,)})
267+
268+ # Create dataset with mixed data types
269+ dataset = DataSet (conn = source_conn , exp_id = exp .exp_id )
270+ dataset .set_interdependencies (interdeps )
271+ dataset .mark_started ()
272+
273+ # Add some data that might be challenging to export
274+ for i in range (3 ):
275+ dataset .add_results ([{"x" : f"text_{ i } " , "y" : i ** 2 }])
276+
277+ dataset .mark_completed ()
278+ source_conn .close ()
279+
280+ # Run the export function
281+ result = export_datasets_and_create_metadata_db (
282+ source_db_path = source_db_path ,
283+ target_db_path = target_db_path ,
284+ export_path = export_path ,
285+ )
286+
287+ # Should handle the dataset one way or another
288+ assert len (result ) == 1
289+ assert dataset .run_id in result
290+ # Should either export or copy as-is
291+ assert result [dataset .run_id ] in ["exported" , "copied_as_is" ]
292+
293+
294+ def test_export_datasets_nonexistent_source ():
295+ """Test behavior with non-existent source database"""
296+ with tempfile .TemporaryDirectory () as temp_dir :
297+ source_db_path = Path (temp_dir ) / "nonexistent.db"
298+ target_db_path = Path (temp_dir ) / "target.db"
299+ export_path = Path (temp_dir ) / "exports"
300+
301+ # Should handle non-existent source gracefully
302+ with pytest .raises ((FileNotFoundError , OSError )):
303+ export_datasets_and_create_metadata_db (
304+ source_db_path = source_db_path ,
305+ target_db_path = target_db_path ,
306+ export_path = export_path ,
307+ )
308+
309+
310+ def test_export_datasets_readonly_target ():
311+ """Test behavior when target path is not writable"""
312+ source_db_path , run_id = simple_dataset
313+
314+ with tempfile .TemporaryDirectory () as temp_dir :
315+ # Create a read-only directory for target
316+ readonly_dir = Path (temp_dir ) / "readonly"
317+ readonly_dir .mkdir ()
318+ readonly_dir .chmod (0o444 ) # Read-only
319+
320+ try :
321+ target_db_path = readonly_dir / "target.db"
322+ export_path = Path (temp_dir ) / "exports"
323+
324+ # Should handle permission errors gracefully
325+ with pytest .raises ((PermissionError , OSError )):
326+ export_datasets_and_create_metadata_db (
327+ source_db_path = source_db_path ,
328+ target_db_path = target_db_path ,
329+ export_path = export_path ,
330+ )
331+ finally :
332+ # Restore permissions for cleanup
333+ readonly_dir .chmod (0o755 )
334+
335+
248336@pytest .mark .parametrize (
249337 "upgrade_source,upgrade_target" ,
250338 [
@@ -273,4 +361,123 @@ def test_export_datasets_upgrade_flags(simple_dataset, upgrade_source, upgrade_t
273361
274362 # Function should complete successfully regardless of upgrade flags
275363 # (assuming databases are already current version)
276- assert isinstance (result , dict )
364+ assert isinstance (result , dict )
365+
366+
367+ def test_export_datasets_large_dataset_scenario ():
368+ """Test handling of a scenario with multiple datasets including edge cases"""
369+ with tempfile .TemporaryDirectory () as temp_dir :
370+ source_db_path = Path (temp_dir ) / "source.db"
371+ target_db_path = Path (temp_dir ) / "target.db"
372+ export_path = Path (temp_dir ) / "exports"
373+
374+ # Create source database
375+ source_conn = connect (source_db_path )
376+ exp = load_or_create_experiment (
377+ experiment_name = "test_exp" ,
378+ sample_name = "test_sample" ,
379+ conn = source_conn
380+ )
381+
382+ # Create interdependencies
383+ x = ParamSpec ("x" , "numeric" , unit = "V" )
384+ y = ParamSpec ("y" , "numeric" , unit = "A" )
385+ interdeps = InterDependencies_ (dependencies = {y : (x ,)})
386+
387+ created_datasets = []
388+
389+ # Create several datasets with different characteristics
390+ for i in range (5 ):
391+ dataset = DataSet (conn = source_conn , exp_id = exp .exp_id )
392+ dataset .set_interdependencies (interdeps )
393+ dataset .mark_started ()
394+
395+ # Add varying amounts of data
396+ for j in range (i + 1 ): # 1, 2, 3, 4, 5 data points respectively
397+ dataset .add_results ([{"x" : j , "y" : j * (i + 1 )}])
398+
399+ if i < 4 : # Leave one dataset incomplete
400+ dataset .mark_completed ()
401+
402+ created_datasets .append (dataset )
403+
404+ source_conn .close ()
405+
406+ # Run the export function
407+ result = export_datasets_and_create_metadata_db (
408+ source_db_path = source_db_path ,
409+ target_db_path = target_db_path ,
410+ export_path = export_path ,
411+ )
412+
413+ # Check that all datasets were processed
414+ assert len (result ) == 5
415+
416+ # Check that target database has all runs
417+ target_conn = connect (target_db_path )
418+ target_runs = get_runs (target_conn )
419+ target_conn .close ()
420+
421+ assert len (target_runs ) == 5
422+
423+ # The incomplete dataset should be copied as-is
424+ incomplete_dataset = created_datasets [- 1 ]
425+ assert result [incomplete_dataset .run_id ] == "copied_as_is"
426+
427+
428+ def test_export_datasets_status_reporting ():
429+ """Test that the function returns detailed status information"""
430+ with tempfile .TemporaryDirectory () as temp_dir :
431+ source_db_path = Path (temp_dir ) / "source.db"
432+ target_db_path = Path (temp_dir ) / "target.db"
433+ export_path = Path (temp_dir ) / "exports"
434+
435+ # Create source database with a completed dataset
436+ source_conn = connect (source_db_path )
437+ exp = load_or_create_experiment (
438+ experiment_name = "test_exp" ,
439+ sample_name = "test_sample" ,
440+ conn = source_conn
441+ )
442+
443+ # Create interdependencies
444+ x = ParamSpec ("x" , "numeric" , unit = "V" )
445+ y = ParamSpec ("y" , "numeric" , unit = "A" )
446+ interdeps = InterDependencies_ (dependencies = {y : (x ,)})
447+
448+ # Create and complete a dataset
449+ dataset = DataSet (conn = source_conn , exp_id = exp .exp_id )
450+ dataset .set_interdependencies (interdeps )
451+ dataset .mark_started ()
452+
453+ for i in range (5 ):
454+ dataset .add_results ([{"x" : i , "y" : i ** 2 }])
455+
456+ dataset .mark_completed ()
457+ source_conn .close ()
458+
459+ # Run the export function
460+ result = export_datasets_and_create_metadata_db (
461+ source_db_path = source_db_path ,
462+ target_db_path = target_db_path ,
463+ export_path = export_path ,
464+ )
465+
466+ # Check return value structure
467+ assert isinstance (result , dict )
468+ assert len (result ) == 1
469+ assert dataset .run_id in result
470+
471+ # Status should be one of the expected values
472+ status = result [dataset .run_id ]
473+ expected_statuses = ["exported" , "copied_as_is" , "already_exists" ]
474+ assert status in expected_statuses , f"Unexpected status: { status } "
475+
476+ # If we run again, should report already_exists
477+ result2 = export_datasets_and_create_metadata_db (
478+ source_db_path = source_db_path ,
479+ target_db_path = target_db_path ,
480+ export_path = export_path ,
481+ )
482+
483+ assert result2 [dataset .run_id ] == "already_exists"
0 commit comments