diff --git a/tests/functional/deprecations/fixtures.py b/tests/functional/deprecations/fixtures.py deleted file mode 100644 index 0028f206..00000000 --- a/tests/functional/deprecations/fixtures.py +++ /dev/null @@ -1,101 +0,0 @@ -models__already_exists_sql = """ -select 1 as id - -{% if adapter.already_exists(this.schema, this.identifier) and not should_full_refresh() %} - where id > (select max(id) from {{this}}) -{% endif %} -""" - -models_trivial__model_sql = """ -select 1 as id -""" - - -bad_name_yaml = """ -version: 2 - -exposures: - - name: simple exposure spaced!! - type: dashboard - depends_on: - - ref('model') - owner: - email: something@example.com -""" - -# deprecated test config fixtures -data_tests_yaml = """ -models: - - name: model - columns: - - name: id - data_tests: - - not_null -""" - -test_type_mixed_yaml = """ -models: - - name: model - columns: - - name: id - data_tests: - - not_null - tests: - - unique -""" - -old_tests_yaml = """ -models: - - name: model - columns: - - name: id - tests: - - not_null -""" - -sources_old_tests_yaml = """ -sources: - - name: seed_source - schema: "{{ var('schema_override', target.schema) }}" - tables: - - name: "seed" - columns: - - name: id - tests: - - unique -""" - -seed_csv = """id,name -1,Mary -2,Sam -3,John -""" - - -local_dependency__dbt_project_yml = """ - -name: 'local_dep' -version: '1.0' - -seeds: - quote_columns: False - -""" - -local_dependency__schema_yml = """ -sources: - - name: seed_source - schema: "{{ var('schema_override', target.schema) }}" - tables: - - name: "seed" - columns: - - name: id - tests: - - unique -""" - -local_dependency__seed_csv = """id,name -1,Mary -2,Sam -3,John -""" diff --git a/tests/functional/deprecations/model_deprecations.py b/tests/functional/deprecations/model_deprecations.py deleted file mode 100644 index c762e7a6..00000000 --- a/tests/functional/deprecations/model_deprecations.py +++ /dev/null @@ -1,106 +0,0 @@ -from dbt.cli.main import dbtRunner -from dbt.tests.util import run_dbt -from dbt_common.exceptions import EventCompilationError -import pytest - - -deprecated_model__yml = """ -version: 2 - -models: - - name: my_model - description: deprecated - deprecation_date: 1999-01-01 -""" - -deprecating_model__yml = """ -version: 2 - -models: - - name: my_model - description: deprecating in the future - deprecation_date: 2999-01-01 -""" - -model__sql = """ -select 1 as Id -""" - -dependant_model__sql = """ -select * from {{ ref("my_model") }} -""" - - -class TestModelDeprecationWarning: - @pytest.fixture(scope="class") - def models(self): - return {"my_model.sql": model__sql, "my_schema.yml": deprecated_model__yml} - - def test_deprecation_warning(self, project): - events = [] - dbtRunner(callbacks=[events.append]).invoke(["parse"]) - matches = list([e for e in events if e.info.name == "DeprecatedModel"]) - assert len(matches) == 1 - assert matches[0].read_data.model_name == "my_model" - - def test_deprecation_warning_error(self, project): - with pytest.raises(EventCompilationError): - run_dbt(["--warn-error", "parse"]) - - def test_deprecation_warning_error_options(self, project): - with pytest.raises(EventCompilationError): - run_dbt(["--warn-error-options", '{"include": ["DeprecatedModel"]}', "parse"]) - - -class TestUpcomingReferenceDeprecatingWarning: - @pytest.fixture(scope="class") - def models(self): - return { - "my_model.sql": model__sql, - "my_dependant_model.sql": dependant_model__sql, - "my_schema.yml": deprecating_model__yml, - } - - def test_deprecation_warning(self, project): - events = [] - dbtRunner(callbacks=[events.append]).invoke(["parse"]) - matches = list([e for e in events if e.info.name == "UpcomingReferenceDeprecation"]) - assert len(matches) == 1 - assert matches[0].read_data.model_name == "my_dependant_model" - assert matches[0].read_data.ref_model_name == "my_model" - - def test_deprecation_warning_error(self, project): - with pytest.raises(EventCompilationError): - run_dbt(["--warn-error", "parse"]) - - def test_deprecation_warning_error_options(self, project): - with pytest.raises(EventCompilationError): - run_dbt( - ["--warn-error-options", '{"include": ["UpcomingReferenceDeprecation"]}', "parse"] - ) - - -class TestDeprecatedReferenceWarning: - @pytest.fixture(scope="class") - def models(self): - return { - "my_model.sql": model__sql, - "my_dependant_model.sql": dependant_model__sql, - "my_schema.yml": deprecated_model__yml, - } - - def test_deprecation_warning(self, project): - events = [] - dbtRunner(callbacks=[events.append]).invoke(["parse"]) - matches = list([e for e in events if e.info.name == "DeprecatedReference"]) - assert len(matches) == 1 - assert matches[0].read_data.model_name == "my_dependant_model" - assert matches[0].read_data.ref_model_name == "my_model" - - def test_deprecation_warning_error(self, project): - with pytest.raises(EventCompilationError): - run_dbt(["--warn-error", "parse"]) - - def test_deprecation_warning_error_options(self, project): - with pytest.raises(EventCompilationError): - run_dbt(["--warn-error-options", '{"include": ["DeprecatedReference"]}', "parse"]) diff --git a/tests/functional/deprecations/test_config_deprecations.py b/tests/functional/deprecations/test_config_deprecations.py deleted file mode 100644 index f8623c1a..00000000 --- a/tests/functional/deprecations/test_config_deprecations.py +++ /dev/null @@ -1,148 +0,0 @@ -from dbt.deprecations import active_deprecations, reset_deprecations -from dbt.exceptions import ProjectContractError, YamlParseDictError -from dbt.tests.fixtures.project import write_project_files -from dbt.tests.util import run_dbt, update_config_file -from dbt_common.exceptions import CompilationError -import pytest - -from tests.functional.deprecations import fixtures - - -# test deprecation messages -class TestTestsConfigDeprecation: - @pytest.fixture(scope="class") - def models(self): - return {"model.sql": fixtures.models_trivial__model_sql} - - @pytest.fixture(scope="class") - def project_config_update(self, unique_schema): - return {"tests": {"enabled": "true"}} - - def test_tests_config(self, project): - reset_deprecations() - assert active_deprecations == set() - run_dbt(["parse"]) - expected = {"project-test-config"} - assert expected == active_deprecations - - def test_tests_config_fail(self, project): - reset_deprecations() - assert active_deprecations == set() - with pytest.raises(CompilationError) as exc: - run_dbt(["--warn-error", "--no-partial-parse", "parse"]) - exc_str = " ".join(str(exc.value).split()) # flatten all whitespace - expected_msg = "The `tests` config has been renamed to `data_tests`" - assert expected_msg in exc_str - - -class TestSchemaTestDeprecation: - @pytest.fixture(scope="class") - def models(self): - return { - "model.sql": fixtures.models_trivial__model_sql, - "schema.yml": fixtures.old_tests_yaml, - } - - def test_tests_config(self, project): - reset_deprecations() - assert active_deprecations == set() - run_dbt(["parse"]) - expected = {"project-test-config"} - assert expected == active_deprecations - - def test_schema_tests_fail(self, project): - reset_deprecations() - assert active_deprecations == set() - with pytest.raises(CompilationError) as exc: - run_dbt(["--warn-error", "--no-partial-parse", "parse"]) - exc_str = " ".join(str(exc.value).split()) # flatten all whitespace - expected_msg = "The `tests` config has been renamed to `data_tests`" - assert expected_msg in exc_str - - -class TestSourceSchemaTestDeprecation: - @pytest.fixture(scope="class") - def models(self): - return {"schema.yml": fixtures.sources_old_tests_yaml} - - @pytest.fixture(scope="class") - def seeds(self): - return {"seed.csv": fixtures.seed_csv} - - def test_source_tests_config(self, project): - reset_deprecations() - assert active_deprecations == set() - run_dbt(["seed"]) - run_dbt(["parse"]) - expected = {"project-test-config"} - assert expected == active_deprecations - - def test_schema_tests(self, project): - run_dbt(["seed"]) - results = run_dbt(["test"]) - assert len(results) == 1 - - -# test for failure with test and data_tests in the same file -class TestBothSchemaTestDeprecation: - @pytest.fixture(scope="class") - def models(self): - return { - "model.sql": fixtures.models_trivial__model_sql, - "schema.yml": fixtures.test_type_mixed_yaml, - } - - def test_schema(self, project): - expected_msg = "Invalid test config: cannot have both 'tests' and 'data_tests' defined" - with pytest.raises(YamlParseDictError) as excinfo: - run_dbt(["parse"]) - assert expected_msg in str(excinfo.value) - - -# test for failure with test and data_tests in the same dbt_project.yml -class TestBothProjectTestDeprecation: - @pytest.fixture(scope="class") - def models(self): - return {"model.sql": fixtures.models_trivial__model_sql} - - def test_tests_config(self, project): - config_patch = {"tests": {"+enabled": "true"}, "data_tests": {"+tags": "super"}} - update_config_file(config_patch, project.project_root, "dbt_project.yml") - - expected_msg = "Invalid project config: cannot have both 'tests' and 'data_tests' defined" - with pytest.raises(ProjectContractError) as excinfo: - run_dbt(["parse"]) - assert expected_msg in str(excinfo.value) - - -# test a local dependency can have tests while the rest of the project uses data_tests -class TestTestConfigInDependency: - @pytest.fixture(scope="class", autouse=True) - def setUp(self, project_root): - local_dependency_files = { - "dbt_project.yml": fixtures.local_dependency__dbt_project_yml, - "models": { - "schema.yml": fixtures.local_dependency__schema_yml, - }, - "seeds": {"seed.csv": fixtures.local_dependency__seed_csv}, - } - write_project_files(project_root, "local_dependency", local_dependency_files) - - @pytest.fixture(scope="class") - def packages(self): - return {"packages": [{"local": "local_dependency"}]} - - @pytest.fixture(scope="class") - def models(self): - return { - "model.sql": fixtures.models_trivial__model_sql, - "schema.yml": fixtures.data_tests_yaml, - } - - def test_test_dep(self, project): - run_dbt(["deps"]) - run_dbt(["seed"]) - run_dbt(["run"]) - results = run_dbt(["test"]) - # 1 data_test in the dep and 1 in the project - assert len(results) == 2 diff --git a/tests/functional/deprecations/test_deprecations.py b/tests/functional/deprecations/test_deprecations.py deleted file mode 100644 index 1f4a31c2..00000000 --- a/tests/functional/deprecations/test_deprecations.py +++ /dev/null @@ -1,148 +0,0 @@ -from dbt.deprecations import active_deprecations, reset_deprecations -from dbt.tests.util import run_dbt, write_file -from dbt_common.exceptions import CompilationError -import pytest -import yaml - -from tests.functional.deprecations import fixtures - - -class TestConfigPathDeprecation: - @pytest.fixture(scope="class") - def models(self): - return {"already_exists.sql": fixtures.models_trivial__model_sql} - - @pytest.fixture(scope="class") - def project_config_update(self): - return { - "config-version": 2, - "data-paths": ["data"], - "log-path": "customlogs", - "target-path": "customtarget", - } - - def test_data_path(self, project): - reset_deprecations() - assert active_deprecations == set() - run_dbt(["debug"]) - expected = { - "project-config-data-paths", - "project-config-log-path", - "project-config-target-path", - } - assert expected == active_deprecations - - def test_data_path_fail(self, project): - reset_deprecations() - assert active_deprecations == set() - with pytest.raises(CompilationError) as exc: - run_dbt(["--warn-error", "debug"]) - exc_str = " ".join(str(exc.value).split()) # flatten all whitespace - expected_msg = "The `data-paths` config has been renamed" - assert expected_msg in exc_str - - -class TestPackageInstallPathDeprecation: - @pytest.fixture(scope="class") - def models_trivial(self): - return {"model.sql": fixtures.models_trivial__model_sql} - - @pytest.fixture(scope="class") - def project_config_update(self): - return {"config-version": 2, "clean-targets": ["dbt_modules"]} - - def test_package_path(self, project): - reset_deprecations() - assert active_deprecations == set() - run_dbt(["clean"]) - expected = {"install-packages-path"} - assert expected == active_deprecations - - def test_package_path_not_set(self, project): - reset_deprecations() - assert active_deprecations == set() - with pytest.raises(CompilationError) as exc: - run_dbt(["--warn-error", "clean"]) - exc_str = " ".join(str(exc.value).split()) # flatten all whitespace - expected_msg = "path has changed from `dbt_modules` to `dbt_packages`." - assert expected_msg in exc_str - - -class TestPackageRedirectDeprecation: - @pytest.fixture(scope="class") - def models(self): - return {"already_exists.sql": fixtures.models_trivial__model_sql} - - @pytest.fixture(scope="class") - def packages(self): - return {"packages": [{"package": "fishtown-analytics/dbt_utils", "version": "0.7.0"}]} - - def test_package_redirect(self, project): - reset_deprecations() - assert active_deprecations == set() - run_dbt(["deps"]) - expected = {"package-redirect"} - assert expected == active_deprecations - - # if this test comes before test_package_redirect it will raise an exception as expected - def test_package_redirect_fail(self, project): - reset_deprecations() - assert active_deprecations == set() - with pytest.raises(CompilationError) as exc: - run_dbt(["--warn-error", "deps"]) - exc_str = " ".join(str(exc.value).split()) # flatten all whitespace - expected_msg = "The `fishtown-analytics/dbt_utils` package is deprecated in favor of `dbt-labs/dbt_utils`" - assert expected_msg in exc_str - - -class TestExposureNameDeprecation: - @pytest.fixture(scope="class") - def models(self): - return { - "model.sql": fixtures.models_trivial__model_sql, - "bad_name.yml": fixtures.bad_name_yaml, - } - - def test_exposure_name(self, project): - reset_deprecations() - assert active_deprecations == set() - run_dbt(["parse"]) - expected = {"exposure-name"} - assert expected == active_deprecations - - def test_exposure_name_fail(self, project): - reset_deprecations() - assert active_deprecations == set() - with pytest.raises(CompilationError) as exc: - run_dbt(["--warn-error", "--no-partial-parse", "parse"]) - exc_str = " ".join(str(exc.value).split()) # flatten all whitespace - expected_msg = "Starting in v1.3, the 'name' of an exposure should contain only letters, numbers, and underscores." - assert expected_msg in exc_str - - -class TestPrjectFlagsMovedDeprecation: - @pytest.fixture(scope="class") - def profiles_config_update(self): - return { - "config": {"send_anonymous_usage_stats": False}, - } - - @pytest.fixture(scope="class") - def dbt_project_yml(self, project_root, project_config_update): - project_config = { - "name": "test", - "profile": "test", - } - write_file(yaml.safe_dump(project_config), project_root, "dbt_project.yml") - return project_config - - @pytest.fixture(scope="class") - def models(self): - return {"my_model.sql": "select 1 as fun"} - - def test_profile_config_deprecation(self, project): - reset_deprecations() - assert active_deprecations == set() - run_dbt(["parse"]) - expected = {"project-flags-moved"} - assert expected == active_deprecations diff --git a/tests/functional/minimal_cli/fixtures.py b/tests/functional/minimal_cli/fixtures.py deleted file mode 100644 index dadfb130..00000000 --- a/tests/functional/minimal_cli/fixtures.py +++ /dev/null @@ -1,111 +0,0 @@ -import pytest -from click.testing import CliRunner - -models__schema_yml = """ -version: 2 -models: - - name: sample_model - columns: - - name: sample_num - data_tests: - - accepted_values: - values: [1, 2] - - not_null - - name: sample_bool - data_tests: - - not_null - - unique -""" - -models__sample_model = """ -select * from {{ ref('sample_seed') }} -""" - -snapshots__sample_snapshot = """ -{% snapshot orders_snapshot %} - -{{ - config( - target_database='dbt', - target_schema='snapshots', - unique_key='sample_num', - strategy='timestamp', - updated_at='updated_at', - ) -}} - -select * from {{ ref('sample_model') }} - -{% endsnapshot %} -""" - -seeds__sample_seed = """sample_num,sample_bool -1,true -2,false -,true -""" - -tests__failing_sql = """ -{{ config(severity = 'warn') }} -select 1 -""" - - -class BaseConfigProject: - @pytest.fixture() - def runner(self): - return CliRunner() - - @pytest.fixture(scope="class") - def project_config_update(self): - return { - "name": "jaffle_shop", - "profile": "jaffle_shop", - "version": "0.1.0", - "config-version": 2, - "clean-targets": ["target", "dbt_packages", "logs"], - } - - @pytest.fixture(scope="class") - def profiles_config_update(self): - return { - "jaffle_shop": { - "outputs": { - "dev": { - "type": "postgres", - "dbname": "dbt", - "schema": "jaffle_shop", - "host": "localhost", - "user": "root", - "port": 5432, - "pass": "password", - } - }, - "target": "dev", - } - } - - @pytest.fixture(scope="class") - def packages(self): - return {"packages": [{"package": "dbt-labs/dbt_utils", "version": "1.0.0"}]} - - @pytest.fixture(scope="class") - def models(self): - return { - "schema.yml": models__schema_yml, - "sample_model.sql": models__sample_model, - } - - @pytest.fixture(scope="class") - def snapshots(self): - return {"sample_snapshot.sql": snapshots__sample_snapshot} - - @pytest.fixture(scope="class") - def seeds(self): - return {"sample_seed.csv": seeds__sample_seed} - - @pytest.fixture(scope="class") - def tests(self): - return { - "failing.sql": tests__failing_sql, - } diff --git a/tests/functional/minimal_cli/test_minimal_cli.py b/tests/functional/minimal_cli/test_minimal_cli.py deleted file mode 100644 index 1fccbbd0..00000000 --- a/tests/functional/minimal_cli/test_minimal_cli.py +++ /dev/null @@ -1,52 +0,0 @@ -from dbt.cli.main import cli - -from tests.functional.minimal_cli.fixtures import BaseConfigProject -from tests.functional.utils import up_one - - -class TestClean(BaseConfigProject): - """Test the minimal/happy-path for the CLI using the Click CliRunner""" - - def test_clean(self, runner, project): - result = runner.invoke(cli, ["clean"]) - assert "target" in result.output - assert "dbt_packages" in result.output - assert "logs" in result.output - - -class TestCleanUpLevel(BaseConfigProject): - def test_clean_one_level_up(self, runner, project): - with up_one(): - result = runner.invoke(cli, ["clean"]) - assert result.exit_code == 2 - assert "Runtime Error" in result.output - assert "No dbt_project.yml" in result.output - - -class TestDeps(BaseConfigProject): - def test_deps(self, runner, project): - result = runner.invoke(cli, ["deps"]) - assert "dbt-labs/dbt_utils" in result.output - assert "1.0.0" in result.output - - -class TestBuild(BaseConfigProject): - def test_build(self, runner, project): - runner.invoke(cli, ["deps"]) - result = runner.invoke(cli, ["build"]) - # 1 seed, 1 model, 2 data tests - assert "PASS=4" in result.output - # 2 data tests - assert "ERROR=2" in result.output - # Singular test - assert "WARN=1" in result.output - # 1 snapshot - assert "SKIP=1" in result.output - - -class TestDocsGenerate(BaseConfigProject): - def test_docs_generate(self, runner, project): - runner.invoke(cli, ["deps"]) - result = runner.invoke(cli, ["docs", "generate"]) - assert "Building catalog" in result.output - assert "Catalog written" in result.output diff --git a/tests/functional/test_dbt_runner.py b/tests/functional/test_dbt_runner.py deleted file mode 100644 index d3db2d20..00000000 --- a/tests/functional/test_dbt_runner.py +++ /dev/null @@ -1,73 +0,0 @@ -from unittest import mock - -from dbt.cli.exceptions import DbtUsageException -from dbt.cli.main import dbtRunner -from dbt.exceptions import DbtProjectError -import pytest - - -class TestDbtRunner: - @pytest.fixture - def dbt(self) -> dbtRunner: - return dbtRunner() - - def test_group_invalid_option(self, dbt: dbtRunner) -> None: - res = dbt.invoke(["--invalid-option"]) - assert isinstance(res.exception, DbtUsageException) - - def test_command_invalid_option(self, dbt: dbtRunner) -> None: - res = dbt.invoke(["deps", "--invalid-option"]) - assert isinstance(res.exception, DbtUsageException) - - def test_command_mutually_exclusive_option(self, dbt: dbtRunner) -> None: - res = dbt.invoke(["--warn-error", "--warn-error-options", '{"include": "all"}', "deps"]) - assert isinstance(res.exception, DbtUsageException) - res = dbt.invoke(["deps", "--warn-error", "--warn-error-options", '{"include": "all"}']) - assert isinstance(res.exception, DbtUsageException) - - def test_invalid_command(self, dbt: dbtRunner) -> None: - res = dbt.invoke(["invalid-command"]) - assert isinstance(res.exception, DbtUsageException) - - def test_invoke_version(self, dbt: dbtRunner) -> None: - dbt.invoke(["--version"]) - - def test_callbacks(self) -> None: - mock_callback = mock.MagicMock() - dbt = dbtRunner(callbacks=[mock_callback]) - # the `debug` command is one of the few commands wherein you don't need - # to have a project to run it and it will emit events - dbt.invoke(["debug"]) - mock_callback.assert_called() - - def test_invoke_kwargs(self, project, dbt): - res = dbt.invoke( - ["run"], - log_format="json", - log_path="some_random_path", - version_check=False, - profile_name="some_random_profile_name", - target_dir="some_random_target_dir", - ) - assert res.result.args["log_format"] == "json" - assert res.result.args["log_path"] == "some_random_path" - assert res.result.args["version_check"] is False - assert res.result.args["profile_name"] == "some_random_profile_name" - assert res.result.args["target_dir"] == "some_random_target_dir" - - def test_invoke_kwargs_project_dir(self, project, dbt): - res = dbt.invoke(["run"], project_dir="some_random_project_dir") - assert isinstance(res.exception, DbtProjectError) - - msg = "No dbt_project.yml found at expected path some_random_project_dir" - assert msg in res.exception.msg - - def test_invoke_kwargs_profiles_dir(self, project, dbt): - res = dbt.invoke(["run"], profiles_dir="some_random_profiles_dir") - assert isinstance(res.exception, DbtProjectError) - msg = "Could not find profile named 'test'" - assert msg in res.exception.msg - - def test_invoke_kwargs_and_flags(self, project, dbt): - res = dbt.invoke(["--log-format=text", "run"], log_format="json") - assert res.result.args["log_format"] == "json" diff --git a/tests/functional/test_init.py b/tests/functional/test_init.py deleted file mode 100644 index 1c8202b7..00000000 --- a/tests/functional/test_init.py +++ /dev/null @@ -1,493 +0,0 @@ -import os -from pathlib import Path -from unittest.mock import Mock, call, patch - -import click -from dbt_common.exceptions import DbtRuntimeError -from dbt.tests.util import run_dbt -import pytest - - -class TestInitProjectWithExistingProfilesYml: - @patch("dbt.task.init._get_adapter_plugin_names") - @patch("click.confirm") - @patch("click.prompt") - def test_init_task_in_project_with_existing_profiles_yml( - self, mock_prompt, mock_confirm, mock_get_adapter, project - ): - manager = Mock() - manager.attach_mock(mock_prompt, "prompt") - manager.attach_mock(mock_confirm, "confirm") - manager.confirm.side_effect = ["y"] - manager.prompt.side_effect = [ - 1, - "localhost", - 5432, - "test_user", - "test_password", - "test_db", - "test_schema", - 4, - ] - mock_get_adapter.return_value = [project.adapter.type()] - - run_dbt(["init"]) - - manager.assert_has_calls( - [ - call.confirm( - f"The profile test already exists in {os.path.join(project.profiles_dir, 'profiles.yml')}. Continue and overwrite it?" - ), - call.prompt( - "Which database would you like to use?\n[1] postgres\n\n(Don't see the one you want? https://docs.getdbt.com/docs/available-adapters)\n\nEnter a number", - type=click.INT, - ), - call.prompt( - "host (hostname for the instance)", default=None, hide_input=False, type=None - ), - call.prompt("port", default=5432, hide_input=False, type=click.INT), - call.prompt("user (dev username)", default=None, hide_input=False, type=None), - call.prompt("pass (dev password)", default=None, hide_input=True, type=None), - call.prompt( - "dbname (default database that dbt will build objects in)", - default=None, - hide_input=False, - type=None, - ), - call.prompt( - "schema (default schema that dbt will build objects in)", - default=None, - hide_input=False, - type=None, - ), - call.prompt("threads (1 or more)", default=1, hide_input=False, type=click.INT), - ] - ) - - with open(os.path.join(project.profiles_dir, "profiles.yml"), "r") as f: - assert ( - f.read() - == """test: - outputs: - dev: - dbname: test_db - host: localhost - pass: test_password - port: 5432 - schema: test_schema - threads: 4 - type: postgres - user: test_user - target: dev -""" - ) - - def test_init_task_in_project_specifying_profile_errors(self): - with pytest.raises(DbtRuntimeError) as error: - run_dbt(["init", "--profile", "test"], expect_pass=False) - assert "Can not init existing project with specified profile" in str(error) - - -class TestInitProjectWithoutExistingProfilesYml: - @patch("dbt.task.init._get_adapter_plugin_names") - @patch("click.prompt") - @patch.object(Path, "exists", autospec=True) - def test_init_task_in_project_without_existing_profiles_yml( - self, exists, mock_prompt, mock_get_adapter, project - ): - def exists_side_effect(path): - # Override responses on specific files, default to 'real world' if not overriden - return {"profiles.yml": False}.get(path.name, os.path.exists(path)) - - exists.side_effect = exists_side_effect - manager = Mock() - manager.attach_mock(mock_prompt, "prompt") - manager.prompt.side_effect = [ - 1, - "localhost", - 5432, - "test_user", - "test_password", - "test_db", - "test_schema", - 4, - ] - mock_get_adapter.return_value = [project.adapter.type()] - - run_dbt(["init"]) - - manager.assert_has_calls( - [ - call.prompt( - "Which database would you like to use?\n[1] postgres\n\n(Don't see the one you want? https://docs.getdbt.com/docs/available-adapters)\n\nEnter a number", - type=click.INT, - ), - call.prompt( - "host (hostname for the instance)", default=None, hide_input=False, type=None - ), - call.prompt("port", default=5432, hide_input=False, type=click.INT), - call.prompt("user (dev username)", default=None, hide_input=False, type=None), - call.prompt("pass (dev password)", default=None, hide_input=True, type=None), - call.prompt( - "dbname (default database that dbt will build objects in)", - default=None, - hide_input=False, - type=None, - ), - call.prompt( - "schema (default schema that dbt will build objects in)", - default=None, - hide_input=False, - type=None, - ), - call.prompt("threads (1 or more)", default=1, hide_input=False, type=click.INT), - ] - ) - - with open(os.path.join(project.profiles_dir, "profiles.yml"), "r") as f: - assert ( - f.read() - == """test: - outputs: - dev: - dbname: test_db - host: localhost - pass: test_password - port: 5432 - schema: test_schema - threads: 4 - type: postgres - user: test_user - target: dev -""" - ) - - @patch.object(Path, "exists", autospec=True) - def test_init_task_in_project_without_profile_yml_specifying_profile_errors(self, exists): - def exists_side_effect(path): - # Override responses on specific files, default to 'real world' if not overriden - return {"profiles.yml": False}.get(path.name, os.path.exists(path)) - - exists.side_effect = exists_side_effect - - # Even through no profiles.yml file exists, the init will not modify project.yml, - # so this errors - with pytest.raises(DbtRuntimeError) as error: - run_dbt(["init", "--profile", "test"], expect_pass=False) - assert "Could not find profile named test" in str(error) - - -class TestInitProjectWithoutExistingProfilesYmlOrTemplate: - @patch("dbt.task.init._get_adapter_plugin_names") - @patch("click.confirm") - @patch("click.prompt") - @patch.object(Path, "exists", autospec=True) - def test_init_task_in_project_without_existing_profiles_yml_or_profile_template( - self, exists, mock_prompt, mock_confirm, mock_get_adapter, project - ): - def exists_side_effect(path): - # Override responses on specific files, default to 'real world' if not overriden - return { - "profiles.yml": False, - "profile_template.yml": False, - }.get(path.name, os.path.exists(path)) - - exists.side_effect = exists_side_effect - manager = Mock() - manager.attach_mock(mock_prompt, "prompt") - manager.attach_mock(mock_confirm, "confirm") - manager.prompt.side_effect = [ - 1, - ] - mock_get_adapter.return_value = [project.adapter.type()] - run_dbt(["init"]) - manager.assert_has_calls( - [ - call.prompt( - "Which database would you like to use?\n[1] postgres\n\n(Don't see the one you want? https://docs.getdbt.com/docs/available-adapters)\n\nEnter a number", - type=click.INT, - ), - ] - ) - - with open(os.path.join(project.profiles_dir, "profiles.yml"), "r") as f: - assert ( - f.read() - == """test: - outputs: - - dev: - type: postgres - threads: [1 or more] - host: [host] - port: [port] - user: [dev_username] - pass: [dev_password] - dbname: [dbname] - schema: [dev_schema] - - prod: - type: postgres - threads: [1 or more] - host: [host] - port: [port] - user: [prod_username] - pass: [prod_password] - dbname: [dbname] - schema: [prod_schema] - - target: dev -""" - ) - - -class TestInitProjectWithProfileTemplateWithoutExistingProfilesYml: - @patch("dbt.task.init._get_adapter_plugin_names") - @patch("click.confirm") - @patch("click.prompt") - @patch.object(Path, "exists", autospec=True) - def test_init_task_in_project_with_profile_template_without_existing_profiles_yml( - self, exists, mock_prompt, mock_confirm, mock_get_adapter, project - ): - def exists_side_effect(path): - # Override responses on specific files, default to 'real world' if not overriden - return { - "profiles.yml": False, - }.get(path.name, os.path.exists(path)) - - exists.side_effect = exists_side_effect - - with open("profile_template.yml", "w") as f: - f.write( - """fixed: - type: postgres - threads: 4 - host: localhost - dbname: my_db - schema: my_schema - target: my_target -prompts: - target: - hint: 'The target name' - type: string - port: - hint: 'The port (for integer test purposes)' - type: int - default: 5432 - user: - hint: 'Your username' - pass: - hint: 'Your password' - hide_input: true""" - ) - - manager = Mock() - manager.attach_mock(mock_prompt, "prompt") - manager.attach_mock(mock_confirm, "confirm") - manager.prompt.side_effect = ["my_target", 5432, "test_username", "test_password"] - mock_get_adapter.return_value = [project.adapter.type()] - run_dbt(["init"]) - manager.assert_has_calls( - [ - call.prompt( - "target (The target name)", default=None, hide_input=False, type=click.STRING - ), - call.prompt( - "port (The port (for integer test purposes))", - default=5432, - hide_input=False, - type=click.INT, - ), - call.prompt("user (Your username)", default=None, hide_input=False, type=None), - call.prompt("pass (Your password)", default=None, hide_input=True, type=None), - ] - ) - - with open(os.path.join(project.profiles_dir, "profiles.yml"), "r") as f: - assert ( - f.read() - == """test: - outputs: - my_target: - dbname: my_db - host: localhost - pass: test_password - port: 5432 - schema: my_schema - threads: 4 - type: postgres - user: test_username - target: my_target -""" - ) - - -class TestInitInvalidProfileTemplate: - @patch("dbt.task.init._get_adapter_plugin_names") - @patch("click.confirm") - @patch("click.prompt") - def test_init_task_in_project_with_invalid_profile_template( - self, mock_prompt, mock_confirm, mock_get_adapter, project - ): - """Test that when an invalid profile_template.yml is provided in the project, - init command falls back to the target's profile_template.yml""" - with open(os.path.join(project.project_root, "profile_template.yml"), "w") as f: - f.write("""invalid template""") - - manager = Mock() - manager.attach_mock(mock_prompt, "prompt") - manager.attach_mock(mock_confirm, "confirm") - manager.confirm.side_effect = ["y"] - manager.prompt.side_effect = [ - 1, - "localhost", - 5432, - "test_username", - "test_password", - "test_db", - "test_schema", - 4, - ] - mock_get_adapter.return_value = [project.adapter.type()] - - run_dbt(["init"]) - - manager.assert_has_calls( - [ - call.confirm( - f"The profile test already exists in {os.path.join(project.profiles_dir, 'profiles.yml')}. Continue and overwrite it?" - ), - call.prompt( - "Which database would you like to use?\n[1] postgres\n\n(Don't see the one you want? https://docs.getdbt.com/docs/available-adapters)\n\nEnter a number", - type=click.INT, - ), - call.prompt( - "host (hostname for the instance)", default=None, hide_input=False, type=None - ), - call.prompt("port", default=5432, hide_input=False, type=click.INT), - call.prompt("user (dev username)", default=None, hide_input=False, type=None), - call.prompt("pass (dev password)", default=None, hide_input=True, type=None), - call.prompt( - "dbname (default database that dbt will build objects in)", - default=None, - hide_input=False, - type=None, - ), - call.prompt( - "schema (default schema that dbt will build objects in)", - default=None, - hide_input=False, - type=None, - ), - call.prompt("threads (1 or more)", default=1, hide_input=False, type=click.INT), - ] - ) - - with open(os.path.join(project.profiles_dir, "profiles.yml"), "r") as f: - assert ( - f.read() - == """test: - outputs: - dev: - dbname: test_db - host: localhost - pass: test_password - port: 5432 - schema: test_schema - threads: 4 - type: postgres - user: test_username - target: dev -""" - ) - - -class TestInitInsideOfProjectBase: - @pytest.fixture(scope="class") - def project_name(self, unique_schema): - return f"my_project_{unique_schema}" - - -class TestInitOutsideOfProjectBase: - @pytest.fixture(scope="class") - def project_name(self, unique_schema): - return f"my_project_{unique_schema}" - - @pytest.fixture(scope="class", autouse=True) - def setup(self, project): - # Start by removing the dbt_project.yml so that we're not in an existing project - os.remove(os.path.join(project.project_root, "dbt_project.yml")) - - -class TestInitInsideProjectAndSkipProfileSetup(TestInitInsideOfProjectBase): - @patch("dbt.task.init._get_adapter_plugin_names") - @patch("click.confirm") - @patch("click.prompt") - def test_init_inside_project_and_skip_profile_setup( - self, mock_prompt, mock_confirm, mock_get, project, project_name - ): - manager = Mock() - manager.attach_mock(mock_prompt, "prompt") - manager.attach_mock(mock_confirm, "confirm") - - assert Path("dbt_project.yml").exists() - - # skip interactive profile setup - run_dbt(["init", "--skip-profile-setup"]) - assert len(manager.mock_calls) == 0 - - -class TestInitOutsideOfProjectSpecifyingInvalidProfile(TestInitOutsideOfProjectBase): - @patch("dbt.task.init._get_adapter_plugin_names") - @patch("click.prompt") - def test_init_task_outside_project_specifying_invalid_profile_errors( - self, mock_prompt, mock_get_adapter, project, project_name - ): - manager = Mock() - manager.attach_mock(mock_prompt, "prompt") - manager.prompt.side_effect = [ - project_name, - ] - mock_get_adapter.return_value = [project.adapter.type()] - - with pytest.raises(DbtRuntimeError) as error: - run_dbt(["init", "--profile", "invalid"], expect_pass=False) - assert "Could not find profile named invalid" in str(error) - - manager.assert_has_calls( - [ - call.prompt("Enter a name for your project (letters, digits, underscore)"), - ] - ) - - -class TestInitOutsideOfProjectSpecifyingProfileNoProfilesYml(TestInitOutsideOfProjectBase): - @patch("dbt.task.init._get_adapter_plugin_names") - @patch("click.prompt") - def test_init_task_outside_project_specifying_profile_no_profiles_yml_errors( - self, mock_prompt, mock_get_adapter, project, project_name - ): - manager = Mock() - manager.attach_mock(mock_prompt, "prompt") - manager.prompt.side_effect = [ - project_name, - ] - mock_get_adapter.return_value = [project.adapter.type()] - - # Override responses on specific files, default to 'real world' if not overriden - original_isfile = os.path.isfile - with patch( - "os.path.isfile", - new=lambda path: {"profiles.yml": False}.get( - os.path.basename(path), original_isfile(path) - ), - ): - with pytest.raises(DbtRuntimeError) as error: - run_dbt(["init", "--profile", "test"], expect_pass=False) - assert "Could not find profile named invalid" in str(error) - - manager.assert_has_calls( - [ - call.prompt("Enter a name for your project (letters, digits, underscore)"), - ] - )