-
Notifications
You must be signed in to change notification settings - Fork 5
test sf cli #86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test sf cli #86
Changes from all commits
9db1001
26746b7
0eea072
218b138
cfe02e7
ba33452
db755ca
2fcf3e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,277 @@ | ||
| name: SF CLI Integration Test | ||
|
|
||
| on: | ||
| pull_request: | ||
|
|
||
| jobs: | ||
| sf-cli-integration: | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| SF_AUTOUPDATE_DISABLE: true | ||
| NO_COLOR: '1' | ||
|
|
||
| steps: | ||
| # ── Setup ───────────────────────────────────────────────────────────────── | ||
|
|
||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Python 3.11 | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.11' | ||
|
|
||
| - name: Install Poetry and Python SDK | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install poetry | ||
| make develop | ||
|
|
||
| - name: Add Poetry venv to PATH | ||
| run: echo "$(poetry env info --path)/bin" >> $GITHUB_PATH | ||
|
|
||
| - name: Set up Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: lts/* | ||
|
|
||
| - name: Install Salesforce CLI | ||
| run: npm install -g @salesforce/cli | ||
|
|
||
| - name: Install data-code-extension plugin | ||
| run: sf plugins install @salesforce/plugin-data-code-extension --force | ||
|
|
||
| - name: Set up Java 17 (required for PySpark during run) | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| distribution: temurin | ||
| java-version: '17' | ||
|
|
||
| # ── Mock Salesforce server + fake org auth ──────────────────────────────── | ||
|
|
||
| - name: Start mock Salesforce server | ||
| run: python scripts/mock_sf_server.py & | ||
| env: | ||
| MOCK_SF_PORT: '8888' | ||
|
|
||
| - name: Create fake SF CLI auth for org alias 'dev1' | ||
| run: | | ||
| sleep 1 | ||
| python - <<'PYEOF' | ||
| import json, pathlib | ||
| home = pathlib.Path.home() | ||
|
|
||
| # Auth file — @salesforce/core reads ~/.sfdx/<username>.json on Linux | ||
| # (plain-text storage, no OS keychain involved on CI runners) | ||
| sfdx_dir = home / ".sfdx" | ||
| sfdx_dir.mkdir(exist_ok=True) | ||
| auth = { | ||
| "accessToken": "00D000000000001AAA!fakeTokenForCITesting", | ||
| "instanceUrl": "http://localhost:8888", | ||
| "loginUrl": "https://login.salesforce.com", | ||
| "orgId": "00D000000000001AAA", | ||
| "userId": "005000000000001AAA", | ||
| "username": "dev1@example.com", | ||
| "clientId": "PlatformCLI", | ||
| "isDevHub": False, | ||
| "isSandbox": False, | ||
| "created": "2024-01-01T00:00:00.000Z", | ||
| "createdOrgInstance": "CS1", | ||
| } | ||
| (sfdx_dir / "dev1@example.com.json").write_text(json.dumps(auth, indent=2)) | ||
|
|
||
| # Alias mapping — write to both locations for compat across sf versions | ||
| alias_data = {"orgs": {"dev1": "dev1@example.com"}} | ||
| sf_dir = home / ".sf" | ||
| sf_dir.mkdir(exist_ok=True) | ||
| (sf_dir / "alias.json").write_text(json.dumps(alias_data)) | ||
| (sfdx_dir / "alias.json").write_text(json.dumps(alias_data)) | ||
|
|
||
| print("Fake SF CLI org auth written to ~/.sfdx/dev1@example.com.json") | ||
| PYEOF | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this command and all that follow- is there any reason to do them inline like this, as opposed to putting all of these into a python script or something and calling that from here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Migrating these commands to native Typescript will likely happen one at a time. Spelling them out this way allows for easy removal of the init commands as an example once that Typescript has been implemented leaving the other tests in place until they get migrated. Lastly if a particular test fails it's pretty easy to track down the where and why. Probably some of the fake token logic and auth logic could be extracted for portability, interesting day after thought as I was pondering your questions.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It there a way to run all these tests locally, via act maybe? |
||
|
|
||
| # ── Script: init ────────────────────────────────────────────────────────── | ||
|
|
||
| - name: '[script] init — sf data-code-extension script init --package-dir testScript' | ||
| run: | | ||
| sf data-code-extension script init --package-dir testScript || { | ||
| echo "::error::sf data-code-extension script init FAILED. Verify --package-dir is still a recognised flag and that the command exits 0 on success." | ||
| exit 1 | ||
| } | ||
|
|
||
| - name: '[script] verify init — expected files exist' | ||
| run: | | ||
| test -f testScript/payload/entrypoint.py || { | ||
| echo "::error::testScript/payload/entrypoint.py not found after init. The init command may not have copied the script template." | ||
| exit 1 | ||
| } | ||
| test -f testScript/.datacustomcode_proj/sdk_config.json || { | ||
| echo "::error::testScript/.datacustomcode_proj/sdk_config.json not found after init. The SDK config marker was not written." | ||
| exit 1 | ||
| } | ||
|
|
||
| # ── Script: scan ────────────────────────────────────────────────────────── | ||
|
|
||
| - name: '[script] scan — sf data-code-extension script scan --entrypoint testScript/payload/entrypoint.py' | ||
| run: | | ||
| sf data-code-extension script scan --entrypoint testScript/payload/entrypoint.py || { | ||
| echo "::error::sf data-code-extension script scan FAILED. Verify --entrypoint is still a recognised flag and the command exits 0." | ||
| exit 1 | ||
| } | ||
|
|
||
| - name: '[script] verify scan — config.json contains permissions' | ||
| run: | | ||
| python - <<'EOF' | ||
| import json, sys | ||
| path = "testScript/payload/config.json" | ||
| try: | ||
| with open(path) as f: | ||
| data = json.load(f) | ||
| except Exception as e: | ||
| print(f"::error::Could not read {path}: {e}") | ||
| sys.exit(1) | ||
| if "permissions" not in data: | ||
| print(f"::error::{path} is missing 'permissions' key after scan. Got: {json.dumps(data)}") | ||
| sys.exit(1) | ||
| print("config.json OK:", json.dumps(data, indent=2)) | ||
| EOF | ||
|
|
||
| # ── Script: zip ─────────────────────────────────────────────────────────── | ||
|
|
||
| - name: '[script] prepare for zip — clear requirements.txt to skip native-dep Docker build' | ||
| run: echo "" > testScript/payload/requirements.txt | ||
|
|
||
| - name: '[script] zip — sf data-code-extension script zip --package-dir testScript' | ||
| run: | | ||
| sf data-code-extension script zip --package-dir testScript || { | ||
| echo "::error::sf data-code-extension script zip FAILED. Verify --package-dir is still recognised and the command exits 0." | ||
| exit 1 | ||
| } | ||
|
|
||
| - name: '[script] verify zip — deployment.zip exists' | ||
| run: | | ||
| test -f deployment.zip || { | ||
| echo "::error::deployment.zip not found after sf data-code-extension script zip. The zip command may have written to a different path or failed silently." | ||
| exit 1 | ||
| } | ||
|
|
||
| # ── Script: run ─────────────────────────────────────────────────────────── | ||
|
|
||
| - name: '[script] run — sf data-code-extension script run --entrypoint testScript/payload/entrypoint.py -o dev1' | ||
| run: | | ||
| sf data-code-extension script run \ | ||
| --entrypoint testScript/payload/entrypoint.py \ | ||
| -o dev1 || { | ||
| echo "::error::sf data-code-extension script run FAILED. Check mock server output above for which endpoint failed. The --entrypoint flag or SF CLI org auth contract may have changed." | ||
| exit 1 | ||
| } | ||
|
|
||
| # ── Script: deploy ─────────────────────────────────────────────────────── | ||
|
|
||
| - name: '[script] deploy — sf data-code-extension script deploy' | ||
| run: | | ||
| sf data-code-extension script deploy \ | ||
| --name test-script-deploy \ | ||
| --package-version 0.0.1 \ | ||
| --description "Test script deploy" \ | ||
| --package-dir testScript/payload \ | ||
| --cpu-size CPU_2XL \ | ||
| -o dev1 || { | ||
| echo "::error::sf data-code-extension script deploy FAILED. Check mock server output above for which endpoint failed. The deploy command flags or API contract may have changed." | ||
| exit 1 | ||
| } | ||
|
|
||
| # ── Function: init ──────────────────────────────────────────────────────── | ||
|
|
||
| - name: '[function] init — sf data-code-extension function init --package-dir testFunction' | ||
| run: | | ||
| sf data-code-extension function init --package-dir testFunction || { | ||
| echo "::error::sf data-code-extension function init FAILED. Verify --package-dir is still recognised and the function template copies correctly." | ||
| exit 1 | ||
| } | ||
|
|
||
| - name: '[function] verify init — expected files exist' | ||
| run: | | ||
| test -f testFunction/payload/entrypoint.py || { | ||
| echo "::error::testFunction/payload/entrypoint.py not found after function init." | ||
| exit 1 | ||
| } | ||
| test -f testFunction/.datacustomcode_proj/sdk_config.json || { | ||
| echo "::error::testFunction/.datacustomcode_proj/sdk_config.json not found after function init." | ||
| exit 1 | ||
| } | ||
|
|
||
| # ── Function: scan ──────────────────────────────────────────────────────── | ||
|
|
||
| - name: '[function] scan — sf data-code-extension function scan --entrypoint testFunction/payload/entrypoint.py' | ||
| run: | | ||
| sf data-code-extension function scan --entrypoint testFunction/payload/entrypoint.py || { | ||
| echo "::error::sf data-code-extension function scan FAILED." | ||
| exit 1 | ||
| } | ||
|
|
||
| - name: '[function] verify scan — config.json contains entryPoint' | ||
| run: | | ||
| python - <<'EOF' | ||
| import json, sys | ||
| path = "testFunction/payload/config.json" | ||
| try: | ||
| with open(path) as f: | ||
| data = json.load(f) | ||
| except Exception as e: | ||
| print(f"::error::Could not read {path}: {e}") | ||
| sys.exit(1) | ||
| if "entryPoint" not in data: | ||
| print(f"::error::{path} is missing 'entryPoint' key after scan. Got: {json.dumps(data)}") | ||
| sys.exit(1) | ||
| print("config.json OK:", json.dumps(data, indent=2)) | ||
| EOF | ||
|
|
||
| # ── Function: zip ───────────────────────────────────────────────────────── | ||
|
|
||
| - name: '[function] prepare for zip — clear requirements.txt to skip native-dep Docker build' | ||
| run: echo "" > testFunction/payload/requirements.txt | ||
|
|
||
| - name: '[function] clean up previous deployment.zip before function zip' | ||
| run: rm -f deployment.zip | ||
|
|
||
| - name: '[function] zip — sf data-code-extension function zip --package-dir testFunction' | ||
| run: | | ||
| sf data-code-extension function zip --package-dir testFunction || { | ||
| echo "::error::sf data-code-extension function zip FAILED." | ||
| exit 1 | ||
| } | ||
|
|
||
| - name: '[function] verify zip — deployment.zip exists' | ||
| run: | | ||
| test -f deployment.zip || { | ||
| echo "::error::deployment.zip not found after sf data-code-extension function zip." | ||
| exit 1 | ||
| } | ||
|
|
||
| # ── Function: run ───────────────────────────────────────────────────────── | ||
|
|
||
| - name: '[function] run — sf data-code-extension function run --entrypoint testFunction/payload/entrypoint.py -o dev1' | ||
| run: | | ||
| sf data-code-extension function run \ | ||
| --entrypoint testFunction/payload/entrypoint.py \ | ||
| -o dev1 || { | ||
| echo "::error::sf data-code-extension function run FAILED. Check mock server output above; the --entrypoint flag or SF CLI org auth contract may have changed." | ||
| exit 1 | ||
| } | ||
|
|
||
| # ── Function: deploy ───────────────────────────────────────────────────── | ||
|
|
||
| - name: '[function] deploy — sf data-code-extension function deploy' | ||
| run: | | ||
| sf data-code-extension function deploy \ | ||
| --name test-function-deploy \ | ||
| --package-version 0.0.1 \ | ||
| --description "Test function deploy" \ | ||
| --package-dir testFunction/payload \ | ||
| --cpu-size CPU_2XL \ | ||
| --function-invoke-opt UnstructuredChunking \ | ||
| -o dev1 || { | ||
| echo "::error::sf data-code-extension function deploy FAILED. Check mock server output above for which endpoint failed. The deploy command flags or API contract may have changed." | ||
| exit 1 | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will the
&process be dangling if a test fails? Might want to add something to cleanup at the end, like: