Back to Repositories

Testing Bitbucket Provider Integration in PR-Agent

This test suite validates the BitbucketProvider and BitbucketServerProvider classes, focusing on URL parsing and diff file handling across different Bitbucket versions. The tests verify proper handling of pull request URLs, file content comparisons, and complex merge scenarios.

Test Coverage Overview

The test suite provides comprehensive coverage of Bitbucket integration functionality:
  • URL parsing for both cloud and server instances
  • File diff generation across different Bitbucket versions (6.0, 7.0, 8.16)
  • Complex merge scenarios including divergent branches and multi-merge cases
  • Edge cases for file content comparison and change detection

Implementation Analysis

The testing approach uses mock objects extensively to simulate Bitbucket API responses and file content retrieval. The tests employ detailed assertion patterns to verify correct handling of file diffs, URL components, and version-specific behaviors.
  • Mock implementations for file content and API responses
  • Detailed test cases for different branch merge scenarios
  • Version-specific test implementations for backwards compatibility

Technical Details

Key technical components include:
  • unittest.mock for API simulation
  • Atlassian Bitbucket client library integration
  • FilePatchInfo data structure for diff representation
  • Mock response mappings for different API versions
  • Custom diff comparison logic

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Comprehensive mock object usage for external dependencies
  • Clear test case organization and naming
  • Detailed documentation of complex scenarios
  • Separation of concerns between different provider implementations
  • Thorough version compatibility testing

codium-ai/pr-agent

tests/unittest/test_bitbucket_provider.py

            
from unittest.mock import MagicMock

from atlassian.bitbucket import Bitbucket

from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo
from pr_agent.git_providers import BitbucketServerProvider
from pr_agent.git_providers.bitbucket_provider import BitbucketProvider


class TestBitbucketProvider:
    def test_parse_pr_url(self):
        url = "https://bitbucket.org/WORKSPACE_XYZ/MY_TEST_REPO/pull-requests/321"
        workspace_slug, repo_slug, pr_number = BitbucketProvider._parse_pr_url(url)
        assert workspace_slug == "WORKSPACE_XYZ"
        assert repo_slug == "MY_TEST_REPO"
        assert pr_number == 321


