test(tools): add unit tests for Bitbucket tools#1017
test(tools): add unit tests for Bitbucket tools#1017muddlebee merged 4 commits intoTracer-Cloud:mainfrom
Conversation
Greptile SummaryThis PR adds unit tests for the three Bitbucket tools ( Confidence Score: 5/5Safe to merge; all findings are P2 style/coverage suggestions that don't affect runtime correctness. All three issues found are P2: an unused import (lint-only), and two minor gaps in parametrize matrices for tests/tools/test_bitbucket_commits_tool.py — unused MagicMock import should be removed before merging to keep ruff clean. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["_bb_available(sources)"] --> B{connection_verified?}
B -- No --> Z[False]
B -- Yes --> C{workspace?}
C -- No --> Z
C -- Yes --> D{username?}
D -- No --> Z
D -- Yes --> E{app_password?}
E -- No --> Z
E -- Yes --> F[True]
F --> G["BitbucketSearchCodeTool\n_search_bitbucket_code_available"]
F --> H["BitbucketCommitsTool\n_list_bitbucket_commits_available"]
F --> I["BitbucketFileContentsTool\n_get_bitbucket_file_contents_available"]
H --> J{repo_slug or repo?}
J -- No --> Z
J -- Yes --> K[True - Commits]
I --> L{repo_slug or repo?}
L -- No --> Z
L -- Yes --> M{path?}
M -- No --> Z
M -- Yes --> N[True - File Contents]
Reviews (1): Last reviewed commit: "test(tools): add Bitbucket tool unit tes..." | Re-trigger Greptile |
| from __future__ import annotations | ||
|
|
||
| from typing import Any, cast | ||
| from unittest.mock import MagicMock, patch |
There was a problem hiding this comment.
| @pytest.mark.parametrize( | ||
| "sources,expected", | ||
| [ | ||
| ( | ||
| { | ||
| "bitbucket": { | ||
| "connection_verified": True, | ||
| "workspace": "acme", | ||
| "username": "bb-user", | ||
| "app_password": "bb-pass", | ||
| "repo_slug": "backend-service", | ||
| } | ||
| }, | ||
| True, | ||
| ), | ||
| ({"bitbucket": {"connection_verified": True, "workspace": "acme"}}, False), | ||
| ({"bitbucket": {"connection_verified": True, "username": "bb-user"}}, False), | ||
| ({"bitbucket": {"connection_verified": True, "app_password": "bb-pass"}}, False), | ||
| ( | ||
| {"bitbucket": {"workspace": "acme", "username": "bb-user", "app_password": "bb-pass"}}, | ||
| False, | ||
| ), | ||
| ({}, False), | ||
| ], | ||
| ) | ||
| def test_is_available_requires_repo_and_credentials(sources: dict, expected: bool) -> None: | ||
| rt = _registered_tool() | ||
| assert rt.is_available(sources) is expected | ||
|
|
||
|
|
||
| def test_extract_params_maps_fields() -> None: |
There was a problem hiding this comment.
Missing
is_available negative case for absent repo_slug
_list_bitbucket_commits_available requires both the four credentials and a repo_slug (or repo) key. The parametrized matrix has no case where all four credentials are fully supplied but repo_slug is omitted — so the per-field credential tests don't prove that the repo_slug guard works independently of the credential guards. Adding a case like {"bitbucket": {"connection_verified": True, "workspace": "acme", "username": "bb-user", "app_password": "bb-pass"}} → False would complete the coverage.
| @pytest.mark.parametrize( | ||
| "sources,expected", | ||
| [ | ||
| ( | ||
| { | ||
| "bitbucket": { | ||
| "connection_verified": True, | ||
| "workspace": "acme", | ||
| "username": "bb-user", | ||
| "app_password": "bb-pass", | ||
| "repo_slug": "backend-service", | ||
| "path": "src/main.py", | ||
| } | ||
| }, | ||
| True, | ||
| ), | ||
| ({"bitbucket": {"connection_verified": True, "workspace": "acme"}}, False), | ||
| ({"bitbucket": {"connection_verified": True, "repo_slug": "backend-service"}}, False), | ||
| ({"bitbucket": {"connection_verified": True, "path": "src/main.py"}}, False), | ||
| ({}, False), | ||
| ], | ||
| ) | ||
| def test_is_available_requires_file_and_credentials(sources: dict, expected: bool) -> None: | ||
| rt = _registered_tool() | ||
| assert rt.is_available(sources) is expected |
There was a problem hiding this comment.
Missing
is_available negative cases for credential-present-but-field-absent combinations
_get_bitbucket_file_contents_available requires credentials and both repo_slug and path. The current matrix only has single-field cases (workspace alone, repo_slug alone, path alone), so there's no case proving the function returns False when credentials are fully satisfied but path is absent, or credentials + path are present but repo_slug is absent. Adding those two cases would fully verify the compound guard.
There was a problem hiding this comment.
Pull request overview
Adds unit test coverage for the Bitbucket tool entrypoints and tightens Bitbucket tool availability so tools are only considered available when verified credentials are present in sources["bitbucket"].
Changes:
- Add
BaseToolContract-style unit tests for Bitbucket commits, file contents, and search code tools. - Expand Bitbucket availability helper (
_bb_available) to requireconnection_verifiedplusworkspace,username, andapp_password.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
tests/tools/test_bitbucket_search_code_tool.py |
Adds contract/is_available/extract_params/run tests for search_bitbucket_code. |
tests/tools/test_bitbucket_file_contents_tool.py |
Adds contract/is_available/extract_params/run tests for get_bitbucket_file_contents. |
tests/tools/test_bitbucket_commits_tool.py |
Adds contract/is_available/extract_params/run tests for list_bitbucket_commits. |
app/tools/BitbucketSearchCodeTool/__init__.py |
Updates shared _bb_available helper to require verified, complete Bitbucket credentials. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def test_run_returns_unavailable_without_credentials() -> None: | ||
| result = search_bitbucket_code(query="error OR exception") | ||
|
|
||
| assert result["available"] is False | ||
| assert result["results"] == [] | ||
| assert result["error"] == "Bitbucket integration is not configured." |
There was a problem hiding this comment.
The missing-credentials test relies on the real environment having no BITBUCKET_* variables. If BITBUCKET_WORKSPACE (and/or credentials) are set locally/CI, _resolve_config() may return a non-None config, causing this test to either make a real network call or return a different shape (e.g., error: "Not configured."). Patch app.tools.BitbucketSearchCodeTool.bitbucket_config_from_env to return None in this test to keep it deterministic.
| def test_run_returns_unavailable_without_credentials() -> None: | ||
| result = get_bitbucket_file_contents(repo_slug="backend-service", path="src/main.py") | ||
|
|
||
| assert result["available"] is False | ||
| assert result["file"] == {} | ||
| assert result["error"] == "Bitbucket integration is not configured." |
There was a problem hiding this comment.
This missing-credentials test can become flaky if BITBUCKET_* env vars are present, because _resolve_config() may load env config and the tool may call the real Bitbucket integration. Patch app.tools.BitbucketSearchCodeTool.bitbucket_config_from_env to return None here so the test always exercises the unavailable path without side effects.
| def test_run_returns_unavailable_without_credentials() -> None: | ||
| result = list_bitbucket_commits(repo_slug="backend-service") | ||
|
|
||
| assert result["available"] is False | ||
| assert result["commits"] == [] | ||
| assert result["error"] == "Bitbucket integration is not configured." |
There was a problem hiding this comment.
This missing-credentials test assumes there is no Bitbucket env configuration. If BITBUCKET_WORKSPACE/credentials are set, _resolve_config() can return a config and the tool may call the real Bitbucket API (or return a different error payload). Patch app.tools.BitbucketSearchCodeTool.bitbucket_config_from_env to return None in this test to make it deterministic and side-effect free.
…ra availability cases; remove unused import
|
@yashksaini-coder |
yashksaini-coder
left a comment
There was a problem hiding this comment.
Good work overall — all 46 tests pass locally, ruff/mypy/CI all clean, and the env-based flakiness in the missing-credentials tests is correctly fixed by patching app.tools.BitbucketSearchCodeTool.bitbucket_config_from_env (which is right, since _resolve_config lives there and is shared by all three tools).
Two greptile threads are still open and need small additions before this merges.
| assert rt.is_available(sources) is expected | ||
|
|
||
|
|
||
| def test_extract_params_maps_fields() -> None: |
There was a problem hiding this comment.
This greptile thread is still open. The parametrize list is missing the case where all four credentials are present but repo_slug is absent.
_list_bitbucket_commits_available checks _bb_available(sources) and bb.get('repo_slug', bb.get('repo')) — so a source dict with verified credentials but no repo_slug key should return False. That path has no test coverage.
Add one more parametrize case:
(
{
"bitbucket": {
"connection_verified": True,
"workspace": "acme",
"username": "bb-user",
"app_password": "bb-pass",
# repo_slug intentionally absent
}
},
False,
),This catches regressions where someone accidentally removes the repo_slug guard from is_available.
| ) | ||
| def test_is_available_requires_file_and_credentials(sources: dict, expected: bool) -> None: | ||
| rt = _registered_tool() | ||
| assert rt.is_available(sources) is expected |
There was a problem hiding this comment.
Same issue as the commits tool. _get_bitbucket_file_contents_available requires credentials AND both repo_slug AND path. The test matrix has the case where credentials are present but path is absent (sources2), but it's missing the symmetric case: credentials present, repo_slug present, path absent.
Add:
(
{
"bitbucket": {
"connection_verified": True,
"workspace": "acme",
"username": "bb-user",
"app_password": "bb-pass",
"repo_slug": "backend-service",
# path intentionally absent
}
},
False,
),This ensures that if the bb.get('path') guard is ever accidentally removed from is_available, a test breaks.
|
Hi @yashksaini-coder, addressed both remaining Greptile threads in the latest commit dbccb1a |
|
@7vignesh fix conflicts pls |
@muddlebee fixed |
|
⚡ LGTM → Merged. @7vignesh, your work is in. Every commit counts — thank you for this one. 👋 Join us on Discord - OpenSRE : hang out, contribute, or hunt for features and issues. Everyone's welcome. |

Fixes #992
Added unit tests for the Bitbucket tool entrypoints:
BitbucketCommitsToolBitbucketFileContentsToolBitbucketSearchCodeToolThis PR covers:
is_availablechecks for verified Bitbucket credentialsextract_paramsmapping for repo, path, query, and credentialsrunhappy-path behaviorI also updated the shared Bitbucket availability helper so the tools require verified Bitbucket credentials, not just a present Bitbucket source.
Demo/Screenshot for feature changes and bug fixes -
Validation output:
python -m pytest tests/tools/test_bitbucket_commits_tool.py tests/tools/test_bitbucket_file_contents_tool.py tests/tools/test_bitbucket_search_code_tool.py -qpython -m ruff check app/ tests/python -m ruff format --check app/ tests/python -m mypy app/Code Understanding and AI Usage
Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?
If you used AI assistance:
Explain your implementation approach:
Added focused unit tests for each Bitbucket tool using the existing
BaseToolContractpattern. The tests validate the tool metadata contract, source availability, parameter extraction, happy-path execution, and unavailable-path behavior. I used direct function patching against the Bitbucket integration helpers so the tests stay deterministic and do not make network calls.Checklist before requesting a review