Skip to main content

databricks-isv-testing

How to run and structure auth tests for Databricks ISV integrations: clean env per test, runner script patterns, U2M browser flows, and language-specific considerations.

Source facts

Repository
databricks-solutions/partner-ai-dev-kit
Last source activity
April 15, 2026 at 00:36
Detected SKILL.md language
English
Stars
5
Forks
1

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
databricks-isv-testing
description
How to run and structure auth tests for Databricks ISV integrations: clean env per test, runner script patterns, U2M browser flows, and language-specific considerations.
<!-- skill-version: 1.0.0 --> # Testing Auth for ISV Integrations Use this skill when **running or designing tests** for Databricks partner authentication (PAT, OAuth M2M, U2M). ## Core Principle: Clean Environment Per Test **Never run multiple auth types in one process** with mixed environment variables. SDKs and drivers error with "more than one authorization method configured" if conflicting credentials are set. ### Option A: `env -i` (Automated Test Runners) ```bash env -i PATH="$PATH" HOME="$HOME" \ DATABRICKS_HOST="https://myworkspace.cloud.databricks.com" \ DATABRICKS_TOKEN="dapi..." \ python your_pat_example.py ``` **Caveat:** In long interactive sessions, `env -i` may strip needed vars like `PYTHONPATH`. Use Option B for manual testing. ### Option B: Fresh Bash Subshell (Interactive Testing) ```bash /bin/bash -c ' export DATABRICKS_HOST="https://myworkspace.cloud.databricks.com" export DATABRICKS_TOKEN="dapi..." unset DATABRICKS_CLIENT_ID DATABRICKS_CLIENT_SECRET export PYTHONUNBUFFERED=1 python your_pat_example.py ' ``` Starts a completely clean child process. More reliable for interactive manual testing. --- ## Environment Variables by Auth Type | Auth Type | Selector Value | Required Variables | Must NOT Be Set | |-----------|----------------|--------------------|-----------------| | PAT | `pat` | `DATABRICKS_HOST`, `DATABRICKS_TOKEN` | `CLIENT_ID`, `CLIENT_SECRET` | | OAuth M2M | `oauth_m2m` | `DATABRICKS_HOST`, `DATABRICKS_CLIENT_ID`, `DATABRICKS_CLIENT_SECRET` | `DATABRICKS_TOKEN` | | U2M External Browser | `u2m_external_browser` | `DATABRICKS_HOST` | `CLIENT_ID`, `CLIENT_SECRET`, `TOKEN` | | U2M Custom OAuth App | `u2m_custom_oauth_app` | `DATABRICKS_HOST`, `DATABRICKS_U2M_CLIENT_ID` | `DATABRICKS_CLIENT_ID` | | U2M Token-Env | `u2m_token_env` | `DATABRICKS_HOST`, `DATABRICKS_ACCESS_TOKEN` | `CLIENT_ID`, `CLIENT_SECRET` | **Optional for all:** `DATABRICKS_HTTP_PATH` (SQL drivers), `DATABRICKS_WAREHOUSE_ID` (Statement Execution API). **Optional for U2M Custom OAuth App:** `DATABRICKS_U2M_CLIENT_SECRET` (if confidential app), `DATABRICKS_REDIRECT_URI` (default varies by connector). ### CLIENT_ID Distinction (Critical) | Env Var | Auth Type | What It Is | |---------|-----------|------------| | `DATABRICKS_CLIENT_ID` | `oauth_m2m` | M2M Service Principal UUID | | `DATABRICKS_U2M_CLIENT_ID` | `u2m_custom_oauth_app` | Custom OAuth App from App Connections | Using the wrong one causes `"OAuth application with client_id not available"`. --- ## Test Runner Script Pattern A well-structured test runner: 1. **Sources credentials once** from an env file 2. **Runs each test with isolated env** using `env -i` plus only required vars 3. **Records pass/fail** per test 4. **Prints summary** at the end ### Example Runner Structure ```bash #!/usr/bin/env bash set -euo pipefail # Load credentials source "${1:-credentials.env}" PASSED=0 FAILED=0 run_test() { local name="$1" local cmd="$2" echo "Running: $name" if eval "$cmd" > /dev/null 2>&1; then echo " PASS" ((PASSED++)) else echo " FAIL" ((FAILED++)) fi } # PAT Test run_test "PAT" "env -i PATH=\"$PATH\" HOME=\"$HOME\" \ DATABRICKS_HOST=\"$DATABRICKS_HOST\" \ DATABRICKS_TOKEN=\"$DATABRICKS_TOKEN\" \ python your_pat_example.py" # OAuth M2M Test run_test "OAuth M2M" "env -i PATH=\"$PATH\" HOME=\"$HOME\" \ DATABRICKS_HOST=\"$DATABRICKS_HOST\" \ DATABRICKS_CLIENT_ID=\"$DATABRICKS_CLIENT_ID\" \ DATABRICKS_CLIENT_SECRET=\"$DATABRICKS_CLIENT_SECRET\" \ python your_m2m_example.py" # Summary echo "" echo "Results: $PASSED passed, $FAILED failed" ``` --- ## U2M Browser Test Considerations ### Built-in OAuth App (External Browser) - Do NOT pass M2M credentials (`DATABRICKS_CLIENT_ID`/`DATABRICKS_CLIENT_SECRET`) - The SDK uses the built-in `databricks-cli` OAuth app - Redirect URI is typically `http://localhost:8020` ```bash env -i PATH="$PATH" HOME="$HOME" \ DATABRICKS_HOST="$DATABRICKS_HOST" \ USER="$USER" DISPLAY="${DISPLAY:-}" \ python your_u2m_browser_example.py ``` ### Custom OAuth App (PKCE) - Use `DATABRICKS_U2M_CLIENT_ID` (NOT `DATABRICKS_CLIENT_ID`) - Optionally set `DATABRICKS_U2M_CLIENT_SECRET` and `DATABRICKS_REDIRECT_URI` - Default redirect URI is typically `http://localhost:8040/callback` ```bash env -i PATH="$PATH" HOME="$HOME" \ DATABRICKS_HOST="$DATABRICKS_HOST" \ DATABRICKS_U2M_CLIENT_ID="$DATABRICKS_U2M_CLIENT_ID" \ DATABRICKS_U2M_CLIENT_SECRET="$DATABRICKS_U2M_CLIENT_SECRET" \ DATABRICKS_REDIRECT_URI="http://localhost:8040/callback" \ USER="$USER" DISPLAY="${DISPLAY:-}" \ python your_u2m_custom_app_example.py ``` ### Port Cleanup Before U2M Tests Before running custom OAuth app tests, kill any process holding the redirect port: ```bash lsof -ti:8040 | xargs kill -9 2>/dev/null || true ``` A previous hung test can leave the port bound. --- ## HTTPServer Pitfalls (PKCE Flow Implementation) When implementing the local callback server for PKCE: ### Python Implementation Pattern ```python import threading from http.server import HTTPServer, BaseHTTPRequestHandler class CallbackHandler(BaseHTTPRequestHandler): auth_code = None callback_received = threading.Event() def do_GET(self): if "/callback" in self.path: # Extract code from query params from urllib.parse import urlparse, parse_qs query = parse_qs(urlparse(self.path).query) CallbackHandler.auth_code = query.get("code", [None])[0] self.send_response(200) self.end_headers() self.wfile.write(b"Authentication complete. You can close this tab.") CallbackHandler.callback_received.set() else: # Ignore other requests (favicon, etc.) self.send_response(204) self.end_headers() def log_message(self, format, *args): pass # Suppress logging def run_callback_server(port=8040, timeout=120): server = HTTPServer(("localhost", port), CallbackHandler) server.timeout = timeout # CRITICAL: Set timeout # Handle requests until callback received or timeout while not CallbackHandler.callback_received.is_set(): server.handle_request() server.server_close() # Use server_close(), NOT shutdown() return CallbackHandler.auth_code ``` **Key Points:** - **Always set `server.timeout`** — Without it, `handle_request()` blocks forever - **Use `server.server_close()`** — NOT `server.shutdown()` (which hangs with `handle_request()`) - **Handle multiple requests** — Browser may send favicon requests before the callback - **Use `threading.Event`** — To signal when the actual callback is received --- ## Language-Specific Notes ### Python ```bash # PAT env -i PATH="$PATH" HOME="$HOME" PYTHONUNBUFFERED=1 \ DATABRICKS_HOST="$DATABRICKS_HOST" \ DATABRICKS_TOKEN="$DATABRICKS_TOKEN" \ python your_example.py # OAuth M2M env -i PATH="$PATH" HOME="$HOME" PYTHONUNBUFFERED=1 \ DATABRICKS_HOST="$DATABRICKS_HOST" \ DATABRICKS_CLIENT_ID="$DATABRICKS_CLIENT_ID" \ DATABRICKS_CLIENT_SECRET="$DATABRICKS_CLIENT_SECRET" \ python your_example.py # U2M (browser) env -i PATH="$PATH" HOME="$HOME" USER="$USER" DISPLAY="${DISPLAY:-}" \ DATABRICKS_HOST="$DATABRICKS_HOST" \ DATABRICKS_U2M_CLIENT_ID="$DATABRICKS_U2M_CLIENT_ID" \ python your_example.py ``` ### Java - **Environment isolation:** Run with `env -i` to prevent Java SDK auto-reading conflicting env vars - **Java 17+ JDBC:** Add `--add-opens=java.base/java.nio=ALL-UNNAMED` to JVM args for Arrow-based OSS JDBC driver - **SDK Workarounds (U2M):** Must call `config.setScopes(Arrays.asList("all-apis"))` and `config.resolve()` before `config.authenticate()` ```bash export MAVEN_OPTS="--add-opens=java.base/java.nio=ALL-UNNAMED" # PAT env -i PATH="$PATH" HOME="$HOME" JAVA_HOME="$JAVA_HOME" \ DATABRICKS_HOST="$DATABRICKS_HOST" \ DATABRICKS_TOKEN="$DATABRICKS_TOKEN" \ mvn -q exec:java -Dexec.mainClass="com.example.PatExample" # OAuth M2M env -i PATH="$PATH" HOME="$HOME" JAVA_HOME="$JAVA_HOME" \ DATABRICKS_HOST="$DATABRICKS_HOST" \ DATABRICKS_CLIENT_ID="$DATABRICKS_CLIENT_ID" \ DATABRICKS_CLIENT_SECRET="$DATABRICKS_CLIENT_SECRET" \ mvn -q exec:java -Dexec.mainClass="com.example.M2MExample" ``` ### Go - **Go SDK uses `APP_AUTH_TYPE`:** The SDK reads `DATABRICKS_AUTH_TYPE` internally, so use `APP_AUTH_TYPE` for your auth selector - **Go SQL Driver uses `DATABRICKS_AUTH_TYPE`:** Safe to use as the selector - **Build once, run with `env -i`:** Build the binary first, then run with isolated env ```bash # Build once (example for Go SDK with build tags) go build -tags all_auth_example -o ./my_example ./ # PAT (Go SDK uses APP_AUTH_TYPE) env -i PATH="$PATH" HOME="$HOME" \ APP_AUTH_TYPE=pat \ DATABRICKS_HOST="$DATABRICKS_HOST" \ DATABRICKS_TOKEN="$DATABRICKS_TOKEN" \ ./my_example # OAuth M2M env -i PATH="$PATH" HOME="$HOME" \ APP_AUTH_TYPE=oauth_m2m \ DATABRICKS_HOST="$DATABRICKS_HOST" \ DATABRICKS_CLIENT_ID="$DATABRICKS_CLIENT_ID" \ DATABRICKS_CLIENT_SECRET="$DATABRICKS_CLIENT_SECRET" \ ./my_example # U2M (browser) - needs USER and DISPLAY env -i PATH="$PATH" HOME="$HOME" USER="$USER" DISPLAY="${DISPLAY:-}" \ APP_AUTH_TYPE=u2m_custom_oauth_app \ DATABRICKS_HOST="$DATABRICKS_HOST" \ DATABRICKS_U2M_CLIENT_ID="$DATABRICKS_U2M_CLIENT_ID" \ ./my_example ``` ### Node.js ```bash # PAT env -i PATH="$PATH" HOME="$HOME" \ DATABRICKS_HOST="$DATABRICKS_HOST" \ DATABRICKS_HTTP_PATH="$DATABRICKS_HTTP_PATH" \ DATABRICKS_TOKEN="$DATABRICKS_TOKEN" \ node your_example.js # OAuth M2M env -i PATH="$PATH" HOME="$HOME" \ DATABRICKS_HOST="$DATABRICKS_HOST" \ DATABRICKS_HTTP_PATH="$DATABRICKS_HTTP_PATH" \ DATABRICKS_CLIENT_ID="$DATABRICKS_CLIENT_ID" \ DATABRICKS_CLIENT_SECRET="$DATABRICKS_CLIENT_SECRET" \ node your_example.js ``` --- ## Multi-Auth Example Pattern Support multiple auth types via an environment variable selector: ```python import os auth_type = os.environ.get("APP_AUTH_TYPE", "oauth_m2m")
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub