@@ -345,3 +345,178 @@ def test_to_csv_raises_for_invalid_base(self, mock_session):
345345
346346 with pytest .raises (ValueError , match = "must be either" ):
347347 builder .to_csv_file ()
348+
349+
350+ class TestDatasetBuilderRegisterAsDataset :
351+ """Tests for the register_as_dataset lineage feature."""
352+
353+ @pytest .fixture
354+ def mock_session (self ):
355+ return Mock ()
356+
357+ @pytest .fixture
358+ def mock_feature_group (self ):
359+ fg = MagicMock (spec = FeatureGroup )
360+ fg .feature_group_name = "customers-fg"
361+ fg .feature_group_arn = "arn:aws:sagemaker:us-west-2:123456789012:feature-group/customers-fg"
362+ return fg
363+
364+ def test_register_as_dataset_default_false (self , mock_session ):
365+ """Default register_as_dataset is False."""
366+ builder = DatasetBuilder .create (
367+ base = MagicMock (spec = FeatureGroup ),
368+ output_path = "s3://bucket/output" ,
369+ session = mock_session ,
370+ )
371+ assert builder ._register_as_dataset is False
372+
373+ def test_register_as_dataset_true_sets_flag (self , mock_session ):
374+ """register_as_dataset=True is stored correctly."""
375+ builder = DatasetBuilder .create (
376+ base = MagicMock (spec = FeatureGroup ),
377+ output_path = "s3://bucket/output" ,
378+ session = mock_session ,
379+ register_as_dataset = True ,
380+ )
381+ assert builder ._register_as_dataset is True
382+
383+ def test_collect_source_fg_arns_from_base (self , mock_session , mock_feature_group ):
384+ """Collects base FG ARN."""
385+ builder = DatasetBuilder (
386+ _sagemaker_session = mock_session ,
387+ _base = mock_feature_group ,
388+ _output_path = "s3://bucket/output" ,
389+ _register_as_dataset = True ,
390+ )
391+ arns = builder ._collect_source_feature_group_arns ()
392+ assert arns == ["arn:aws:sagemaker:us-west-2:123456789012:feature-group/customers-fg" ]
393+
394+ def test_collect_source_fg_arns_with_merged_fg (self , mock_session , mock_feature_group ):
395+ """Collects base + merged FG ARNs."""
396+ merged_fg = MagicMock (spec = FeatureGroup )
397+ merged_fg .feature_group_arn = "arn:aws:sagemaker:us-west-2:123456789012:feature-group/orders-fg"
398+
399+ builder = DatasetBuilder (
400+ _sagemaker_session = mock_session ,
401+ _base = mock_feature_group ,
402+ _output_path = "s3://bucket/output" ,
403+ _register_as_dataset = True ,
404+ )
405+ builder ._source_feature_groups .append (merged_fg )
406+
407+ arns = builder ._collect_source_feature_group_arns ()
408+ assert len (arns ) == 2
409+ assert "arn:aws:sagemaker:us-west-2:123456789012:feature-group/customers-fg" in arns
410+ assert "arn:aws:sagemaker:us-west-2:123456789012:feature-group/orders-fg" in arns
411+
412+ def test_collect_source_fg_arns_deduplicates (self , mock_session , mock_feature_group ):
413+ """Doesn't duplicate ARNs if same FG used twice."""
414+ builder = DatasetBuilder (
415+ _sagemaker_session = mock_session ,
416+ _base = mock_feature_group ,
417+ _output_path = "s3://bucket/output" ,
418+ _register_as_dataset = True ,
419+ )
420+ builder ._source_feature_groups .append (mock_feature_group )
421+
422+ arns = builder ._collect_source_feature_group_arns ()
423+ assert len (arns ) == 1
424+
425+ def test_collect_source_fg_arns_dataframe_base_empty (self , mock_session ):
426+ """DataFrame base has no FG ARN."""
427+ df = pd .DataFrame ({"id" : [1 ], "event_time" : ["2024-01-01" ]})
428+ builder = DatasetBuilder (
429+ _sagemaker_session = mock_session ,
430+ _base = df ,
431+ _output_path = "s3://bucket/output" ,
432+ _record_identifier_feature_name = "id" ,
433+ _event_time_identifier_feature_name = "event_time" ,
434+ _register_as_dataset = True ,
435+ )
436+ arns = builder ._collect_source_feature_group_arns ()
437+ assert arns == []
438+
439+ def test_register_hub_content_called_on_success (self , mock_session , mock_feature_group ):
440+ """DataSet.create is called with correct params."""
441+ builder = DatasetBuilder (
442+ _sagemaker_session = mock_session ,
443+ _base = mock_feature_group ,
444+ _output_path = "s3://bucket/output" ,
445+ _register_as_dataset = True ,
446+ )
447+
448+ with patch (
449+ "sagemaker.ai_registry.dataset.DataSet.create" ,
450+ ) as mock_create :
451+ builder ._register_as_hub_content_dataset (
452+ csv_path = "s3://bucket/output/result.csv" ,
453+ query_execution_id = "abc-123" ,
454+ )
455+ mock_create .assert_called_once ()
456+ call_kwargs = mock_create .call_args [1 ]
457+ assert "customers-fg" in call_kwargs ["name" ]
458+ assert call_kwargs ["source" ] == "s3://bucket/output/result.csv"
459+ assert call_kwargs ["content_metadata" ]["SourceFeatureGroups" ] == [
460+ "arn:aws:sagemaker:us-west-2:123456789012:feature-group/customers-fg"
461+ ]
462+ assert call_kwargs ["content_metadata" ]["ExtractionMethod" ] == "FeatureStoreDatasetBuilder"
463+ assert call_kwargs ["content_metadata" ]["AthenaQueryExecutionId" ] == "abc-123"
464+ assert call_kwargs ["sagemaker_session" ] == mock_session
465+ assert call_kwargs ["wait" ] is False
466+
467+ def test_register_graceful_on_access_denied (self , mock_session , mock_feature_group , caplog ):
468+ """AccessDeniedException logs warning, doesn't raise."""
469+ builder = DatasetBuilder (
470+ _sagemaker_session = mock_session ,
471+ _base = mock_feature_group ,
472+ _output_path = "s3://bucket/output" ,
473+ _register_as_dataset = True ,
474+ )
475+
476+ with patch (
477+ "sagemaker.ai_registry.dataset.DataSet.create" ,
478+ ) as mock_create :
479+ mock_create .side_effect = Exception ("AccessDenied: not authorized" )
480+ # Should NOT raise
481+ builder ._register_as_hub_content_dataset (
482+ csv_path = "s3://bucket/output/result.csv" ,
483+ )
484+
485+ def test_register_graceful_on_generic_error (self , mock_session , mock_feature_group ):
486+ """Generic errors log warning, don't raise."""
487+ builder = DatasetBuilder (
488+ _sagemaker_session = mock_session ,
489+ _base = mock_feature_group ,
490+ _output_path = "s3://bucket/output" ,
491+ _register_as_dataset = True ,
492+ )
493+
494+ with patch (
495+ "sagemaker.ai_registry.dataset.DataSet.create" ,
496+ ) as mock_create :
497+ mock_create .side_effect = Exception ("Some service error" )
498+ # Should NOT raise
499+ builder ._register_as_hub_content_dataset (
500+ csv_path = "s3://bucket/output/result.csv" ,
501+ )
502+
503+ def test_register_skipped_when_no_fg_arns (self , mock_session ):
504+ """Skips registration when no FG ARNs available (DataFrame base, no merged FGs)."""
505+ df = pd .DataFrame ({"id" : [1 ], "event_time" : ["2024-01-01" ]})
506+ builder = DatasetBuilder (
507+ _sagemaker_session = mock_session ,
508+ _base = df ,
509+ _output_path = "s3://bucket/output" ,
510+ _record_identifier_feature_name = "id" ,
511+ _event_time_identifier_feature_name = "event_time" ,
512+ _register_as_dataset = True ,
513+ )
514+
515+ with patch (
516+ "sagemaker.ai_registry.dataset.DataSet.create" ,
517+ ) as mock_create :
518+ builder ._register_as_hub_content_dataset (
519+ csv_path = "s3://bucket/output/result.csv" ,
520+ )
521+ # Should NOT call DataSet.create since no FG ARNs
522+ mock_create .assert_not_called ()
0 commit comments