diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 88152a5..32f11e1 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -40,6 +40,7 @@ jobs: env: SNOWFLAKE_TEST_ACCOUNT: ${{ secrets.SNOWFLAKE_TEST_ACCOUNT }} SNOWFLAKE_TEST_USER: ${{ secrets.SNOWFLAKE_TEST_USER }} + SNOWFLAKE_TEST_AUTHENTICATOR: snowflake_jwt SNOWFLAKE_TEST_PRIVATE_KEY_PATH: /tmp/snowflake_private_key.p8 SNOWFLAKE_TEST_PRIVATE_KEY_PASSPHRASE: ${{ secrets.SNOWFLAKE_TEST_PRIVATE_KEY_PASSPHRASE }} SNOWFLAKE_TEST_ROLE: ${{ secrets.SNOWFLAKE_TEST_ROLE }} @@ -61,6 +62,7 @@ jobs: env: SNOWFLAKE_TEST_ACCOUNT: ${{ secrets.SNOWFLAKE_TEST_ACCOUNT }} SNOWFLAKE_TEST_USER: ${{ secrets.SNOWFLAKE_TEST_USER }} + SNOWFLAKE_TEST_AUTHENTICATOR: snowflake_jwt SNOWFLAKE_TEST_PRIVATE_KEY_PATH: /tmp/snowflake_private_key.p8 SNOWFLAKE_TEST_PRIVATE_KEY_PASSPHRASE: ${{ secrets.SNOWFLAKE_TEST_PRIVATE_KEY_PASSPHRASE }} SNOWFLAKE_TEST_ROLE: ${{ secrets.SNOWFLAKE_TEST_ROLE }} diff --git a/.gitignore b/.gitignore index 2d35c02..bbe3e69 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ .vscode/ target/ logs/ -.venv/ \ No newline at end of file +.venv/ +.env +integration_tests/.env +integration_tests/.user.yml +integration_tests/dbt_packages/ +integration_tests/package-lock.yml \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f05fcba..0738a79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `snowflake__create_mcp_server` macro for DDL generation - `snowflake__get_drop_mcp_server_sql` macro for DROP DDL generation - Integration tests for the MCP server materialization (`mcp_server_test`), verified via `DESCRIBE MCP SERVER` captured into a table, since `GET_DDL` does not support MCP servers +- Local development setup: `integration_tests/.env` template, `scripts/run_tests.ps1` runner, and `profiles.yml` SSO support (`externalbrowser` authenticator) + +### Fixed +- Singular integration tests now declare `-- depends_on: {{ ref('cortex_agent_test') }}` so `dbt build` runs models before tests ## [0.4.2] - 2026-06-05 diff --git a/README.md b/README.md index b67e48a..a8df82a 100644 --- a/README.md +++ b/README.md @@ -257,6 +257,50 @@ Notes specific to MCP servers: Refer to the [Snowflake CREATE MCP SERVER docs](https://docs.snowflake.com/en/sql-reference/sql/create-mcp-server) for the full and up-to-date specification reference. +## Local Development + +### Prerequisites + +- PowerShell +- dbt-snowflake installed in your environment +- A Snowflake account with Cortex Agents enabled + +### Setup + +1. Copy the env template and fill in your Snowflake details: + +```powershell +# integration_tests/.env is git-ignored +``` + +Edit `integration_tests/.env`: + +``` +SNOWFLAKE_TEST_ACCOUNT=- +SNOWFLAKE_TEST_USER=you@example.com +SNOWFLAKE_TEST_ROLE= +SNOWFLAKE_TEST_DATABASE= +SNOWFLAKE_TEST_WAREHOUSE= +SNOWFLAKE_TEST_SCHEMA= +SNOWFLAKE_TEST_AUTHENTICATOR=externalbrowser +``` + +> Set `SNOWFLAKE_TEST_AUTHENTICATOR=externalbrowser` for Google SSO (a browser window will open on first connection). For key-pair auth, set it to `snowflake_jwt` and add `SNOWFLAKE_TEST_PRIVATE_KEY_PATH` and `SNOWFLAKE_TEST_PRIVATE_KEY_PASSPHRASE`. + +2. Run the integration tests: + +```powershell +.\scripts\run_tests.ps1 +``` + +This script stages a clean copy of the package to avoid a Windows path-length issue caused by dbt's recursive local package installation, then runs `dbt deps` and `dbt build` from `integration_tests/`. + +To install packages only (no build): + +```powershell +.\scripts\run_tests.ps1 -DepsOnly +``` + ## License Apache 2.0 diff --git a/integration_tests/profiles.yml b/integration_tests/profiles.yml index 7b28b5b..014631e 100644 --- a/integration_tests/profiles.yml +++ b/integration_tests/profiles.yml @@ -9,7 +9,8 @@ integration_tests: type: snowflake account: "{{ env_var('SNOWFLAKE_TEST_ACCOUNT') }}" user: "{{ env_var('SNOWFLAKE_TEST_USER') }}" - private_key_path: "{{ env_var('SNOWFLAKE_TEST_PRIVATE_KEY_PATH') }}" + authenticator: "{{ env_var('SNOWFLAKE_TEST_AUTHENTICATOR', 'externalbrowser') }}" + private_key_path: "{{ env_var('SNOWFLAKE_TEST_PRIVATE_KEY_PATH', '') }}" private_key_passphrase: "{{ env_var('SNOWFLAKE_TEST_PRIVATE_KEY_PASSPHRASE', '') }}" role: "{{ env_var('SNOWFLAKE_TEST_ROLE') }}" database: "{{ env_var('SNOWFLAKE_TEST_DATABASE') }}" diff --git a/integration_tests/tests/cortex_agent_test_has_comment.sql b/integration_tests/tests/cortex_agent_test_has_comment.sql index e03b065..b9118fc 100644 --- a/integration_tests/tests/cortex_agent_test_has_comment.sql +++ b/integration_tests/tests/cortex_agent_test_has_comment.sql @@ -1,5 +1,6 @@ -- Fails if the comment is absent from the agent DDL. -- Returns 0 rows on success (standard dbt test contract). +-- depends_on: {{ ref('cortex_agent_test') }} select 'comment missing from cortex_agent_test DDL' as error where not contains( diff --git a/integration_tests/tests/cortex_agent_test_has_feedback_proc.sql b/integration_tests/tests/cortex_agent_test_has_feedback_proc.sql index ff6cc21..72d1a8a 100644 --- a/integration_tests/tests/cortex_agent_test_has_feedback_proc.sql +++ b/integration_tests/tests/cortex_agent_test_has_feedback_proc.sql @@ -1,3 +1,4 @@ +-- depends_on: {{ ref('cortex_agent_test') }} -- Fails if the AGENT_SUBMIT_FEEDBACK stored procedure was not created by the materialization, -- or if the feedback_execute_as='owner' override was not applied. -- Returns 0 rows on success (standard dbt test contract). diff --git a/integration_tests/tests/cortex_agent_test_has_profile.sql b/integration_tests/tests/cortex_agent_test_has_profile.sql index 05d72a4..ae7e3b4 100644 --- a/integration_tests/tests/cortex_agent_test_has_profile.sql +++ b/integration_tests/tests/cortex_agent_test_has_profile.sql @@ -1,5 +1,6 @@ -- Fails if the profile display_name is absent from the agent DDL. -- Returns 0 rows on success (standard dbt test contract). +-- depends_on: {{ ref('cortex_agent_test') }} select 'profile display_name missing from cortex_agent_test DDL' as error where not contains( diff --git a/integration_tests/tests/cortex_agent_test_spec_content.sql b/integration_tests/tests/cortex_agent_test_spec_content.sql index 0d88686..ce7ed1a 100644 --- a/integration_tests/tests/cortex_agent_test_spec_content.sql +++ b/integration_tests/tests/cortex_agent_test_spec_content.sql @@ -1,3 +1,4 @@ +-- depends_on: {{ ref('cortex_agent_test') }} -- Validates that key spec sections are present in the agent DDL: -- - orchestration model name -- - token budget diff --git a/scripts/run_tests.ps1 b/scripts/run_tests.ps1 new file mode 100644 index 0000000..b768835 --- /dev/null +++ b/scripts/run_tests.ps1 @@ -0,0 +1,47 @@ +param([switch]$DepsOnly) +$ErrorActionPreference = 'Stop' + +$root = Resolve-Path "$PSScriptRoot\.." +$integrationDir = Join-Path $root "integration_tests" + +# Load env vars from .env +Get-Content (Join-Path $integrationDir ".env") | + Where-Object { $_ -notmatch '^\s*#' -and $_ -match '=' } | + ForEach-Object { + $k, $v = $_ -split '=', 2 + [System.Environment]::SetEnvironmentVariable($k.Trim(), $v.Trim(), 'Process') + } + +# Stage a clean copy of the root package WITHOUT integration_tests/. +# This breaks the recursive path dbt creates when copying ../ which includes +# integration_tests/packages.yml -> local ../ -> repeat -> WinError 206. +$stage = Join-Path $env:TEMP "dbt_cortex_agent_pkg" +Write-Host "Staging package to $stage ..." +if (Test-Path $stage) { + $empty = New-Item -ItemType Directory -Force "$env:TEMP\empty_wipe" + robocopy $empty $stage /MIR /NFL /NDL /NJH /NJS | Out-Null + Remove-Item $stage -Recurse -Force + Remove-Item $empty -Recurse -Force +} +robocopy $root $stage /E /XD integration_tests dbt_packages target .venv .git /NFL /NDL /NJH /NJS | Out-Null + +# Temporarily patch packages.yml to point at the staged copy +$pkgFile = Join-Path $integrationDir "packages.yml" +$original = Get-Content $pkgFile -Raw +$stagePath = $stage -replace '\\', '/' +$patched = $original -replace [regex]::Escape('- local: ../'), "- local: $stagePath/" +Set-Content $pkgFile $patched -NoNewline + +try { + Push-Location $integrationDir + Write-Host "Running dbt deps ..." + dbt deps + if (-not $DepsOnly) { + Write-Host "Running dbt build ..." + dbt build + } +} finally { + Pop-Location + Set-Content $pkgFile $original -NoNewline + Write-Host "packages.yml restored." +}