diff --git a/src/snowflake/cli/_plugins/nativeapp/entities/application.py b/src/snowflake/cli/_plugins/nativeapp/entities/application.py index 7ac087f96c..24cd288e3c 100644 --- a/src/snowflake/cli/_plugins/nativeapp/entities/application.py +++ b/src/snowflake/cli/_plugins/nativeapp/entities/application.py @@ -185,52 +185,14 @@ def deploy_package(): ) def drop_application_before_upgrade(cascade: bool = False): - if cascade: - try: - if application_objects := self.get_objects_owned_by_application( - app_name, app_role - ): - application_objects_str = self.application_objects_to_str( - application_objects - ) - workspace_ctx.console.message( - f"The following objects are owned by application {app_name} and need to be dropped:\n{application_objects_str}" - ) - except ProgrammingError as err: - if err.errno != APPLICATION_NO_LONGER_AVAILABLE: - generic_sql_error_handler(err) - workspace_ctx.console.warning( - "The application owns other objects but they could not be determined." - ) - user_prompt = "Do you want the Snowflake CLI to drop these objects, then drop the existing application object and recreate it?" - else: - user_prompt = "Do you want the Snowflake CLI to drop the existing application object and recreate it?" - - if not policy.should_proceed(user_prompt): - if is_interactive: - workspace_ctx.console.message( - "Not upgrading the application object." - ) - raise typer.Exit(0) - else: - workspace_ctx.console.message( - "Cannot upgrade the application object non-interactively without --force." - ) - raise typer.Exit(1) - try: - cascade_msg = " (cascade)" if cascade else "" - workspace_ctx.console.step( - f"Dropping application object {app_name}{cascade_msg}." - ) - cascade_sql = " cascade" if cascade else "" - sql_executor = get_sql_executor() - sql_executor.execute_query(f"drop application {app_name}{cascade_sql}") - except ProgrammingError as err: - if err.errno == APPLICATION_OWNS_EXTERNAL_OBJECTS and not cascade: - # We need to cascade the deletion, let's try again (only if we didn't try with cascade already) - return drop_application_before_upgrade(cascade=True) - else: - generic_sql_error_handler(err) + self.drop_application_before_upgrade( + console=workspace_ctx.console, + app_name=app_name, + app_role=app_role, + policy=policy, + is_interactive=is_interactive, + cascade=cascade, + ) self.deploy( console=workspace_ctx.console, @@ -800,6 +762,66 @@ def get_existing_app_info_static(app_name: str, app_role: str) -> Optional[dict] "applications", app_name, name_col=NAME_COL ) + @classmethod + def drop_application_before_upgrade( + cls, + console: AbstractConsole, + app_name: str, + app_role: str, + policy: PolicyBase, + is_interactive: bool, + cascade: bool = False, + ): + if cascade: + try: + if application_objects := cls.get_objects_owned_by_application( + app_name, app_role + ): + application_objects_str = cls.application_objects_to_str( + application_objects + ) + console.message( + f"The following objects are owned by application {app_name} and need to be dropped:\n{application_objects_str}" + ) + except ProgrammingError as err: + if err.errno != APPLICATION_NO_LONGER_AVAILABLE: + generic_sql_error_handler(err) + console.warning( + "The application owns other objects but they could not be determined." + ) + user_prompt = "Do you want the Snowflake CLI to drop these objects, then drop the existing application object and recreate it?" + else: + user_prompt = "Do you want the Snowflake CLI to drop the existing application object and recreate it?" + + if not policy.should_proceed(user_prompt): + if is_interactive: + console.message("Not upgrading the application object.") + raise typer.Exit(0) + else: + console.message( + "Cannot upgrade the application object non-interactively without --force." + ) + raise typer.Exit(1) + try: + cascade_msg = " (cascade)" if cascade else "" + console.step(f"Dropping application object {app_name}{cascade_msg}.") + cascade_sql = " cascade" if cascade else "" + sql_executor = get_sql_executor() + sql_executor.execute_query(f"drop application {app_name}{cascade_sql}") + except ProgrammingError as err: + if err.errno == APPLICATION_OWNS_EXTERNAL_OBJECTS and not cascade: + # We need to cascade the deletion, let's try again (only if we didn't try with cascade already) + return cls.drop_application_before_upgrade( + console=console, + app_name=app_name, + app_role=app_role, + policy=policy, + is_interactive=is_interactive, + cascade=True, + ) + else: + generic_sql_error_handler(err) + @classmethod def get_events( cls, diff --git a/tests/nativeapp/factories.py b/tests/nativeapp/factories.py index 234e05f321..d37eded522 100644 --- a/tests/nativeapp/factories.py +++ b/tests/nativeapp/factories.py @@ -118,6 +118,30 @@ def _create(cls, model_class, *args, **kwargs): return cls._build(model_class, *args, **kwargs) +class MetaFieldFactory(factory.DictFactory): + post_deploy = factory.List([]) + + +class EntityModelBaseFactory(factory.DictFactory): + meta = factory.SubFactory(MetaFieldFactory) + + +class ApplicationPackageEntityModelFactory(EntityModelBaseFactory): + type = "application package" # noqa: A003 + manifest = "manifest.yml" + artifacts = factory.List( + ["setup.sql", "README.md", "manifest.yml"], list_factory=ArtifactFactory + ) + + +class ApplicationEntityModelFactory(EntityModelBaseFactory): + type = "application" # noqa: A003 + fromm = factory.Dict({"target": "pkg"}) + + class Meta: + rename = {"fromm": "from"} + + @dataclass class PdfFactoryResult: yml: dict @@ -127,41 +151,24 @@ def as_json_str(self): return json.dumps(self.yml) -class PdfV10Factory(factory.DictFactory): +class _PdfFactory(factory.DictFactory): """ - Prepare PDF V1 dict and write to file. + Base class to prepare PDF dict and write to file. Returns: PdfFactoryResult - - Usage: - Create a pdf dict with definition_version: "1", native_app with faker-generated name and an empty artifacts list and - write to snowflake.yml in current directory: - - PdfV10Factory() - - Create snowflake.local.yml and write to file - - PdfV10Factory.with_filename("snowflake.local.yml")(native_app__name="my_local_name") - - Build and return yml but do not write to file: - - PdfV10Factory.build( - native_app__name="my_app", - native_app__artifacts=["setup.sql", "README.md"], - native_app__package__role="pkg_role" - ) """ - definition_version = "1" - native_app = factory.SubFactory(NativeAppFactory) env = factory.SubFactory(FactoryNoEmptyDict) _filename = "snowflake.yml" # for snowflake.local.yml @classmethod def with_filename(cls, filename): - class PdfV10FactoryWithFilename(cls): + class _PdfFactoryWithFilename(cls): _filename = filename - return PdfV10FactoryWithFilename + return _PdfFactoryWithFilename @classmethod def _build(cls, model_class, *args, **kwargs): @@ -185,10 +192,83 @@ def _create(cls, model_class, *args, **kwargs) -> PdfFactoryResult: ) +class PdfV10Factory(_PdfFactory): + """ + Prepare PDF 1.0 dict and write to file. + + Returns: + PdfFactoryResult + + Usage: + Create a PDF dict with definition_version: "1", native_app with faker-generated name and an empty artifacts list and + write to snowflake.yml in current directory: + - PdfV10Factory() + + Create snowflake.local.yml and write to file + - PdfV10Factory.with_filename("snowflake.local.yml")(native_app__name="my_local_name") + + Build and return yml but do not write to file: + - PdfV10Factory.build( + native_app__name="my_app", + native_app__artifacts=["setup.sql", "README.md"], + native_app__package__role="pkg_role" + ) + """ + + definition_version = "1" + native_app = factory.SubFactory(NativeAppFactory) + + class PdfV11Factory(PdfV10Factory): + """Override of Pdfv10Factory to set definition_version to 1.1""" + definition_version = "1.1" +class PdfV2Factory(_PdfFactory): + """ + Prepare PDF 2 dict and write to file. + + Returns: + PdfFactoryResult + + Usage: + Create a PDF dict with definition_version: "2" with empty list of entities and + write to snowflake.yml in current directory: + - PdfV2Factory() + + Create snowflake.local.yml with some entities and write to file + - PdfV2Factory.with_filename("snowflake.local.yml")( + entities=dict( + pkg=ApplicationPackageEntityModelFactory( + identifier="myapp_pkg", + ), + app=ApplicationEntityModelFactory( + identifier="myapp", + fromm__target="pkg", + ), + ) + ) + + Build and return yml but do not write to file: + - PdfV2Factory.build( + entities=dict( + pkg=ApplicationPackageEntityModelFactory( + identifier="myapp_pkg", + ), + app=ApplicationEntityModelFactory( + identifier="myapp", + fromm__target="pkg", + ), + ) + ) + """ + + definition_version = "2" + entities = factory.Dict({}) + env = factory.Dict({}) + + @dataclass class FileModel: filename: Union[str, Path] @@ -226,7 +306,7 @@ class ProjectFactoryModel: ProjectFiles = dict[str | Path, str] -class ProjectV10Factory(factory.Factory): +class _ProjectFactory(factory.Factory): """ Factory to create PDF dict, and write in working directory PDF to snowflake.yml file, and other optional files. """ @@ -234,8 +314,7 @@ class ProjectV10Factory(factory.Factory): class Meta: model = ProjectFactoryModel - pdf = factory.SubFactory(PdfV10Factory) - + pdf = factory.SubFactory(_PdfFactory) files: ProjectFiles = {} @classmethod @@ -245,6 +324,13 @@ def _create(cls, model_class, *args, **kwargs): return super()._create(model_class, *args, **kwargs) -class ProjectV11Factory(ProjectV10Factory): +class ProjectV10Factory(_ProjectFactory): + pdf = factory.SubFactory(PdfV10Factory) + +class ProjectV11Factory(ProjectV10Factory): pdf = factory.SubFactory(PdfV11Factory) + + +class ProjectV2Factory(_ProjectFactory): + pdf = factory.SubFactory(PdfV2Factory) diff --git a/tests/nativeapp/test_manager.py b/tests/nativeapp/test_manager.py index beadf762e0..f71b7cb6a9 100644 --- a/tests/nativeapp/test_manager.py +++ b/tests/nativeapp/test_manager.py @@ -86,19 +86,6 @@ from tests.testing_utils.files_and_dirs import create_named_file from tests.testing_utils.fixtures import MockConnectionCtx -mock_project_definition_override = { - "native_app": { - "application": { - "name": "sample_application_name", - "role": "sample_application_role", - }, - "package": { - "name": "sample_package_name", - "role": "sample_package_role", - }, - } -} - def _get_dm(working_dir: Optional[str] = None): return DefinitionManager(working_dir) diff --git a/tests/nativeapp/test_package_scripts.py b/tests/nativeapp/test_package_scripts.py deleted file mode 100644 index 60bc2d7e52..0000000000 --- a/tests/nativeapp/test_package_scripts.py +++ /dev/null @@ -1,315 +0,0 @@ -# Copyright (c) 2024 Snowflake Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from pathlib import Path -from textwrap import dedent -from unittest import mock - -import pytest -from click import ClickException -from snowflake.cli._plugins.nativeapp.exceptions import ( - InvalidTemplateInFileError, - MissingScriptError, -) -from snowflake.cli._plugins.nativeapp.run_processor import NativeAppRunProcessor -from snowflake.cli.api.errno import ( - DOES_NOT_EXIST_OR_CANNOT_BE_PERFORMED, - NO_WAREHOUSE_SELECTED_IN_SESSION, -) -from snowflake.cli.api.project.definition_manager import DefinitionManager -from snowflake.connector import ProgrammingError - -from tests.nativeapp.patch_utils import mock_connection -from tests.nativeapp.utils import ( - SQL_EXECUTOR_EXECUTE, - SQL_EXECUTOR_EXECUTE_QUERIES, -) -from tests.testing_utils.fixtures import MockConnectionCtx - - -def _get_na_manager(working_dir): - dm = DefinitionManager(working_dir) - return NativeAppRunProcessor( - project_definition=dm.project_definition.native_app, - project_root=dm.project_root, - ) - - -@mock.patch(SQL_EXECUTOR_EXECUTE_QUERIES) -@mock.patch(SQL_EXECUTOR_EXECUTE) -@mock_connection() -@pytest.mark.parametrize( - "project_definition_files, expected_calls", - [ - ( - "napp_project_1", # With connection warehouse, without PDF warehouse - [ - mock.call("select current_warehouse()"), - ], - ), - ( - "napp_project_with_pkg_warehouse", # With connection warehouse, with PDF warehouse - [ - mock.call("select current_warehouse()"), - mock.call("use warehouse myapp_pkg_warehouse"), - mock.call("use warehouse MockWarehouse"), - ], - ), - ], - indirect=["project_definition_files"], -) -def test_package_scripts_with_conn_info( - mock_conn, - mock_execute_query, - mock_execute_queries, - project_definition_files, - expected_calls, - mock_cursor, -): - mock_conn.return_value = MockConnectionCtx() - working_dir: Path = project_definition_files[0].parent - # Only consequential for "select current_warehouse()" - mock_execute_query.return_value = mock_cursor([("MockWarehouse",)], []) - native_app_manager = _get_na_manager(str(working_dir)) - native_app_manager._apply_package_scripts() # noqa: SLF001 - assert mock_execute_query.mock_calls == expected_calls - assert mock_execute_queries.mock_calls == [ - mock.call( - dedent( - f"""\ - -- package script (1/2) - - create schema if not exists myapp_pkg_polly.my_shared_content; - grant usage on schema myapp_pkg_polly.my_shared_content - to share in application package myapp_pkg_polly; - """ - ) - ), - mock.call( - dedent( - f"""\ - -- package script (2/2) - - create or replace table myapp_pkg_polly.my_shared_content.shared_table ( - col1 number, - col2 varchar - ); - grant select on table myapp_pkg_polly.my_shared_content.shared_table - to share in application package myapp_pkg_polly; - """ - ) - ), - ] - - -# Without connection warehouse, without PDF warehouse -@mock.patch(SQL_EXECUTOR_EXECUTE_QUERIES) -@mock.patch(SQL_EXECUTOR_EXECUTE) -@mock_connection() -@pytest.mark.parametrize("project_definition_files", ["napp_project_1"], indirect=True) -def test_package_scripts_without_conn_info_throws_error( - mock_conn, - mock_execute_query, - mock_execute_queries, - project_definition_files, - mock_cursor, -): - mock_conn.return_value = MockConnectionCtx(warehouse=None) - working_dir: Path = project_definition_files[0].parent - mock_execute_query.return_value = mock_cursor([(None,)], []) - native_app_manager = _get_na_manager(str(working_dir)) - with pytest.raises(ClickException) as err: - native_app_manager._apply_package_scripts() # noqa: SLF001 - - assert "Application package warehouse cannot be empty." in err.value.message - assert mock_execute_query.mock_calls == [] - assert mock_execute_queries.mock_calls == [] - - -# Without connection warehouse, with PDF warehouse -@mock.patch(SQL_EXECUTOR_EXECUTE_QUERIES) -@mock.patch(SQL_EXECUTOR_EXECUTE) -@mock_connection() -@pytest.mark.parametrize( - "project_definition_files", ["napp_project_with_pkg_warehouse"], indirect=True -) -def test_package_scripts_without_conn_info_succeeds( - mock_conn, - mock_execute_query, - mock_execute_queries, - project_definition_files, - mock_cursor, -): - mock_conn.return_value = MockConnectionCtx(warehouse=None) - working_dir: Path = project_definition_files[0].parent - mock_execute_query.return_value = mock_cursor([(None,)], []) - native_app_manager = _get_na_manager(str(working_dir)) - native_app_manager._apply_package_scripts() # noqa: SLF001 - - assert mock_execute_query.mock_calls == [ - mock.call("select current_warehouse()"), - mock.call("use warehouse myapp_pkg_warehouse"), - ] - assert mock_execute_queries.mock_calls == [ - mock.call( - dedent( - f"""\ - -- package script (1/2) - - create schema if not exists myapp_pkg_polly.my_shared_content; - grant usage on schema myapp_pkg_polly.my_shared_content - to share in application package myapp_pkg_polly; - """ - ) - ), - mock.call( - dedent( - f"""\ - -- package script (2/2) - - create or replace table myapp_pkg_polly.my_shared_content.shared_table ( - col1 number, - col2 varchar - ); - grant select on table myapp_pkg_polly.my_shared_content.shared_table - to share in application package myapp_pkg_polly; - """ - ) - ), - ] - - -@mock.patch(SQL_EXECUTOR_EXECUTE_QUERIES) -@mock_connection() -@pytest.mark.parametrize("project_definition_files", ["napp_project_1"], indirect=True) -def test_missing_package_script(mock_conn, mock_execute, project_definition_files): - mock_conn.return_value = MockConnectionCtx() - working_dir: Path = project_definition_files[0].parent - native_app_manager = _get_na_manager(str(working_dir)) - with pytest.raises(MissingScriptError): - (working_dir / "002-shared.sql").unlink() - native_app_manager._apply_package_scripts() # noqa: SLF001 - - # even though the second script was the one missing, nothing should be executed - assert mock_execute.mock_calls == [] - - -@mock.patch(SQL_EXECUTOR_EXECUTE_QUERIES) -@mock_connection() -@pytest.mark.parametrize("project_definition_files", ["napp_project_1"], indirect=True) -def test_invalid_package_script(mock_conn, mock_execute, project_definition_files): - mock_conn.return_value = MockConnectionCtx() - working_dir: Path = project_definition_files[0].parent - native_app_manager = _get_na_manager(str(working_dir)) - with pytest.raises(InvalidTemplateInFileError): - second_file = working_dir / "002-shared.sql" - second_file.unlink() - second_file.write_text("select * from {{ package_name") - native_app_manager._apply_package_scripts() # noqa: SLF001 - - # even though the second script was the one missing, nothing should be executed - assert mock_execute.mock_calls == [] - - -@mock.patch(SQL_EXECUTOR_EXECUTE_QUERIES) -@mock_connection() -@pytest.mark.parametrize("project_definition_files", ["napp_project_1"], indirect=True) -def test_undefined_var_package_script( - mock_conn, mock_execute, project_definition_files -): - mock_conn.return_value = MockConnectionCtx() - working_dir: Path = project_definition_files[0].parent - native_app_manager = _get_na_manager(str(working_dir)) - with pytest.raises(InvalidTemplateInFileError): - second_file = working_dir / "001-shared.sql" - second_file.unlink() - second_file.write_text("select * from {{ abc }}") - native_app_manager._apply_package_scripts() # noqa: SLF001 - - assert mock_execute.mock_calls == [] - - -@mock.patch(SQL_EXECUTOR_EXECUTE_QUERIES) -@mock.patch(SQL_EXECUTOR_EXECUTE) -@mock_connection() -@pytest.mark.parametrize("project_definition_files", ["napp_project_1"], indirect=True) -def test_package_scripts_w_missing_warehouse_exception( - mock_conn, - mock_execute_query, - mock_execute_queries, - project_definition_files, - mock_cursor, -): - mock_conn.return_value = MockConnectionCtx() - mock_execute_query.side_effect = [ - mock_cursor( - [ - ("old_wh"), - ], - [], - ), - None, - None, - ] - - mock_execute_queries.side_effect = ProgrammingError( - msg="No active warehouse selected in the current session.", - errno=NO_WAREHOUSE_SELECTED_IN_SESSION, - ) - - working_dir: Path = project_definition_files[0].parent - native_app_manager = _get_na_manager(str(working_dir)) - - with pytest.raises(ProgrammingError) as err: - native_app_manager._apply_package_scripts() # noqa: SLF001 - - assert "Please provide a warehouse for the active session role" in err.value.msg - - -@mock.patch(SQL_EXECUTOR_EXECUTE) -@mock_connection() -@pytest.mark.parametrize("project_definition_files", ["napp_project_1"], indirect=True) -def test_package_scripts_w_warehouse_access_exception( - mock_conn, - mock_execute_query, - project_definition_files, - mock_cursor, -): - side_effects = [ - mock_cursor( - [ - ("old_wh"), - ], - [], - ), - ProgrammingError( - msg="Object does not exist, or operation cannot be performed.", - errno=DOES_NOT_EXIST_OR_CANNOT_BE_PERFORMED, - ), - None, - ] - - mock_conn.return_value = MockConnectionCtx() - mock_execute_query.side_effect = side_effects - - working_dir: Path = project_definition_files[0].parent - native_app_manager = _get_na_manager(str(working_dir)) - - with pytest.raises(ProgrammingError) as err: - native_app_manager._apply_package_scripts() # noqa: SLF001 - - assert ( - "Could not use warehouse MockWarehouse. Object does not exist, or operation cannot be performed." - in err.value.msg - ) diff --git a/tests/nativeapp/test_post_deploy_for_app.py b/tests/nativeapp/test_post_deploy_for_app.py index 6fc552ea84..bcba039e9b 100644 --- a/tests/nativeapp/test_post_deploy_for_app.py +++ b/tests/nativeapp/test_post_deploy_for_app.py @@ -18,18 +18,26 @@ import pytest from pydantic import ValidationError +from snowflake.cli._plugins.nativeapp.entities.application import ( + ApplicationEntity, + ApplicationEntityModel, +) from snowflake.cli._plugins.nativeapp.exceptions import MissingScriptError -from snowflake.cli._plugins.nativeapp.run_processor import NativeAppRunProcessor +from snowflake.cli.api.console import cli_console as cc +from snowflake.cli.api.entities.utils import execute_post_deploy_hooks from snowflake.cli.api.exceptions import InvalidTemplate from snowflake.cli.api.project.definition_manager import DefinitionManager from snowflake.cli.api.project.errors import SchemaValidationError from snowflake.cli.api.project.schemas.entities.common import PostDeployHook -from tests.nativeapp.factories import ProjectV11Factory +from tests.nativeapp.factories import ( + ApplicationEntityModelFactory, + ApplicationPackageEntityModelFactory, + ProjectV2Factory, +) from tests.nativeapp.patch_utils import mock_connection from tests.nativeapp.utils import ( CLI_GLOBAL_TEMPLATE_CONTEXT, - RUN_PROCESSOR_APP_POST_DEPLOY_HOOKS, SQL_EXECUTOR_EXECUTE, SQL_EXECUTOR_EXECUTE_QUERIES, ) @@ -39,14 +47,6 @@ MOCK_CONNECTION_WH = "tests.testing_utils.fixtures.MockConnectionCtx.warehouse" -def _get_run_processor(working_dir): - dm = DefinitionManager(working_dir) - return NativeAppRunProcessor( - project_definition=dm.project_definition.native_app, - project_root=dm.project_root, - ) - - @mock.patch(SQL_EXECUTOR_EXECUTE) @mock.patch(SQL_EXECUTOR_EXECUTE_QUERIES) @mock.patch(CLI_GLOBAL_TEMPLATE_CONTEXT, new_callable=mock.PropertyMock) @@ -60,9 +60,6 @@ def test_sql_scripts( temp_dir, ): mock_conn.return_value = MockConnectionCtx() - mock_cli_ctx.return_value = { - "ctx": {"native_app": {"name": "myapp"}, "env": {"foo": "bar"}} - } post_deploy_1 = dedent( """\ -- app post-deploy script (1/2) @@ -73,13 +70,20 @@ def test_sql_scripts( ) post_deploy_2 = "-- app post-deploy script (2/2)\n" - ProjectV11Factory( - pdf__native_app__artifacts=[{"src": "app/*", "dest": "./"}], - pdf__native_app__name="myapp", - pdf__native_app__application__post_deploy=[ - {"sql_script": "scripts/app_post_deploy1.sql"}, - {"sql_script": "scripts/app_post_deploy2.sql"}, - ], + ProjectV2Factory( + pdf__entities=dict( + pkg=ApplicationPackageEntityModelFactory( + identifier="myapp_pkg", + ), + app=ApplicationEntityModelFactory( + identifier="myapp", + fromm__target="pkg", + meta__post_deploy=[ + {"sql_script": "scripts/app_post_deploy1.sql"}, + {"sql_script": "scripts/app_post_deploy2.sql"}, + ], + ), + ), pdf__env__foo="bar", files={ "scripts/app_post_deploy1.sql": post_deploy_1, @@ -87,14 +91,23 @@ def test_sql_scripts( }, ) - processor = _get_run_processor(str(temp_dir)) - - processor.execute_app_post_deploy_hooks() + dm = DefinitionManager() + mock_cli_ctx.return_value = dm.template_context + app_model: ApplicationEntityModel = dm.project_definition.entities["app"] + ApplicationEntity.execute_post_deploy_hooks( + console=cc, + project_root=dm.project_root, + post_deploy_hooks=app_model.meta.post_deploy, + app_name=app_model.fqn.name, + app_warehouse=app_model.meta.warehouse or "MockWarehouse", + ) - assert mock_execute_query.mock_calls == [ - mock.call("use database myapp_test_user"), - mock.call("use database myapp_test_user"), - ] + mock_execute_query.assert_has_calls( + [ + mock.call(f"use database {app_model.fqn.name}"), + mock.call(f"use database {app_model.fqn.name}"), + ] + ) assert mock_execute_queries.mock_calls == [ mock.call(post_deploy_1), mock.call(post_deploy_2), @@ -120,19 +133,26 @@ def test_sql_scripts_with_no_warehouse_no_database( mock_conn_wh.return_value = None mock_conn_db.return_value = None mock_conn.return_value = MockConnectionCtx(None) - mock_cli_ctx.return_value = { - "ctx": {"native_app": {"name": "myapp"}, "env": {"foo": "bar"}} - } - with project_directory("napp_post_deploy") as project_dir: - processor = _get_run_processor(str(project_dir)) - - processor.execute_app_post_deploy_hooks() + with project_directory("napp_post_deploy_v2") as project_dir: + dm = DefinitionManager() + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] + mock_cli_ctx.return_value = dm.template_context + + # Directly testing the function without the use_warehouse + # that ApplicationEntity.execute_post_deploy_hooks adds + execute_post_deploy_hooks( + console=cc, + project_root=dm.project_root, + post_deploy_hooks=app_model.meta.post_deploy, + deployed_object_type="application", + database_name=app_model.fqn.name, + ) # Verify no "use warehouse" # Verify "use database" applies to current application assert mock_execute_query.mock_calls == [ - mock.call("use database myapp_test_user"), - mock.call("use database myapp_test_user"), + mock.call("use database myapp"), + mock.call("use database myapp"), ] assert mock_execute_queries.mock_calls == [ mock.call( @@ -149,38 +169,53 @@ def test_sql_scripts_with_no_warehouse_no_database( ] +@mock.patch(SQL_EXECUTOR_EXECUTE) @mock_connection() def test_missing_sql_script( + mock_execute_query, mock_conn, project_directory, ): mock_conn.return_value = MockConnectionCtx() - with project_directory("napp_post_deploy_missing_file") as project_dir: - processor = _get_run_processor(str(project_dir)) + with project_directory("napp_post_deploy_missing_file_v2") as project_dir: + dm = DefinitionManager() + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] with pytest.raises(MissingScriptError) as err: - processor.execute_app_post_deploy_hooks() + ApplicationEntity.execute_post_deploy_hooks( + console=cc, + project_root=dm.project_root, + post_deploy_hooks=app_model.meta.post_deploy, + app_name=app_model.fqn.name, + app_warehouse=app_model.meta.warehouse or "MockWarehouse", + ) assert err.value.message == 'Script "scripts/missing.sql" does not exist' -@mock.patch(RUN_PROCESSOR_APP_POST_DEPLOY_HOOKS, new_callable=mock.PropertyMock) +@mock.patch(SQL_EXECUTOR_EXECUTE) @mock_connection() def test_invalid_hook_type( mock_conn, - mock_deploy_hooks, + mock_execute_query, project_directory, ): mock_hook = mock.Mock() mock_hook.invalid_type = "invalid_type" mock_hook.sql_script = None - mock_deploy_hooks.return_value = [mock_hook] mock_conn.return_value = MockConnectionCtx() - with project_directory("napp_post_deploy") as project_dir: - processor = _get_run_processor(str(project_dir)) + with project_directory("napp_post_deploy_v2") as project_dir: + dm = DefinitionManager() + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] with pytest.raises(ValueError) as err: - processor.execute_app_post_deploy_hooks() + ApplicationEntity.execute_post_deploy_hooks( + console=cc, + project_root=dm.project_root, + post_deploy_hooks=[mock_hook], + app_name=app_model.fqn.name, + app_warehouse=app_model.meta.warehouse or "MockWarehouse", + ) assert "Unsupported application post-deploy hook type" in str(err) @@ -220,7 +255,7 @@ def test_app_post_deploy_with_template( mock_conn.return_value = MockConnectionCtx() mock_cli_ctx.return_value = {"ctx": {"env": {"test": "test_value"}}} - with project_directory("napp_post_deploy") as project_dir: + with project_directory("napp_post_deploy_v2") as project_dir: # edit scripts/app_post_deploy1.sql to include template variables with open(project_dir / "scripts" / "app_post_deploy1.sql", "w") as f: f.write( @@ -232,14 +267,23 @@ def test_app_post_deploy_with_template( """ ) ) - processor = _get_run_processor(str(project_dir)) - - processor.execute_app_post_deploy_hooks() + dm = DefinitionManager() + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] + + ApplicationEntity.execute_post_deploy_hooks( + console=cc, + project_root=dm.project_root, + post_deploy_hooks=app_model.meta.post_deploy, + app_name=app_model.fqn.name, + app_warehouse=app_model.meta.warehouse or "MockWarehouse", + ) - assert mock_execute_query.mock_calls == [ - mock.call("use database myapp_test_user"), - mock.call("use database myapp_test_user"), - ] + mock_execute_query.assert_has_calls( + [ + mock.call(f"use database {app_model.fqn.name}"), + mock.call(f"use database {app_model.fqn.name}"), + ] + ) assert mock_execute_queries.mock_calls == [ # Verify template variables were expanded correctly mock.call( @@ -270,7 +314,7 @@ def test_app_post_deploy_with_mixed_syntax_template( mock_conn.return_value = MockConnectionCtx() mock_cli_ctx.return_value = {"ctx": {"env": {"test": "test_value"}}} - with project_directory("napp_post_deploy") as project_dir: + with project_directory("napp_post_deploy_v2") as project_dir: # edit scripts/app_post_deploy1.sql to include template variables with open(project_dir / "scripts" / "app_post_deploy1.sql", "w") as f: f.write( @@ -283,10 +327,17 @@ def test_app_post_deploy_with_mixed_syntax_template( """ ) ) - processor = _get_run_processor(str(project_dir)) + dm = DefinitionManager() + app_model: ApplicationEntityModel = dm.project_definition.entities["myapp"] with pytest.raises(InvalidTemplate) as err: - processor.execute_app_post_deploy_hooks() + ApplicationEntity.execute_post_deploy_hooks( + console=cc, + project_root=dm.project_root, + post_deploy_hooks=app_model.meta.post_deploy, + app_name=app_model.fqn.name, + app_warehouse=app_model.meta.warehouse or "MockWarehouse", + ) assert ( "The SQL query in scripts/app_post_deploy1.sql mixes &{ ... } syntax and <% ... %> syntax." diff --git a/tests/nativeapp/test_run_processor.py b/tests/nativeapp/test_run_processor.py index 93698ade60..47a2fd259e 100644 --- a/tests/nativeapp/test_run_processor.py +++ b/tests/nativeapp/test_run_processor.py @@ -24,6 +24,14 @@ LOOSE_FILES_MAGIC_VERSION, SPECIAL_COMMENT, ) +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 ( ApplicationCreatedExternallyError, ApplicationPackageDoesNotExistError, @@ -32,14 +40,15 @@ AllowAlwaysPolicy, AskAlwaysPolicy, DenyAlwaysPolicy, -) -from snowflake.cli._plugins.nativeapp.run_processor import ( - NativeAppRunProcessor, + PolicyBase, ) from snowflake.cli._plugins.nativeapp.same_account_install_method import ( SameAccountInstallMethod, ) from snowflake.cli._plugins.stage.diff import DiffResult +from snowflake.cli._plugins.workspace.manager import WorkspaceManager +from snowflake.cli.api.console import cli_console as cc +from snowflake.cli.api.entities.common import EntityActions from snowflake.cli.api.errno import ( APPLICATION_NO_LONGER_AVAILABLE, APPLICATION_OWNS_EXTERNAL_OBJECTS, @@ -49,6 +58,7 @@ NO_WAREHOUSE_SELECTED_IN_SESSION, ) 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 @@ -62,38 +72,67 @@ SQL_EXECUTOR_EXECUTE, TYPER_CONFIRM, mock_execute_helper, - mock_snowflake_yml_file, - quoted_override_yml_file, + mock_snowflake_yml_file_v2, + quoted_override_yml_file_v2, ) from tests.testing_utils.files_and_dirs import create_named_file from tests.testing_utils.fixtures import MockConnectionCtx -mock_project_definition_override = { - "native_app": { - "application": { - "name": "sample_application_name", - "role": "sample_application_role", - }, - "package": { - "name": "sample_package_name", - "role": "sample_package_role", - }, - } -} - allow_always_policy = AllowAlwaysPolicy() ask_always_policy = AskAlwaysPolicy() deny_always_policy = DenyAlwaysPolicy() -def _get_na_run_processor(): +def _get_wm(): dm = DefinitionManager() - return NativeAppRunProcessor( - project_definition=dm.project_definition.native_app, + return WorkspaceManager( + project_definition=dm.project_definition, project_root=dm.project_root, ) +def _create_or_upgrade_app( + policy: PolicyBase, + install_method: SameAccountInstallMethod, + is_interactive: bool = False, + package_id: str = "app_pkg", + app_id: str = "myapp", +): + dm = DefinitionManager() + pd = dm.project_definition + pkg_model: ApplicationPackageEntityModel = pd.entities[package_id] + app_model: ApplicationEntityModel = pd.entities[app_id] + stage_fqn = f"{pkg_model.fqn.name}.{pkg_model.stage}" + + def drop_application_before_upgrade(cascade: bool = False): + ApplicationEntity.drop_application_before_upgrade( + console=cc, + app_name=app_model.fqn.identifier, + app_role=app_model.meta.role, + policy=policy, + is_interactive=is_interactive, + cascade=cascade, + ) + + return ApplicationEntity.create_or_upgrade_app( + console=cc, + project_root=dm.project_root, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + app_name=app_model.fqn.identifier, + app_role=app_model.meta.role, + app_warehouse=app_model.meta.warehouse, + stage_schema=extract_schema(stage_fqn), + stage_fqn=stage_fqn, + debug_mode=app_model.debug, + policy=policy, + install_method=install_method, + is_interactive=is_interactive, + post_deploy_hooks=app_model.meta.post_deploy, + drop_application_before_upgrade=drop_application_before_upgrade, + ) + + # Test create_dev_app with exception thrown trying to use the warehouse @mock.patch(SQL_EXECUTOR_EXECUTE) @mock_connection() @@ -133,14 +172,13 @@ def test_create_dev_app_w_warehouse_access_exception( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() assert not mock_diff_result.has_changes() with pytest.raises(ProgrammingError) as err: - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev(), ) @@ -195,12 +233,11 @@ def test_create_dev_app_create_new_w_no_additional_privileges( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file.replace("package_role", "app_role")], + contents=[mock_snowflake_yml_file_v2.replace("package_role", "app_role")], ) - run_processor = _get_na_run_processor() assert not mock_diff_result.has_changes() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev() ) assert mock_execute.mock_calls == expected @@ -290,12 +327,11 @@ def test_create_or_upgrade_dev_app_with_warning( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file.replace("package_role", "app_role")], + contents=[mock_snowflake_yml_file_v2.replace("package_role", "app_role")], ) - run_processor = _get_na_run_processor() assert not mock_diff_result.has_changes() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev() ) assert mock_execute.mock_calls == expected @@ -370,12 +406,11 @@ def test_create_dev_app_create_new_with_additional_privileges( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() assert not mock_diff_result.has_changes() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev() ) assert mock_execute_query.mock_calls == mock_execute_query_expected @@ -428,14 +463,13 @@ def test_create_dev_app_create_new_w_missing_warehouse_exception( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file.replace("package_role", "app_role")], + contents=[mock_snowflake_yml_file_v2.replace("package_role", "app_role")], ) - run_processor = _get_na_run_processor() assert not mock_diff_result.has_changes() with pytest.raises(ProgrammingError) as err: - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev(), ) @@ -495,13 +529,12 @@ def test_create_dev_app_incorrect_properties( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) with pytest.raises(ApplicationCreatedExternallyError): - run_processor = _get_na_run_processor() assert not mock_diff_result.has_changes() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev(), ) @@ -555,13 +588,12 @@ def test_create_dev_app_incorrect_owner( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) with pytest.raises(ProgrammingError): - run_processor = _get_na_run_processor() assert not mock_diff_result.has_changes() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev(), ) @@ -613,12 +645,11 @@ def test_create_dev_app_no_diff_changes( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() assert not mock_diff_result.has_changes() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev() ) assert mock_execute.mock_calls == expected @@ -668,12 +699,11 @@ def test_create_dev_app_w_diff_changes( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() assert mock_diff_result.has_changes() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev() ) assert mock_execute.mock_calls == expected @@ -725,14 +755,13 @@ def test_create_dev_app_recreate_w_missing_warehouse_exception( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() assert mock_diff_result.has_changes() with pytest.raises(ProgrammingError) as err: - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev(), ) @@ -787,41 +816,38 @@ def test_create_dev_app_create_new_quoted( contents=[ dedent( """\ - definition_version: 1 - native_app: - name: '"My Native Application"' - - source_stage: - app_src.stage - - artifacts: - - setup.sql - - app/README.md - - src: app/streamlit/*.py - dest: ui/ - - - application: - name: >- - "My Application" - role: app_role - warehouse: app_warehouse + definition_version: 2 + entities: + app_pkg: + type: application package + identifier: '"My Package"' + artifacts: + - setup.sql + - app/README.md + - src: app/streamlit/*.py + dest: ui/ + manifest: app/manifest.yml + stage: app_src.stage + meta: + role: app_role + post_deploy: + - sql_script: shared_content.sql + myapp: + type: application + identifier: '"My Application"' debug: true - - package: - name: >- - "My Package" - role: app_role - scripts: - - shared_content.sql + from: + target: app_pkg + meta: + role: app_role + warehouse: app_warehouse """ ) ], ) - run_processor = _get_na_run_processor() assert not mock_diff_result.has_changes() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev() ) assert mock_execute.mock_calls == expected @@ -870,17 +896,16 @@ def test_create_dev_app_create_new_quoted_override( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file.replace("package_role", "app_role")], + contents=[mock_snowflake_yml_file_v2.replace("package_role", "app_role")], ) create_named_file( file_name="snowflake.local.yml", dir_name=current_working_directory, - contents=[quoted_override_yml_file], + contents=[quoted_override_yml_file_v2], ) - run_processor = _get_na_run_processor() assert not mock_diff_result.has_changes() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev() ) assert mock_execute.mock_calls == expected @@ -969,11 +994,10 @@ def test_create_dev_app_recreate_app_when_orphaned( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev() ) assert mock_execute.mock_calls == expected @@ -1080,11 +1104,10 @@ def test_create_dev_app_recreate_app_when_orphaned_requires_cascade( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev() ) assert mock_execute.mock_calls == expected @@ -1187,11 +1210,10 @@ def test_create_dev_app_recreate_app_when_orphaned_requires_cascade_unknown_obje create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=MagicMock(), install_method=SameAccountInstallMethod.unversioned_dev() ) assert mock_execute.mock_calls == expected @@ -1238,12 +1260,11 @@ def test_upgrade_app_warehouse_error( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() with pytest.raises(ProgrammingError): - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy_param, is_interactive=True, install_method=SameAccountInstallMethod.release_directive(), @@ -1301,12 +1322,11 @@ def test_upgrade_app_incorrect_owner( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() with pytest.raises(ProgrammingError): - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=policy_param, is_interactive=True, install_method=SameAccountInstallMethod.release_directive(), @@ -1358,11 +1378,10 @@ def test_upgrade_app_succeeds( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=policy_param, is_interactive=True, install_method=SameAccountInstallMethod.release_directive(), @@ -1419,12 +1438,11 @@ def test_upgrade_app_fails_generic_error( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() with pytest.raises(ProgrammingError): - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=policy_param, is_interactive=True, install_method=SameAccountInstallMethod.release_directive(), @@ -1490,12 +1508,11 @@ def test_upgrade_app_fails_upgrade_restriction_error( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() with pytest.raises(typer.Exit): - result = run_processor.create_or_upgrade_app( + result = _create_or_upgrade_app( policy_param, is_interactive=is_interactive_param, install_method=SameAccountInstallMethod.release_directive(), @@ -1589,11 +1606,10 @@ def test_versioned_app_upgrade_to_unversioned( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy=AllowAlwaysPolicy(), is_interactive=False, install_method=SameAccountInstallMethod.unversioned_dev(), @@ -1664,12 +1680,11 @@ def test_upgrade_app_fails_drop_fails( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() with pytest.raises(ProgrammingError): - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy_param, is_interactive=is_interactive_param, install_method=SameAccountInstallMethod.release_directive(), @@ -1761,11 +1776,10 @@ def test_upgrade_app_recreate_app( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() - run_processor.create_or_upgrade_app( + _create_or_upgrade_app( policy_param, is_interactive=True, install_method=SameAccountInstallMethod.release_directive(), @@ -1778,27 +1792,25 @@ def test_upgrade_app_recreate_app( APP_PACKAGE_ENTITY_GET_EXISTING_VERSION_INFO, return_value=None, ) -@pytest.mark.parametrize( - "policy_param", [allow_always_policy, ask_always_policy, deny_always_policy] -) -def test_upgrade_app_from_version_throws_usage_error_one( - mock_existing, policy_param, temp_dir, mock_bundle_map -): - +def test_upgrade_app_from_version_throws_usage_error_one(mock_existing, temp_dir): current_working_directory = os.getcwd() create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() + wm = _get_wm() with pytest.raises(UsageError): - run_processor.process( - bundle_map=mock_bundle_map, - policy=policy_param, + wm.perform_action( + "myapp", + EntityActions.DEPLOY, + from_release_directive=False, + prune=True, + recursive=True, + paths=[], + validate=False, version="v1", - is_interactive=True, ) @@ -1807,27 +1819,28 @@ def test_upgrade_app_from_version_throws_usage_error_one( APP_PACKAGE_ENTITY_GET_EXISTING_VERSION_INFO, side_effect=ApplicationPackageDoesNotExistError("app_pkg"), ) -@pytest.mark.parametrize( - "policy_param", [allow_always_policy, ask_always_policy, deny_always_policy] -) def test_upgrade_app_from_version_throws_usage_error_two( - mock_existing, policy_param, temp_dir, mock_bundle_map + mock_existing, + temp_dir, ): - current_working_directory = os.getcwd() create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], + contents=[mock_snowflake_yml_file_v2], ) - run_processor = _get_na_run_processor() + wm = _get_wm() with pytest.raises(UsageError): - run_processor.process( - bundle_map=mock_bundle_map, - policy=policy_param, + wm.perform_action( + "myapp", + EntityActions.DEPLOY, + from_release_directive=False, + prune=True, + recursive=True, + paths=[], + validate=False, version="v1", - is_interactive=True, ) @@ -1921,15 +1934,19 @@ def test_upgrade_app_recreate_app_from_version( create_named_file( file_name="snowflake.yml", dir_name=current_working_directory, - contents=[mock_snowflake_yml_file], - ) - - run_processor = _get_na_run_processor() - run_processor.process( - bundle_map=mock_bundle_map, - policy=policy_param, + contents=[mock_snowflake_yml_file_v2], + ) + + wm = _get_wm() + wm.perform_action( + "myapp", + EntityActions.DEPLOY, + from_release_directive=False, + prune=True, + recursive=True, + paths=[], + validate=False, version="v1", - is_interactive=True, ) assert mock_execute.mock_calls == expected @@ -1972,10 +1989,16 @@ def test_get_existing_version_info(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], ) - processor = _get_na_run_processor() - result = processor.get_existing_version_info(version) + dm = DefinitionManager() + pd = dm.project_definition + pkg_model: ApplicationPackageEntityModel = pd.entities["app_pkg"] + result = ApplicationPackageEntity.get_existing_version_info( + version=version, + package_name=pkg_model.fqn.name, + package_role=pkg_model.meta.role, + ) assert mock_execute.mock_calls == expected assert result["version"] == version