From 35e42424187d532c61b6260c288d9a90baef13eb Mon Sep 17 00:00:00 2001 From: Francois Campbell Date: Tue, 15 Oct 2024 14:30:18 -0400 Subject: [PATCH] SNOW-1706990 Convert test_manager.py to use v2 entities (#1722) Updates tests in `test_manager.py` to use a v2 test project and call out to the v2 entities directly instead of going through the `NativeAppManager`. I've kept the diff as simple as possible and avoided introducing too many helpers or reorganizing the code, that can be done separately if necessary (the top priority is to just work towards removing `NativeAppManager` and all these static methods first). --- .../nativeapp/entities/application_package.py | 10 +- tests/nativeapp/test_manager.py | 497 ++++++++++++------ tests/nativeapp/utils.py | 41 ++ .../projects/napp_project_2/001-shared.sql | 5 + .../projects/napp_project_2/002-shared.sql | 8 + .../projects/napp_project_2/app/README.md | 1 + .../projects/napp_project_2/app/manifest.yml | 0 .../napp_project_2/app/streamlit/config.py | 1 + .../napp_project_2/app/streamlit/main.py | 1 + .../projects/napp_project_2/setup.sql | 1 + .../projects/napp_project_2/snowflake.yml | 24 + 11 files changed, 434 insertions(+), 155 deletions(-) create mode 100644 tests/test_data/projects/napp_project_2/001-shared.sql create mode 100644 tests/test_data/projects/napp_project_2/002-shared.sql create mode 100644 tests/test_data/projects/napp_project_2/app/README.md create mode 100644 tests/test_data/projects/napp_project_2/app/manifest.yml create mode 100644 tests/test_data/projects/napp_project_2/app/streamlit/config.py create mode 100644 tests/test_data/projects/napp_project_2/app/streamlit/main.py create mode 100644 tests/test_data/projects/napp_project_2/setup.sql create mode 100644 tests/test_data/projects/napp_project_2/snowflake.yml diff --git a/src/snowflake/cli/_plugins/nativeapp/entities/application_package.py b/src/snowflake/cli/_plugins/nativeapp/entities/application_package.py index b57550d01d..ce4cd804a2 100644 --- a/src/snowflake/cli/_plugins/nativeapp/entities/application_package.py +++ b/src/snowflake/cli/_plugins/nativeapp/entities/application_package.py @@ -232,7 +232,13 @@ def action_drop(self, action_ctx: ActionContext, force_drop: bool, *args, **kwar ) def action_validate( - self, action_ctx: ActionContext, interactive: bool, force: bool, *args, **kwargs + self, + action_ctx: ActionContext, + interactive: bool, + force: bool, + use_scratch_stage: bool = True, + *args, + **kwargs, ): model = self._entity_model workspace_ctx = self._workspace_ctx @@ -266,7 +272,7 @@ def action_validate( post_deploy_hooks=model.meta and model.meta.post_deploy, package_scripts=[], # Package scripts are not supported in PDFv2 policy=policy, - use_scratch_stage=True, + use_scratch_stage=use_scratch_stage, scratch_stage_fqn=f"{package_name}.{model.scratch_stage}", ) workspace_ctx.console.message("Setup script is valid") diff --git a/tests/nativeapp/test_manager.py b/tests/nativeapp/test_manager.py index 563860d246..54780ce85a 100644 --- a/tests/nativeapp/test_manager.py +++ b/tests/nativeapp/test_manager.py @@ -31,27 +31,38 @@ SPECIAL_COMMENT, SPECIAL_COMMENT_OLD, ) +from snowflake.cli._plugins.nativeapp.entities.application import ( + ApplicationEntity, + ApplicationEntityModel, +) +from snowflake.cli._plugins.nativeapp.entities.application_package import ( + ApplicationPackageEntity, + ApplicationPackageEntityModel, +) from snowflake.cli._plugins.nativeapp.exceptions import ( ApplicationPackageAlreadyExistsError, ApplicationPackageDoesNotExistError, NoEventTableForAccount, SetupScriptFailedValidation, ) -from snowflake.cli._plugins.nativeapp.manager import ( - NativeAppManager, -) from snowflake.cli._plugins.nativeapp.policy import AllowAlwaysPolicy from snowflake.cli._plugins.stage.diff import ( DiffResult, StagePath, ) +from snowflake.cli._plugins.workspace.manager import WorkspaceManager from snowflake.cli.api.console import cli_console as cc -from snowflake.cli.api.entities.utils import _get_stage_paths_to_sync +from snowflake.cli.api.entities.common import EntityActions +from snowflake.cli.api.entities.utils import ( + _get_stage_paths_to_sync, + sync_deploy_root_with_stage, +) from snowflake.cli.api.errno import ( DOES_NOT_EXIST_OR_NOT_AUTHORIZED, ) from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError from snowflake.cli.api.project.definition_manager import DefinitionManager +from snowflake.cli.api.project.util import extract_schema from snowflake.connector import ProgrammingError from snowflake.connector.cursor import DictCursor @@ -68,8 +79,8 @@ NATIVEAPP_MODULE, SQL_EXECUTOR_EXECUTE, mock_execute_helper, - mock_snowflake_yml_file, - quoted_override_yml_file, + mock_snowflake_yml_file_v2, + quoted_override_yml_file_v2, touch, ) from tests.testing_utils.files_and_dirs import create_named_file @@ -89,10 +100,14 @@ } -def _get_na_manager(working_dir: Optional[str] = None): - dm = DefinitionManager(working_dir) - return NativeAppManager( - project_definition=dm.project_definition.native_app, +def _get_dm(working_dir: Optional[str] = None): + return DefinitionManager(working_dir) + + +def _get_wm(working_dir: Optional[str] = None): + dm = _get_dm(working_dir) + return WorkspaceManager( + project_definition=dm.project_definition, project_root=dm.project_root, ) @@ -115,18 +130,25 @@ def test_sync_deploy_root_with_stage( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] - native_app_manager = _get_na_manager() assert mock_diff_result.has_changes() mock_bundle_map = mock.Mock(spec=BundleMap) - native_app_manager.sync_deploy_root_with_stage( + package_name = pkg_model.fqn.identifier + stage_fqn = f"{package_name}.{pkg_model.stage}" + sync_deploy_root_with_stage( + console=cc, + deploy_root=dm.project_root / pkg_model.deploy_root, + package_name=package_name, + stage_schema=extract_schema(stage_fqn), bundle_map=mock_bundle_map, role="new_role", prune=True, recursive=True, - stage_fqn=native_app_manager.stage_fqn, + stage_fqn=stage_fqn, ) expected = [ @@ -143,11 +165,11 @@ def test_sync_deploy_root_with_stage( ] assert mock_execute.mock_calls == expected mock_compute_stage_diff.assert_called_once_with( - native_app_manager.deploy_root, "app_pkg.app_src.stage" + dm.project_root / pkg_model.deploy_root, "app_pkg.app_src.stage" ) mock_local_diff_with_stage.assert_called_once_with( role="new_role", - deploy_root_path=native_app_manager.deploy_root, + deploy_root_path=dm.project_root / pkg_model.deploy_root, diff_result=mock_diff_result, stage_fqn="app_pkg.app_src.stage", ) @@ -186,17 +208,24 @@ def test_sync_deploy_root_with_stage_prune( create_named_file( file_name="snowflake.yml", dir_name=os.getcwd(), - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] mock_bundle_map = mock.Mock(spec=BundleMap) - native_app_manager.sync_deploy_root_with_stage( + package_name = pkg_model.fqn.identifier + stage_fqn = f"{package_name}.{pkg_model.stage}" + sync_deploy_root_with_stage( + console=cc, + deploy_root=dm.project_root / pkg_model.deploy_root, + package_name=package_name, + stage_schema=extract_schema(stage_fqn), bundle_map=mock_bundle_map, role="new_role", prune=prune, recursive=True, - stage_fqn=native_app_manager.stage_fqn, + stage_fqn=stage_fqn, ) if expected_warn: @@ -240,11 +269,16 @@ def test_get_app_pkg_distribution_in_snowflake(mock_execute, temp_dir, mock_curs create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() - actual_distribution = native_app_manager.get_app_pkg_distribution_in_snowflake + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + actual_distribution = ( + ApplicationPackageEntity.get_app_pkg_distribution_in_snowflake( + pkg_model.fqn.name, pkg_model.meta.role + ) + ) assert actual_distribution == "external" assert mock_execute.mock_calls == expected @@ -277,12 +311,16 @@ def test_get_app_pkg_distribution_in_snowflake_throws_programming_error( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + with pytest.raises(ProgrammingError): - native_app_manager.get_app_pkg_distribution_in_snowflake + ApplicationPackageEntity.get_app_pkg_distribution_in_snowflake( + pkg_model.fqn.name, pkg_model.meta.role + ) assert mock_execute.mock_calls == expected @@ -309,12 +347,16 @@ def test_get_app_pkg_distribution_in_snowflake_throws_execution_error( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + with pytest.raises(SnowflakeSQLExecutionError): - native_app_manager.get_app_pkg_distribution_in_snowflake + ApplicationPackageEntity.get_app_pkg_distribution_in_snowflake( + pkg_model.fqn.name, pkg_model.meta.role + ) assert mock_execute.mock_calls == expected @@ -344,12 +386,16 @@ def test_get_app_pkg_distribution_in_snowflake_throws_distribution_error( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + with pytest.raises(ProgrammingError): - native_app_manager.get_app_pkg_distribution_in_snowflake + ApplicationPackageEntity.get_app_pkg_distribution_in_snowflake( + pkg_model.fqn.name, pkg_model.meta.role + ) assert mock_execute.mock_calls == expected @@ -361,7 +407,7 @@ def test_is_app_pkg_distribution_same_in_sf_w_arg(mock_mismatch, temp_dir): create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) create_named_file( @@ -370,8 +416,8 @@ def test_is_app_pkg_distribution_same_in_sf_w_arg(mock_mismatch, temp_dir): contents=[ dedent( """\ - native_app: - package: + entities: + app_pkg: distribution: >- EXTERNAL """ @@ -379,8 +425,15 @@ def test_is_app_pkg_distribution_same_in_sf_w_arg(mock_mismatch, temp_dir): ], ) - native_app_manager = _get_na_manager() - assert native_app_manager.verify_project_distribution("internal") is False + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + assert not ApplicationPackageEntity.verify_project_distribution( + console=cc, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + package_distribution=pkg_model.distribution, + expected_distribution="internal", + ) mock_mismatch.assert_not_called() @@ -392,7 +445,7 @@ def test_is_app_pkg_distribution_same_in_sf_no_mismatch(mock_mismatch, temp_dir) create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) create_named_file( @@ -401,8 +454,8 @@ def test_is_app_pkg_distribution_same_in_sf_no_mismatch(mock_mismatch, temp_dir) contents=[ dedent( """\ - native_app: - package: + entities: + app_pkg: distribution: >- EXTERNAL """ @@ -410,8 +463,14 @@ def test_is_app_pkg_distribution_same_in_sf_no_mismatch(mock_mismatch, temp_dir) ], ) - native_app_manager = _get_na_manager() - assert native_app_manager.verify_project_distribution() is True + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + assert ApplicationPackageEntity.verify_project_distribution( + console=cc, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + package_distribution=pkg_model.distribution, + ) @mock_get_app_pkg_distribution_in_sf() @@ -425,11 +484,17 @@ def test_is_app_pkg_distribution_same_in_sf_has_mismatch( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() - assert native_app_manager.verify_project_distribution() is False + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + assert not ApplicationPackageEntity.verify_project_distribution( + console=cc, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + package_distribution=pkg_model.distribution, + ) mock_warning.assert_called_once_with( "Application package app_pkg in your Snowflake account has distribution property external,\nwhich does not match the value specified in project definition file: internal.\n" ) @@ -467,11 +532,14 @@ def test_get_existing_app_info_app_exists(mock_execute, temp_dir, mock_cursor): create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() - show_obj_row = native_app_manager.get_existing_app_info() + dm = _get_dm() + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] + show_obj_row = ApplicationEntity.get_existing_app_info_static( + app_model.fqn.name, app_model.meta.role + ) assert show_obj_row is not None assert show_obj_row[NAME_COL] == "MYAPP" assert mock_execute.mock_calls == expected @@ -499,11 +567,14 @@ def test_get_existing_app_info_app_does_not_exist(mock_execute, temp_dir, mock_c create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() - show_obj_row = native_app_manager.get_existing_app_info() + dm = _get_dm() + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] + show_obj_row = ApplicationEntity.get_existing_app_info_static( + app_model.fqn.name, app_model.meta.role + ) assert show_obj_row is None assert mock_execute.mock_calls == expected @@ -543,11 +614,14 @@ def test_get_existing_app_pkg_info_app_pkg_exists(mock_execute, temp_dir, mock_c create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() - show_obj_row = native_app_manager.get_existing_app_pkg_info() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + show_obj_row = ApplicationPackageEntity.get_existing_app_pkg_info( + pkg_model.fqn.name, pkg_model.meta.role + ) assert show_obj_row is not None assert show_obj_row[NAME_COL] == "APP_PKG" assert mock_execute.mock_calls == expected @@ -580,11 +654,14 @@ def test_get_existing_app_pkg_info_app_pkg_does_not_exist( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() - show_obj_row = native_app_manager.get_existing_app_pkg_info() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + show_obj_row = ApplicationPackageEntity.get_existing_app_pkg_info( + pkg_model.fqn.name, pkg_model.meta.role + ) assert show_obj_row is None assert mock_execute.mock_calls == expected @@ -632,7 +709,7 @@ def test_get_snowsight_url_with_pdf_warehouse( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) side_effects, expected = mock_execute_helper( @@ -646,9 +723,12 @@ def test_get_snowsight_url_with_pdf_warehouse( ) mock_execute_query.side_effect = side_effects + fallback_side_effect - native_app_manager = _get_na_manager() + dm = _get_dm() + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] assert ( - native_app_manager.get_snowsight_url() + ApplicationEntity.get_snowsight_url_static( + app_model.fqn.name, app_model.meta.warehouse + ) == "https://host/organization/account/#/apps/application/MYAPP" ) assert mock_execute_query.mock_calls == expected + fallback_warehouse_call @@ -665,13 +745,13 @@ def test_get_snowsight_url_with_pdf_warehouse( "project_definition_files, warehouse, expected_calls, fallback_side_effect", [ ( - "napp_project_1", + "napp_project_2", "MockWarehouse", [mock.call("select current_warehouse()")], [None], ), ( - "napp_project_1", + "napp_project_2", None, [], [], @@ -702,15 +782,18 @@ def test_get_snowsight_url_without_pdf_warehouse( mock_cursor([(warehouse,)], []) ] + fallback_side_effect - native_app_manager = _get_na_manager(str(working_dir)) + dm = _get_dm(str(working_dir)) + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp_polly"] if warehouse: assert ( - native_app_manager.get_snowsight_url() + ApplicationEntity.get_snowsight_url_static(app_model.fqn.name, warehouse) == "https://host/organization/account/#/apps/application/MYAPP_POLLY" ) else: with pytest.raises(ClickException) as err: - native_app_manager.get_snowsight_url() + ApplicationEntity.get_snowsight_url_static( + app_model.fqn.name, app_model.meta.warehouse + ) assert "Application warehouse cannot be empty." in err.value.message assert mock_execute_query.mock_calls == expected_calls @@ -750,11 +833,17 @@ def test_create_app_pkg_no_existing_package( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() - native_app_manager.create_app_package() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + ApplicationPackageEntity.create_app_package( + console=cc, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + package_distribution=pkg_model.distribution, + ) assert mock_execute.mock_calls == expected mock_get_existing_app_pkg_info.assert_called_once() @@ -785,13 +874,19 @@ def test_create_app_pkg_different_owner( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] # Invoke create when the package already exists, but the owner is the current role. # This is expected to succeed with no warnings. - native_app_manager.create_app_package() + ApplicationPackageEntity.create_app_package( + console=cc, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + package_distribution=pkg_model.distribution, + ) mock_execute.assert_not_called() @@ -826,11 +921,17 @@ def test_create_app_pkg_external_distribution( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() - native_app_manager.create_app_package() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + ApplicationPackageEntity.create_app_package( + console=cc, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + package_distribution=pkg_model.distribution, + ) if not is_pkg_distribution_same: mock_warning.assert_called_once_with( "Continuing to execute `snow app run` on application package app_pkg with distribution 'external'." @@ -873,11 +974,17 @@ def test_create_app_pkg_internal_distribution_special_comment( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() - native_app_manager.create_app_package() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + ApplicationPackageEntity.create_app_package( + console=cc, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + package_distribution=pkg_model.distribution, + ) if not is_pkg_distribution_same: mock_warning.assert_called_once_with( "Continuing to execute `snow app run` on application package app_pkg with distribution 'internal'." @@ -914,12 +1021,18 @@ def test_create_app_pkg_internal_distribution_no_special_comment( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] with pytest.raises(ApplicationPackageAlreadyExistsError): - native_app_manager.create_app_package() + ApplicationPackageEntity.create_app_package( + console=cc, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + package_distribution=pkg_model.distribution, + ) if not is_pkg_distribution_same: mock_warning.assert_called_once_with( @@ -953,12 +1066,18 @@ def test_existing_app_pkg_without_special_comment( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] with pytest.raises(ApplicationPackageAlreadyExistsError): - native_app_manager.create_app_package() + ApplicationPackageEntity.create_app_package( + console=cc, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + package_distribution=pkg_model.distribution, + ) @pytest.mark.parametrize( @@ -995,7 +1114,7 @@ def test_validate_passing(mock_execute, temp_dir, mock_cursor): create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) success_data = dict(status="SUCCESS") @@ -1011,8 +1130,14 @@ def test_validate_passing(mock_execute, temp_dir, mock_cursor): ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() - native_app_manager.validate() + wm = _get_wm() + wm.perform_action( + "app_pkg", + EntityActions.VALIDATE, + interactive=False, + force=True, + use_scratch_stage=False, + ) assert mock_execute.mock_calls == expected @@ -1025,7 +1150,7 @@ def test_validate_passing_with_warnings( create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) warning_file = "@STAGE/setup_script.sql" @@ -1051,8 +1176,14 @@ def test_validate_passing_with_warnings( ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() - native_app_manager.validate() + wm = _get_wm() + wm.perform_action( + "app_pkg", + EntityActions.VALIDATE, + interactive=False, + force=True, + use_scratch_stage=False, + ) warn_message = f"{warning['message']} (error code {warning['errorCode']})" mock_warning.assert_called_once_with(warn_message) @@ -1065,7 +1196,7 @@ def test_validate_failing(mock_warning, mock_execute, temp_dir, mock_cursor): create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) error_file = "@STAGE/empty.sql" @@ -1101,12 +1232,18 @@ def test_validate_failing(mock_warning, mock_execute, temp_dir, mock_cursor): ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() + wm = _get_wm() with pytest.raises( SetupScriptFailedValidation, match="Snowflake Native App setup script failed validation.", ): - native_app_manager.validate() + wm.perform_action( + "app_pkg", + EntityActions.VALIDATE, + interactive=False, + force=True, + use_scratch_stage=False, + ) warn_message = f"{warning['message']} (error code {warning['errorCode']})" error_message = f"{error['message']} (error code {error['errorCode']})" @@ -1121,7 +1258,7 @@ def test_validate_query_error(mock_execute, temp_dir, mock_cursor): create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) side_effects, expected = mock_execute_helper( @@ -1136,9 +1273,15 @@ def test_validate_query_error(mock_execute, temp_dir, mock_cursor): ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() + wm = _get_wm() with pytest.raises(SnowflakeSQLExecutionError): - native_app_manager.validate() + wm.perform_action( + "app_pkg", + EntityActions.VALIDATE, + interactive=False, + force=True, + use_scratch_stage=False, + ) assert mock_execute.mock_calls == expected @@ -1148,7 +1291,7 @@ def test_validate_not_deployed(mock_execute, temp_dir, mock_cursor): create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) side_effects, expected = mock_execute_helper( @@ -1166,9 +1309,15 @@ def test_validate_not_deployed(mock_execute, temp_dir, mock_cursor): ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() + wm = _get_wm() with pytest.raises(ApplicationPackageDoesNotExistError, match="app_pkg"): - native_app_manager.validate() + wm.perform_action( + "app_pkg", + EntityActions.VALIDATE, + interactive=False, + force=True, + use_scratch_stage=False, + ) assert mock_execute.mock_calls == expected @@ -1179,7 +1328,7 @@ def test_validate_use_scratch_stage(mock_execute, mock_deploy, temp_dir, mock_cu create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) success_data = dict(status="SUCCESS") @@ -1207,29 +1356,40 @@ def test_validate_use_scratch_stage(mock_execute, mock_deploy, temp_dir, mock_cu ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() - native_app_manager.validate(use_scratch_stage=True) + wm = _get_wm() + wm.perform_action( + "app_pkg", + EntityActions.VALIDATE, + interactive=False, + force=True, + use_scratch_stage=True, + ) + pd = wm._project_definition # noqa: SLF001 + pkg_model: ApplicationPackageEntityModel = pd.entities["app_pkg"] + project_root = wm._project_root # noqa: SLF001 mock_deploy.assert_called_with( console=cc, - project_root=native_app_manager.project_root, - deploy_root=native_app_manager.deploy_root, - bundle_root=native_app_manager.bundle_root, - generated_root=native_app_manager.generated_root, - artifacts=native_app_manager.artifacts, + project_root=project_root, + deploy_root=project_root / pkg_model.deploy_root, + bundle_root=project_root / pkg_model.bundle_root, + generated_root=( + project_root / pkg_model.deploy_root / pkg_model.generated_root + ), + artifacts=pkg_model.artifacts, bundle_map=None, - package_name=native_app_manager.package_name, - package_role=native_app_manager.package_role, - package_distribution=native_app_manager.package_distribution, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + package_distribution=pkg_model.distribution, prune=True, recursive=True, paths=[], print_diff=False, validate=False, - stage_fqn=native_app_manager.scratch_stage_fqn, - package_warehouse=native_app_manager.package_warehouse, - post_deploy_hooks=native_app_manager.package_post_deploy_hooks, - package_scripts=native_app_manager.package_scripts, + stage_fqn=f"{pkg_model.fqn.name}.{pkg_model.scratch_stage}", + package_warehouse=pkg_model.meta.warehouse, + post_deploy_hooks=pkg_model.meta.post_deploy, + package_scripts=[], policy=AllowAlwaysPolicy(), ) assert mock_execute.mock_calls == expected @@ -1243,7 +1403,7 @@ def test_validate_failing_drops_scratch_stage( create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) error_file = "@STAGE/empty.sql" @@ -1281,33 +1441,44 @@ def test_validate_failing_drops_scratch_stage( ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() + wm = _get_wm() with pytest.raises( SetupScriptFailedValidation, match="Snowflake Native App setup script failed validation.", ): - native_app_manager.validate(use_scratch_stage=True) + wm.perform_action( + "app_pkg", + EntityActions.VALIDATE, + interactive=False, + force=True, + use_scratch_stage=True, + ) + pd = wm._project_definition # noqa: SLF001 + pkg_model: ApplicationPackageEntityModel = pd.entities["app_pkg"] + project_root = wm._project_root # noqa: SLF001 mock_deploy.assert_called_with( console=cc, - project_root=native_app_manager.project_root, - deploy_root=native_app_manager.deploy_root, - bundle_root=native_app_manager.bundle_root, - generated_root=native_app_manager.generated_root, - artifacts=native_app_manager.artifacts, + project_root=project_root, + deploy_root=project_root / pkg_model.deploy_root, + bundle_root=project_root / pkg_model.bundle_root, + generated_root=( + project_root / pkg_model.deploy_root / pkg_model.generated_root + ), + artifacts=pkg_model.artifacts, bundle_map=None, - package_name=native_app_manager.package_name, - package_role=native_app_manager.package_role, - package_distribution=native_app_manager.package_distribution, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + package_distribution=pkg_model.distribution, prune=True, recursive=True, paths=[], print_diff=False, validate=False, - stage_fqn=native_app_manager.scratch_stage_fqn, - package_warehouse=native_app_manager.package_warehouse, - post_deploy_hooks=native_app_manager.package_post_deploy_hooks, - package_scripts=native_app_manager.package_scripts, + stage_fqn=f"{pkg_model.fqn.name}.{pkg_model.scratch_stage}", + package_warehouse=pkg_model.meta.warehouse, + post_deploy_hooks=pkg_model.meta.post_deploy, + package_scripts=[], policy=AllowAlwaysPolicy(), ) assert mock_execute.mock_calls == expected @@ -1318,7 +1489,7 @@ def test_validate_raw_returns_data(mock_execute, temp_dir, mock_cursor): create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) error_file = "@STAGE/empty.sql" @@ -1354,11 +1525,9 @@ def test_validate_raw_returns_data(mock_execute, temp_dir, mock_cursor): ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() - assert ( - native_app_manager.get_validation_result(use_scratch_stage=False) - == failure_data - ) + wm = _get_wm() + pkg = wm.get_entity("app_pkg") + assert pkg.get_validation_result(use_scratch_stage=False) == failure_data assert mock_execute.mock_calls == expected @@ -1367,7 +1536,7 @@ def test_account_event_table(mock_execute, temp_dir, mock_cursor): create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) event_table = "db.schema.event_table" @@ -1384,8 +1553,7 @@ def test_account_event_table(mock_execute, temp_dir, mock_cursor): ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() - assert native_app_manager.account_event_table == event_table + assert ApplicationEntity.get_account_event_table() == event_table @mock.patch(SQL_EXECUTOR_EXECUTE) @@ -1393,7 +1561,7 @@ def test_account_event_table_not_set_up(mock_execute, temp_dir, mock_cursor): create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) side_effects, expected = mock_execute_helper( @@ -1409,8 +1577,7 @@ def test_account_event_table_not_set_up(mock_execute, temp_dir, mock_cursor): ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() - assert native_app_manager.account_event_table == "" + assert ApplicationEntity.get_account_event_table() == "" @pytest.mark.parametrize( @@ -1518,7 +1685,7 @@ def test_get_events( create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) events = [dict(TIMESTAMP=datetime(2024, 1, 1), VALUE="test")] * 100 @@ -1551,8 +1718,14 @@ def test_get_events( mock_execute.side_effect = side_effects def get_events(): - native_app_manager = _get_na_manager() - return native_app_manager.get_events( + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities[ + "app_pkg" + ] + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] + return ApplicationEntity.get_events( + app_name=app_model.fqn.name, + package_name=pkg_model.fqn.name, since=since, until=until, record_types=types, @@ -1584,12 +1757,12 @@ def test_get_events_quoted_app_name( create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) create_named_file( file_name="snowflake.local.yml", dir_name=temp_dir, - contents=[quoted_override_yml_file], + contents=[quoted_override_yml_file_v2], ) events = [dict(TIMESTAMP=datetime(2024, 1, 1), VALUE="test")] * 100 @@ -1621,8 +1794,15 @@ def test_get_events_quoted_app_name( ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() - assert native_app_manager.get_events() == events + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] + assert ( + ApplicationEntity.get_events( + app_name=app_model.fqn.name, package_name=pkg_model.fqn.name + ) + == events + ) assert mock_execute.mock_calls == expected @@ -1634,12 +1814,16 @@ def test_get_events_no_event_table(mock_account_event_table, temp_dir, mock_curs create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - native_app_manager = _get_na_manager() + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] with pytest.raises(NoEventTableForAccount): - native_app_manager.get_events() + ApplicationEntity.get_events( + app_name=app_model.fqn.name, package_name=pkg_model.fqn.name + ) @mock.patch( @@ -1651,7 +1835,7 @@ def test_stream_events(mock_execute, mock_account_event_table, temp_dir, mock_cu create_named_file( file_name="snowflake.yml", dir_name=temp_dir, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) events = [ @@ -1739,8 +1923,15 @@ def test_stream_events(mock_execute, mock_account_event_table, temp_dir, mock_cu ) mock_execute.side_effect = side_effects - native_app_manager = _get_na_manager() - stream = native_app_manager.stream_events(interval_seconds=0, last=last) + dm = _get_dm() + pkg_model: ApplicationPackageEntityModel = dm.project_definition.entities["app_pkg"] + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] + stream = ApplicationEntity.stream_events( + app_name=app_model.fqn.name, + package_name=pkg_model.fqn.name, + interval_seconds=0, + last=last, + ) for call in events: for event in call: assert next(stream) == event diff --git a/tests/nativeapp/utils.py b/tests/nativeapp/utils.py index 7566b20e93..aa5379a8cb 100644 --- a/tests/nativeapp/utils.py +++ b/tests/nativeapp/utils.py @@ -132,6 +132,35 @@ """ ) +mock_snowflake_yml_file_v2 = dedent( + """\ + definition_version: 2 + entities: + app_pkg: + type: application package + stage: app_src.stage + manifest: app/manifest.yml + artifacts: + - setup.sql + - app/README.md + - src: app/streamlit/*.py + dest: ui/ + meta: + role: package_role + warehouse: pkg_warehouse + post_deploy: + - sql_script: shared_content.sql + myapp: + type: application + debug: true + from: + target: app_pkg + meta: + role: app_role + warehouse: app_warehouse + """ +) + quoted_override_yml_file = dedent( """\ native_app: @@ -144,6 +173,18 @@ """ ) +quoted_override_yml_file_v2 = dedent( + """\ + entities: + myapp: + identifier: >- + "My Application" + app_pkg: + identifier: >- + "My Package" + """ +) + def mock_execute_helper(mock_input: list): side_effects, expected = map(list, zip(*mock_input)) diff --git a/tests/test_data/projects/napp_project_2/001-shared.sql b/tests/test_data/projects/napp_project_2/001-shared.sql new file mode 100644 index 0000000000..f7dca95552 --- /dev/null +++ b/tests/test_data/projects/napp_project_2/001-shared.sql @@ -0,0 +1,5 @@ +-- package script (1/2) + +create schema if not exists <% ctx.entities.pkg.identifier %>.my_shared_content; +grant usage on schema <% ctx.entities.pkg.identifier %>.my_shared_content + to share in application package <% ctx.entities.pkg.identifier %>; diff --git a/tests/test_data/projects/napp_project_2/002-shared.sql b/tests/test_data/projects/napp_project_2/002-shared.sql new file mode 100644 index 0000000000..dd4a5aab69 --- /dev/null +++ b/tests/test_data/projects/napp_project_2/002-shared.sql @@ -0,0 +1,8 @@ +-- package script (2/2) + +create or replace table <% ctx.entities.pkg.identifier %>.my_shared_content.shared_table ( + col1 number, + col2 varchar +); +grant select on table <% ctx.entities.pkg.identifier %>.my_shared_content.shared_table + to share in application package <% ctx.entities.pkg.identifier %>; diff --git a/tests/test_data/projects/napp_project_2/app/README.md b/tests/test_data/projects/napp_project_2/app/README.md new file mode 100644 index 0000000000..3f08dce5d7 --- /dev/null +++ b/tests/test_data/projects/napp_project_2/app/README.md @@ -0,0 +1 @@ +app/README.md diff --git a/tests/test_data/projects/napp_project_2/app/manifest.yml b/tests/test_data/projects/napp_project_2/app/manifest.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_data/projects/napp_project_2/app/streamlit/config.py b/tests/test_data/projects/napp_project_2/app/streamlit/config.py new file mode 100644 index 0000000000..83e8ba967a --- /dev/null +++ b/tests/test_data/projects/napp_project_2/app/streamlit/config.py @@ -0,0 +1 @@ +# config.py diff --git a/tests/test_data/projects/napp_project_2/app/streamlit/main.py b/tests/test_data/projects/napp_project_2/app/streamlit/main.py new file mode 100644 index 0000000000..d03e8126a0 --- /dev/null +++ b/tests/test_data/projects/napp_project_2/app/streamlit/main.py @@ -0,0 +1 @@ +# main.py diff --git a/tests/test_data/projects/napp_project_2/setup.sql b/tests/test_data/projects/napp_project_2/setup.sql new file mode 100644 index 0000000000..97038a7d23 --- /dev/null +++ b/tests/test_data/projects/napp_project_2/setup.sql @@ -0,0 +1 @@ +create versioned schema myschema; diff --git a/tests/test_data/projects/napp_project_2/snowflake.yml b/tests/test_data/projects/napp_project_2/snowflake.yml new file mode 100644 index 0000000000..8a6a8d201f --- /dev/null +++ b/tests/test_data/projects/napp_project_2/snowflake.yml @@ -0,0 +1,24 @@ +definition_version: '2' +entities: + myapp_pkg_polly: + type: application package + artifacts: + - src: setup.sql + - src: app/README.md + - src: app/manifest.yml + - src: app/streamlit/*.py + dest: ui/ + stage: '"MySourceSchema"."SRC_Stage"' + manifest: app/manifest.yml + meta: + role: accountadmin + post_deploy: + - sql_script: 001-shared.sql + - sql_script: 002-shared.sql + myapp_polly: + type: application + debug: true + from: + target: myapp_pkg_polly + meta: + role: myapp_consumer