diff --git a/.github/workflows/argus-qa.yml b/.github/workflows/argus-qa.yml deleted file mode 100644 index 21af1ad..0000000 --- a/.github/workflows/argus-qa.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Argus QA - -on: - workflow_run: - workflows: ["CLI Staging Integration"] - types: [completed] - -jobs: - trigger-argus: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} - steps: - - name: Trigger Argus QA - run: | - FIRE_AT=$(date -u -d '+30 seconds' '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || \ - date -u -v+30S '+%Y-%m-%dT%H:%M:%SZ') - RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://api.cueapi.ai/v1/cues \ - -H "Authorization: Bearer ${{ secrets.ARGUS_API_KEY }}" \ - -H "Content-Type: application/json" \ - -d "{ - \"name\": \"argus-cueapi-cli-${{ github.event.workflow_run.head_sha }}\", - \"schedule\": {\"type\": \"once\", \"at\": \"${FIRE_AT}\"}, - \"transport\": \"worker\", - \"payload\": { - \"task\": \"cli-qa\", - \"repo\": \"cueapi-cli\", - \"commit\": \"${{ github.event.workflow_run.head_sha }}\", - \"staging_url\": \"${{ secrets.CUEAPI_STAGING_URL }}\" - } - }") - HTTP_CODE=$(echo "$RESPONSE" | tail -1) - BODY=$(echo "$RESPONSE" | head -1) - echo "Response ($HTTP_CODE): $BODY" - if [ "$HTTP_CODE" != "201" ]; then - echo "Failed to create Argus cue" - exit 1 - fi - echo "Argus will pick up CLI QA cue within 15 seconds." diff --git a/.github/workflows/cli-test.yml b/.github/workflows/cli-test.yml index adafdfb..9d3fb71 100644 --- a/.github/workflows/cli-test.yml +++ b/.github/workflows/cli-test.yml @@ -17,11 +17,10 @@ jobs: with: python-version: '3.12' - - name: Install CLI - run: pip install -e . + - name: Install CLI and test deps + run: | + pip install -e . + pip install pytest - - name: Verify CLI entry point - run: cueapi --help - - - name: Verify CLI version - run: cueapi --version + - name: Run tests + run: pytest tests/ -v --tb=short diff --git a/.github/workflows/feature-to-main.yml b/.github/workflows/feature-to-main.yml new file mode 100644 index 0000000..3bd3b23 --- /dev/null +++ b/.github/workflows/feature-to-main.yml @@ -0,0 +1,56 @@ +name: Feature PR to Main + +on: + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.7 + + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.3.0 + with: + python-version: '3.12' + + - name: Install CLI and test deps + run: | + pip install -e . + pip install pytest + + - name: Run tests + run: pytest tests/ -v --tb=short + + auto-merge: + needs: test + runs-on: ubuntu-latest + steps: + - name: Auto-merge PR + run: gh pr merge ${{ github.event.pull_request.number }} --squash --auto + env: + GH_TOKEN: ${{ secrets.GOVIND_GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + + notify-merge: + needs: auto-merge + runs-on: ubuntu-latest + steps: + - name: Create merge confirmation cue + run: | + FIRE_AT=$(date -u -d '+2 minutes' '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || \ + date -u -v+2M '+%Y-%m-%dT%H:%M:%SZ') + curl -s -w "\n%{http_code}" -X POST https://api.cueapi.ai/v1/cues \ + -H "Authorization: Bearer ${{ secrets.ARGUS_CUEAPI_KEY }}" \ + -H "Content-Type: application/json" \ + -d "{ + \"name\": \"cueapi-cli-merged-${{ github.event.pull_request.number }}\", + \"schedule\": {\"type\": \"once\", \"at\": \"${FIRE_AT}\"}, + \"transport\": \"worker\", + \"payload\": { + \"task\": \"verify-github-merge\", + \"repo\": \"cueapi/cueapi-cli\", + \"pr_number\": \"${{ github.event.pull_request.number }}\", + \"commit_sha\": \"${{ github.event.pull_request.head.sha }}\" + } + }" diff --git a/.github/workflows/staging-deploy.yml b/.github/workflows/staging-deploy.yml deleted file mode 100644 index f5c44fe..0000000 --- a/.github/workflows/staging-deploy.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: CLI Staging Integration - -on: - push: - branches: [main] - -jobs: - staging-integration: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.7 - - - name: Set up Python - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.3.0 - with: - python-version: '3.12' - - - name: Install CLI - run: pip install -e . - - - name: Verify CLI works against staging - env: - CUEAPI_API_KEY: ${{ secrets.CUEAPI_STAGING_API_KEY }} - CUEAPI_BASE_URL: ${{ secrets.CUEAPI_STAGING_URL }} - run: | - cueapi --help - echo "CLI installed and responsive" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..ba7c912 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,90 @@ +"""Unit tests for cueapi CLI commands using Click's CliRunner. + +No live API calls — tests only verify CLI entry points, help text, and argument parsing. +""" + +from click.testing import CliRunner + +from cueapi.cli import main + + +runner = CliRunner() + + +def test_version(): + result = runner.invoke(main, ["--version"]) + assert result.exit_code == 0 + assert "cueapi" in result.output + assert "0." in result.output # version starts with 0.x + + +def test_help(): + result = runner.invoke(main, ["--help"]) + assert result.exit_code == 0 + assert "CueAPI" in result.output + assert "create" in result.output + assert "list" in result.output + assert "login" in result.output + assert "whoami" in result.output + assert "quickstart" in result.output + + +def test_login_help(): + result = runner.invoke(main, ["login", "--help"]) + assert result.exit_code == 0 + assert "login" in result.output.lower() or "authenticate" in result.output.lower() + + +def test_create_help(): + result = runner.invoke(main, ["create", "--help"]) + assert result.exit_code == 0 + assert "--name" in result.output + assert "--url" in result.output + + +def test_list_help(): + result = runner.invoke(main, ["list", "--help"]) + assert result.exit_code == 0 + assert "list" in result.output.lower() or "cue" in result.output.lower() + + +def test_get_help(): + result = runner.invoke(main, ["get", "--help"]) + assert result.exit_code == 0 + assert "get" in result.output.lower() or "info" in result.output.lower() + + +def test_pause_help(): + result = runner.invoke(main, ["pause", "--help"]) + assert result.exit_code == 0 + assert "pause" in result.output.lower() + + +def test_resume_help(): + result = runner.invoke(main, ["resume", "--help"]) + assert result.exit_code == 0 + assert "resume" in result.output.lower() + + +def test_delete_help(): + result = runner.invoke(main, ["delete", "--help"]) + assert result.exit_code == 0 + assert "delete" in result.output.lower() + + +def test_whoami_help(): + result = runner.invoke(main, ["whoami", "--help"]) + assert result.exit_code == 0 + assert "whoami" in result.output.lower() or "user" in result.output.lower() + + +def test_usage_help(): + result = runner.invoke(main, ["usage", "--help"]) + assert result.exit_code == 0 + assert "usage" in result.output.lower() + + +def test_quickstart_help(): + result = runner.invoke(main, ["quickstart", "--help"]) + assert result.exit_code == 0 + assert "quickstart" in result.output.lower() or "setup" in result.output.lower()