class TestBitbucketServerProvider:
    def test_parse_pr_url(self):
        url = "https://git.onpreminstance.com/projects/AAA/repos/my-repo/pull-requests/1"
        workspace_slug, repo_slug, pr_number = BitbucketServerProvider._parse_pr_url(url)
        assert workspace_slug == "AAA"
        assert repo_slug == "my-repo"
        assert pr_number == 1

    def mock_get_content_of_file(self, project_key, repository_slug, filename, at=None, markup=None):
        content_map = {
            '9c1cffdd9f276074bfb6fb3b70fbee62d298b058': 'file
with
some
lines
to
emulate
a
real
file
',
            '2a1165446bdf991caf114d01f7c88d84ae7399cf': 'file
with
multiple 
lines
to
emulate
a
fake
file
',
            'f617708826cdd0b40abb5245eda71630192a17e3': 'file
with
multiple 
lines
to
emulate
a
real
file
',
            'cb68a3027d6dda065a7692ebf2c90bed1bcdec28': 'file
with
some
changes
to
emulate
a
real
file
',
            '1905dcf16c0aac6ac24f7ab617ad09c73dc1d23b': 'file
with
some
lines
to
emulate
a
fake
test
',
            'ae4eca7f222c96d396927d48ab7538e2ee13ca63': 'readme
without
some
lines
to
simulate
a
real
file',
            '548f8ba15abc30875a082156314426806c3f4d97': 'file
with
some
lines
to
emulate
a
real
file',
            '0e898cb355a5170d8c8771b25d43fcaa1d2d9489': 'file
with
multiple
lines
to
emulate
a
real
file'
        }
        return content_map.get(at, '')

    def mock_get_from_bitbucket_60(self, url):
        response_map = {
            "rest/api/1.0/application-properties": {
                "version": "6.0"
            }
        }
        return response_map.get(url, '')

    def mock_get_from_bitbucket_70(self, url):
        response_map = {
            "rest/api/1.0/application-properties": {
                "version": "7.0"
            }
        }
        return response_map.get(url, '')

    def mock_get_from_bitbucket_816(self, url):
        response_map = {
            "rest/api/1.0/application-properties": {
                "version": "8.16"
            },
            "rest/api/latest/projects/AAA/repos/my-repo/pull-requests/1/merge-base": {
                'id': '548f8ba15abc30875a082156314426806c3f4d97'
            }
        }
        return response_map.get(url, '')


    '''
    tests the 2-way diff functionality where the diff should be between the HEAD of branch b and node c
    NOT between the HEAD of main and the HEAD of branch b

          - o  branch b
         /
    o - o - o  main
        ^ node c
    '''
    def test_get_diff_files_simple_diverge_70(self):
        bitbucket_client = MagicMock(Bitbucket)
        bitbucket_client.get_pull_request.return_value = {
            'toRef': {'latestCommit': '9c1cffdd9f276074bfb6fb3b70fbee62d298b058'},
            'fromRef': {'latestCommit': '2a1165446bdf991caf114d01f7c88d84ae7399cf'}
        }
        bitbucket_client.get_pull_requests_commits.return_value = [
            {'id': '2a1165446bdf991caf114d01f7c88d84ae7399cf',
             'parents': [{'id': 'f617708826cdd0b40abb5245eda71630192a17e3'}]}
        ]
        bitbucket_client.get_commits.return_value = [
            {'id': '9c1cffdd9f276074bfb6fb3b70fbee62d298b058'},
            {'id': 'dbca09554567d2e4bee7f07993390153280ee450'}
        ]
        bitbucket_client.get_pull_requests_changes.return_value = [
            {
                'path': {'toString': 'Readme.md'},
                'type': 'MODIFY',
            }
        ]

        bitbucket_client.get.side_effect = self.mock_get_from_bitbucket_70
        bitbucket_client.get_content_of_file.side_effect = self.mock_get_content_of_file

        provider = BitbucketServerProvider(
            "https://git.onpreminstance.com/projects/AAA/repos/my-repo/pull-requests/1",
            bitbucket_client=bitbucket_client
        )

        expected = [
            FilePatchInfo(
                'file
with
multiple 
lines
to
emulate
a
real
file
',
                'file
with
multiple 
lines
to
emulate
a
fake
file
',
                '--- 
+++ 
@@ -5,5 +5,5 @@
 to
 emulate
 a
-real
+fake
 file
',
                'Readme.md',
                edit_type=EDIT_TYPE.MODIFIED,
            )
        ]

        actual = provider.get_diff_files()

        assert actual == expected


    '''
    tests the 2-way diff functionality where the diff should be between the HEAD of branch b and node c
    NOT between the HEAD of main and the HEAD of branch b

          - o - o - o  branch b
         /     /
    o - o -- o - o     main
             ^ node c
    '''
    def test_get_diff_files_diverge_with_merge_commit_70(self):
        bitbucket_client = MagicMock(Bitbucket)
        bitbucket_client.get_pull_request.return_value = {
            'toRef': {'latestCommit': 'cb68a3027d6dda065a7692ebf2c90bed1bcdec28'},
            'fromRef': {'latestCommit': '1905dcf16c0aac6ac24f7ab617ad09c73dc1d23b'}
        }
        bitbucket_client.get_pull_requests_commits.return_value = [
            {'id': '1905dcf16c0aac6ac24f7ab617ad09c73dc1d23b',
             'parents': [{'id': '692772f456c3db77a90b11ce39ea516f8c2bad93'}]},
            {'id': '692772f456c3db77a90b11ce39ea516f8c2bad93', 'parents': [
                {'id': '2a1165446bdf991caf114d01f7c88d84ae7399cf'},
                {'id': '9c1cffdd9f276074bfb6fb3b70fbee62d298b058'},
            ]},
            {'id': '2a1165446bdf991caf114d01f7c88d84ae7399cf',
             'parents': [{'id': 'f617708826cdd0b40abb5245eda71630192a17e3'}]}
        ]
        bitbucket_client.get_commits.return_value = [
            {'id': 'cb68a3027d6dda065a7692ebf2c90bed1bcdec28'},
            {'id': '9c1cffdd9f276074bfb6fb3b70fbee62d298b058'},
            {'id': 'dbca09554567d2e4bee7f07993390153280ee450'}
        ]
        bitbucket_client.get_pull_requests_changes.return_value = [
            {
                'path': {'toString': 'Readme.md'},
                'type': 'MODIFY',
            }
        ]

        bitbucket_client.get.side_effect = self.mock_get_from_bitbucket_70
        bitbucket_client.get_content_of_file.side_effect = self.mock_get_content_of_file

        provider = BitbucketServerProvider(
            "https://git.onpreminstance.com/projects/AAA/repos/my-repo/pull-requests/1",
            bitbucket_client=bitbucket_client
        )

        expected = [
            FilePatchInfo(
                'file
with
some
lines
to
emulate
a
real
file
',
                'file
with
some
lines
to
emulate
a
fake
test
',
                '--- 
+++ 
@@ -5,5 +5,5 @@
 to
 emulate
 a
-real
-file
+fake
+test
',
                'Readme.md',
                edit_type=EDIT_TYPE.MODIFIED,
            )
        ]

        actual = provider.get_diff_files()

        assert actual == expected


    '''
    tests the 2-way diff functionality where the diff should be between the HEAD of branch c and node d
    NOT between the HEAD of main and the HEAD of branch c

            ---- o - o branch c
           /    /
          ---- o       branch b
         /    /
        o - o - o      main
            ^ node d
    '''
    def get_multi_merge_diverge_mock_client(self, api_version):
        bitbucket_client = MagicMock(Bitbucket)
        bitbucket_client.get_pull_request.return_value = {
            'toRef': {'latestCommit': '9569922b22fe4fd0968be6a50ed99f71efcd0504'},
            'fromRef': {'latestCommit': 'ae4eca7f222c96d396927d48ab7538e2ee13ca63'}
        }
        bitbucket_client.get_pull_requests_commits.return_value = [
            {'id': 'ae4eca7f222c96d396927d48ab7538e2ee13ca63',
             'parents': [{'id': 'bbf300fb3af5129af8c44659f8cc7a526a6a6f31'}]},
            {'id': 'bbf300fb3af5129af8c44659f8cc7a526a6a6f31', 'parents': [
                {'id': '10b7b8e41cb370b48ceda8da4e7e6ad033182213'},
                {'id': 'd1bb183c706a3ebe4c2b1158c25878201a27ad8c'},
            ]},
            {'id': 'd1bb183c706a3ebe4c2b1158c25878201a27ad8c', 'parents': [
                {'id': '5bd76251866cb415fc5ff232f63a581e89223bda'},
                {'id': '548f8ba15abc30875a082156314426806c3f4d97'}
            ]},
            {'id': '5bd76251866cb415fc5ff232f63a581e89223bda',
             'parents': [{'id': '0e898cb355a5170d8c8771b25d43fcaa1d2d9489'}]},
            {'id': '10b7b8e41cb370b48ceda8da4e7e6ad033182213',
             'parents': [{'id': '0e898cb355a5170d8c8771b25d43fcaa1d2d9489'}]}
        ]
        bitbucket_client.get_commits.return_value = [
            {'id': '9569922b22fe4fd0968be6a50ed99f71efcd0504'},
            {'id': '548f8ba15abc30875a082156314426806c3f4d97'}
        ]
        bitbucket_client.get_pull_requests_changes.return_value = [
            {
                'path': {'toString': 'Readme.md'},
                'type': 'MODIFY',
            }
        ]

        bitbucket_client.get_content_of_file.side_effect = self.mock_get_content_of_file
        if api_version == 60:
            bitbucket_client.get.side_effect = self.mock_get_from_bitbucket_60
        elif api_version == 70:
            bitbucket_client.get.side_effect = self.mock_get_from_bitbucket_70
        elif api_version == 816:
            bitbucket_client.get.side_effect = self.mock_get_from_bitbucket_816

        return bitbucket_client

    def test_get_diff_files_multi_merge_diverge_60(self):
        bitbucket_client = self.get_multi_merge_diverge_mock_client(60)

        provider = BitbucketServerProvider(
            "https://git.onpreminstance.com/projects/AAA/repos/my-repo/pull-requests/1",
            bitbucket_client=bitbucket_client
        )

        expected = [
            FilePatchInfo(
                'file
with
multiple
lines
to
emulate
a
real
file',
                'readme
without
some
lines
to
simulate
a
real
file',
                '--- 
+++ 
@@ -1,9 +1,9 @@
-file
-with
-multiple
+readme
+without
+some
 lines
 to
-emulate
+simulate
 a
 real
 file',
                'Readme.md',
                edit_type=EDIT_TYPE.MODIFIED,
            )
        ]

        actual = provider.get_diff_files()

        assert actual == expected

    def test_get_diff_files_multi_merge_diverge_70(self):
        bitbucket_client = self.get_multi_merge_diverge_mock_client(70)

        provider = BitbucketServerProvider(
            "https://git.onpreminstance.com/projects/AAA/repos/my-repo/pull-requests/1",
            bitbucket_client=bitbucket_client
        )

        expected = [
            FilePatchInfo(
                'file
with
some
lines
to
emulate
a
real
file',
                'readme
without
some
lines
to
simulate
a
real
file',
                '--- 
+++ 
@@ -1,9 +1,9 @@
-file
-with
+readme
+without
 some
 lines
 to
-emulate
+simulate
 a
 real
 file',
                'Readme.md',
                edit_type=EDIT_TYPE.MODIFIED,
            )
        ]

        actual = provider.get_diff_files()

        assert actual == expected

    def test_get_diff_files_multi_merge_diverge_816(self):
        bitbucket_client = self.get_multi_merge_diverge_mock_client(816)

        provider = BitbucketServerProvider(
            "https://git.onpreminstance.com/projects/AAA/repos/my-repo/pull-requests/1",
            bitbucket_client=bitbucket_client
        )

        expected = [
            FilePatchInfo(
                'file
with
some
lines
to
emulate
a
real
file',
                'readme
without
some
lines
to
simulate
a
real
file',
                '--- 
+++ 
@@ -1,9 +1,9 @@
-file
-with
+readme
+without
 some
 lines
 to
-emulate
+simulate
 a
 real
 file',
                'Readme.md',
                edit_type=EDIT_TYPE.MODIFIED,
            )
        ]

        actual = provider.get_diff_files()

        assert actual == expected