Skip to content

GHA-257 Jira fixture infrastructure — reusable setup/teardown scripts#136

Open
nils-werner-sonarsource wants to merge 2 commits intomasterfrom
nw/GHA-257
Open

GHA-257 Jira fixture infrastructure — reusable setup/teardown scripts#136
nils-werner-sonarsource wants to merge 2 commits intomasterfrom
nw/GHA-257

Conversation

@nils-werner-sonarsource
Copy link
Copy Markdown
Contributor

Summary

  • Add reusable Python scripts for creating and cleaning up Jira sandbox state for integration tests
  • test-fixtures/jira/setup.py creates a test version + 3 issues (Bug, Feature, Maintenance) linked via fixVersion
  • test-fixtures/jira/cleanup.py idempotently deletes all created resources
  • test-fixtures/jira/jira_client.py extracts the shared Jira connection pattern used by all 6 Jira actions
  • CI workflow runs unit tests and a sandbox integration test with vault credentials

Test plan

  • 24 unit tests pass locally (96% coverage)
  • CI unit tests pass on ubuntu-latest with Python 3.10
  • CI integration test creates fixtures in Jira sandbox and cleans up

🤖 Generated with Claude Code

nils-werner-sonarsource and others added 2 commits April 19, 2026 13:37
Tests for jira_client.py, setup.py, and cleanup.py covering:
- Jira connection with credential validation
- Version and issue creation with correct fields
- Idempotent cleanup that handles missing resources
- JSON output format from setup
- State file and inline argument parsing for cleanup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- jira_client.py: shared connection helper extracted from existing actions
- setup.py: creates test version + 3 issues (Bug, Feature, Maintenance)
  linked via fixVersion, outputs JSON state for cleanup
- cleanup.py: idempotent deletion of issues and versions, supports
  inline args or JSON state file from setup.py
- README.md: usage guide with GitHub Actions workflow example
- test-jira-fixtures.yml: CI workflow with unit tests + sandbox integration
- Remove __init__.py to avoid shadowing the jira package

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hashicorp-vault-sonar-prod
Copy link
Copy Markdown

hashicorp-vault-sonar-prod bot commented Apr 19, 2026

GHA-257

@sonar-review-alpha
Copy link
Copy Markdown

sonar-review-alpha bot commented Apr 19, 2026

Summary

This PR introduces reusable Jira fixture infrastructure for integration testing. It adds three Python scripts (setup.py, cleanup.py, jira_client.py) that create and teardown Jira test state (a version + 3 linked issues) with a companion CI workflow that validates the fixtures end-to-end against the sandbox. All setup/teardown is idempotent and coordinates via a JSON state file. The implementation is well-tested (24 unit tests, 96% coverage) and ready to be invoked from other workflows via workflow_call.

What reviewers should know

Start reading here:

  • .github/workflows/test-jira-fixtures.yml — shows the complete integration flow: unit tests → sandbox fixture creation → verification → cleanup
  • test-fixtures/jira/setup.py and test-fixtures/jira/cleanup.py — the core fixture scripts (short, clean)
  • test-fixtures/jira/README.md — examples of invoking these scripts from other workflows

Key design decisions to note:

  1. Shared Jira client (jira_client.py) — extracts the auth pattern (env vars, error handling) used by all scripts. Reusable.

  2. Idempotent cleanup — cleanup catches and ignores 404s, so it succeeds even if resources were already deleted or never created. The integration test marks cleanup if: always() to guarantee it runs.

  3. State coordination — setup outputs JSON (version_id, version_name, issue_keys) which cleanup can consume via --state-file. This decouples the two scripts while keeping the workflow clean.

  4. Naming convention — versions use prefix 99.<run_id> to keep sandbox pollution minimal and fixture names searchable (all test versions start with 99).

  5. Sandbox-only integration — CI uses a real Jira sandbox instance (not mocked) with credentials from Vault. Unit tests mock the JIRA client entirely.

Watch for:

  • Unit test coverage (96%) — tests include auth failures, missing env vars, idempotent deletes. Well-structured mocks.
  • State file location (/tmp/jira-fixtures.json) — appropriate for ephemeral CI runners, but worth noting if workflows share runners or persistent storage.
  • Vault integration — the CI step expects development/kv/data/jira with user and token fields. Verify this path exists in your Vault setup.

  • Generate Walkthrough
  • Generate Diagram

🗣️ Give feedback

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@sonar-review-alpha sonar-review-alpha bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-structured fixture infrastructure with solid test coverage (24 unit tests confirmed). The idempotent cleanup design and if: always() usage show good CI hygiene. One functional bug around partial setup failure needs fixing before this lands.

🗣️ Give feedback

Comment on lines +59 to +63
if args.state_file:
with open(args.state_file) as f:
state = json.load(f)
version_id = state.get('version_id', version_id)
issue_keys = state.get('issue_keys', issue_keys)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unhandled FileNotFoundError breaks cleanup when setup.py fails mid-run.

If setup.py creates the Jira version but then fails during issue creation (API error, rate limit, etc.), set -euo pipefail in the workflow aborts the step before echo "$STATE" > /tmp/jira-fixtures.json executes. The state file is never written.

The cleanup step still runs (if: always()) and unconditionally passes --state-file /tmp/jira-fixtures.json. Line 60's open() raises FileNotFoundError — unhandled — so cleanup itself exits non-zero. The orphaned version and any partially-created issues are never cleaned up.

Fix here: catch FileNotFoundError and log a warning instead of crashing:

    if args.state_file:
        try:
            with open(args.state_file) as f:
                state = json.load(f)
            version_id = state.get('version_id', version_id)
            issue_keys = state.get('issue_keys', issue_keys)
        except FileNotFoundError:
            eprint(f"Warning: State file {args.state_file} not found. Nothing to clean up from state.")

Note that this alone doesn't recover the orphaned version — setup.py would need to write partial state to a file as resources are created (rather than only emitting JSON to stdout at success) for a complete fix.

  • Mark as noise

Comment on lines +113 to +115
python test-fixtures/jira/cleanup.py \
--jira-url "https://sonarsource-sandbox-608.atlassian.net/" \
--state-file /tmp/jira-fixtures.json
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleanup unconditionally passes --state-file /tmp/jira-fixtures.json. When setup.py fails before writing that file, cleanup crashes with FileNotFoundError (see companion comment in cleanup.py). Guard the invocation:

          if [ -f /tmp/jira-fixtures.json ]; then
            python test-fixtures/jira/cleanup.py \
              --jira-url "[link removed: sonarsource-sandbox-608.atlassian.net]" \
              --state-file /tmp/jira-fixtures.json
          else
            echo "State file not found — setup failed before writing fixtures, nothing to clean up."
          fi

This is the minimal patch; fixing cleanup.py to handle the missing file is still the right defence-in-depth change.

  • Mark as noise

"""Configuration constants for Jira test fixtures."""

SANDBOX_URL = "https://sonarsource-sandbox-608.atlassian.net/"
PROD_URL = "https://sonarsource.atlassian.net/"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PROD_URL is never imported anywhere — confirmed by grep. Neither is SANDBOX_URL; the actual sandbox URL is hardcoded in the workflow and README instead. Remove both unused URL constants. Leaving PROD_URL in a test-fixture config file creates risk: a future contributor copying a usage pattern could accidentally point cleanup at production Jira.

  • Mark as noise

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant