Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ This project adheres to [Semantic Versioning](https://semver.org/). Version numb
- **MINOR**: New features that are backward-compatible.
- **PATCH**: Bug fixes or minor changes that do not affect backward compatibility.

## [1.15.0]

_released 06-12-2026

### Added
- Added new data extraction commands for cases, suites, sections and plans for AI automation

## [1.14.3]

_released 05-28-2026
Expand Down Expand Up @@ -60,7 +67,6 @@ _released 03-18-2026

### Fixed
- Cannot add empty runs via add_run command due to empty test case validation.

## [1.13.3]

_released 03-05-2026
Expand Down Expand Up @@ -447,4 +453,4 @@ _released 10-07-2022

## [1.0]
_released 16-04-2022
- **Initial Public Release:** The first public release of `trcli`.
- **Initial Public Release:** The first public release of `trcli`.
89 changes: 85 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The TestRail CLI currently supports:
- **Auto-generating test cases from OpenAPI specifications**
- **Creating new test runs for results to be uploaded to**
- **Managing project labels for better organization and categorization**
- **Reading plans, suites, and cases from TestRail as JSON**

To see further documentation about the TestRail CLI, please refer to the
[TestRail CLI documentation pages](https://support.gurock.com/hc/en-us/articles/7146548750868-TestRail-CLI)
Expand All @@ -33,7 +34,7 @@ trcli
```
You should get something like this:
```
TestRail CLI v1.14.3
TestRail CLI v1.15.0
Copyright 2025 Gurock Software GmbH - www.gurock.com
Supported and loaded modules:
- parse_junit: JUnit XML Files (& Similar)
Expand All @@ -52,7 +53,7 @@ CLI general reference
--------
```shell
$ trcli --help
TestRail CLI v1.14.3
TestRail CLI v1.15.0
Copyright 2025 Gurock Software GmbH - www.gurock.com
Usage: trcli [OPTIONS] COMMAND [ARGS]...

Expand Down Expand Up @@ -2180,7 +2181,7 @@ Options:
### Reference
```shell
$ trcli add_run --help
TestRail CLI v1.14.3
TestRail CLI v1.15.0
Copyright 2025 Gurock Software GmbH - www.gurock.com
Usage: trcli add_run [OPTIONS]

Expand Down Expand Up @@ -2390,7 +2391,7 @@ providing you with a solid base of test cases, which you can further expand on T
### Reference
```shell
$ trcli parse_openapi --help
TestRail CLI v1.14.3
TestRail CLI v1.15.0
Copyright 2025 Gurock Software GmbH - www.gurock.com
Usage: trcli parse_openapi [OPTIONS]

Expand Down Expand Up @@ -2480,6 +2481,86 @@ expand your test cases to cover specific business logic and workflows.
|---------------------------------------|---------------------------------------------------|
| `VERB /path -> status_code (summary)` | `GET /pet/{petId} -> 200 (Successful operation) ` |

Reading data from TestRail
-----------------

The TestRail CLI provides read-only commands to retrieve plans, suites, sections, and cases from TestRail as JSON.
These commands output JSON directly to stdout, making them easy to integrate with other tools
via piping (e.g., `jq`).

### Commands

| Command | Description |
|---------|-------------|
| `get_plans` | List all test plans for a project |
| `get_plan` | Get a single test plan by ID |
| `get_suites` | List all test suites for a project |
| `get_sections` | List test sections for a project or suite |
| `get_cases` | List test cases for a project and suite |
| `get_case` | Get a single test case by ID |

### Usage Examples

**List all test plans for a project:**
```shell
trcli --host https://example.testrail.io --username user@example.com --key YOUR_API_KEY \
get_plans --project-id 1
```

**Get a single test plan:**
```shell
trcli --host https://example.testrail.io --username user@example.com --key YOUR_API_KEY \
get_plan --plan-id 42
```

**List all test suites for a project:**
```shell
trcli --host https://example.testrail.io --username user@example.com --key YOUR_API_KEY \
get_suites --project-id 1
```

**List test sections (with optional suite filter):**
```shell
trcli --host https://example.testrail.io --username user@example.com --key YOUR_API_KEY \
get_sections --project-id 1 --suite-id 3
```

**List test cases (with optional section filter):**
```shell
# All cases in a suite
trcli --host https://example.testrail.io --username user@example.com --key YOUR_API_KEY \
get_cases --project-id 1 --suite-id 3

# Cases in a specific section
trcli --host https://example.testrail.io --username user@example.com --key YOUR_API_KEY \
get_cases --project-id 1 --suite-id 3 --section-id 10
```

**Get a single test case:**
```shell
trcli --host https://example.testrail.io --username user@example.com --key YOUR_API_KEY \
get_case --case-id 5001
```

**Using environment variables instead of flags:**
```shell
export TR_CLI_HOST=https://example.testrail.io
export TR_CLI_USERNAME=user@example.com
export TR_CLI_KEY=YOUR_API_KEY

trcli get_plans --project-id 1
trcli get_sections --project-id 1 --suite-id 3
trcli get_case --case-id 5001
```

**Piping output to jq:**
```shell
trcli get_cases --project-id 1 --suite-id 3 | jq '.[].title'
```

All read commands output a JSON array or object to stdout. Errors and warnings are printed to stderr,
so they will not interfere with piped JSON output.

Parameter sources
-----------------
You can choose to set parameters from different sources, like a default config file,
Expand Down
105 changes: 105 additions & 0 deletions tests/test_cmd_get_case.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import json
from unittest import mock

import pytest
from click.testing import CliRunner

from trcli.api.api_client import APIClientResult
from trcli.cli import cli as trcli_cli


class TestCmdGetCase:
"""Tests for the get_case CLI command."""

BASE_ARGS = [
"-h", "https://test.testrail.com",
"-u", "user@example.com",
"-p", "password123",
"get_case",
"--case-id", "99",
]

@mock.patch("trcli.commands.cmd_get_case.create_api_client")
def test_happy_path_returns_json(self, mock_create_client):
"""Successful API response prints JSON to stdout."""
case_data = {"id": 99, "title": "My Test Case", "section_id": 5}
mock_client = mock_create_client.return_value
mock_client.send_get.return_value = APIClientResult(
status_code=200, response_text=case_data, error_message=""
)

runner = CliRunner()
result = runner.invoke(trcli_cli, self.BASE_ARGS, catch_exceptions=False)

assert result.exit_code == 0
assert json.loads(result.output) == case_data
mock_client.send_get.assert_called_once_with("get_case/99")

@mock.patch("trcli.commands.cmd_get_case.create_api_client")
def test_api_error_message_exits_with_code_1(self, mock_create_client):
"""API error message is output with exit code 1."""
mock_client = mock_create_client.return_value
mock_client.send_get.return_value = APIClientResult(
status_code=-1, response_text="", error_message="Connection refused"
)

runner = CliRunner()
result = runner.invoke(trcli_cli, self.BASE_ARGS)

assert result.exit_code == 1
assert "Connection refused" in result.output

@mock.patch("trcli.commands.cmd_get_case.create_api_client")
def test_api_non_200_status_exits_with_code_1(self, mock_create_client):
"""Non-200 status code prints error with exit code 1."""
mock_client = mock_create_client.return_value
mock_client.send_get.return_value = APIClientResult(
status_code=404, response_text="", error_message=""
)

runner = CliRunner()
result = runner.invoke(trcli_cli, self.BASE_ARGS)

assert result.exit_code == 1
assert "404" in result.output

def test_missing_case_id_exits_nonzero(self):
"""Missing --case-id triggers a Click error."""
args = [
"-h", "https://test.testrail.com",
"-u", "user@example.com",
"-p", "password123",
"get_case",
]
runner = CliRunner()
result = runner.invoke(trcli_cli, args)

assert result.exit_code != 0

def test_missing_host_exits_nonzero(self):
"""Missing -h triggers an error."""
args = [
"-u", "user@example.com",
"-p", "password123",
"get_case",
"--case-id", "99",
]
runner = CliRunner()
result = runner.invoke(trcli_cli, args)

assert result.exit_code == 1
assert "--host is required" in result.output

def test_missing_password_and_key_exits_nonzero(self):
"""Missing both -p and -k triggers an error."""
args = [
"-h", "https://test.testrail.com",
"-u", "user@example.com",
"get_case",
"--case-id", "99",
]
runner = CliRunner()
result = runner.invoke(trcli_cli, args)

assert result.exit_code == 1
assert "--password or --key is required" in result.output
Loading