Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

auto extend tests - sonnet-3.5 #1327

Closed
wants to merge 1 commit into from
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
14 changes: 13 additions & 1 deletion tests/unittest/test_azure_devops_parsing.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pr_agent.git_providers import AzureDevopsProvider
import pytest


class TestAzureDevOpsParsing():
Expand All @@ -12,4 +13,15 @@ def test_visualstudio_address(self):
pr_url = "https://organization.visualstudio.com/project/_git/repo/pullrequest/1"

# workspace_slug, repo_slug, pr_number
assert AzureDevopsProvider._parse_pr_url(pr_url) == ("project", "repo", 1)
assert AzureDevopsProvider._parse_pr_url(pr_url) == ("project", "repo", 1)

def test_provider_init_without_dependencies(self, monkeypatch):
monkeypatch.setattr("pr_agent.git_providers.azuredevops_provider.AZURE_DEVOPS_AVAILABLE", False)
with pytest.raises(ImportError, match="Azure DevOps provider is not available"):
AzureDevopsProvider()


def test_invalid_pr_url(self):
invalid_url = "https://dev.azure.com/organization/project/invalid/url"
with pytest.raises(ValueError, match="The provided URL does not appear to be a Azure DevOps PR URL"):
AzureDevopsProvider._parse_pr_url(invalid_url)
22 changes: 22 additions & 0 deletions tests/unittest/test_clip_tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,25 @@ def test_clip(self):
result = clip_tokens(text, max_tokens)
expected_results = 'line1\nline2\nline3\n\n...(truncated)'
assert result == expected_results

def test_clip_delete_last_line(self):
text = "line1\nline2\nline3"
max_tokens = 5
result = clip_tokens(text, max_tokens, delete_last_line=True)
expected = "line1\n...(truncated)"
assert result == expected


def test_clip_negative_tokens(self):
text = "some text"
max_tokens = -1
result = clip_tokens(text, max_tokens)
assert result == ""


def test_clip_empty_text(self):
text = ""
max_tokens = 10
result = clip_tokens(text, max_tokens)
assert result == ""

112 changes: 112 additions & 0 deletions tests/unittest/test_codecommit_client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from unittest.mock import MagicMock
from pr_agent.git_providers.codecommit_client import CodeCommitClient
import botocore.exceptions
from unittest.mock import patch
import pytest


class TestCodeCommitProvider:
Expand Down Expand Up @@ -134,3 +137,112 @@ def test_get_pr(self):
assert pr.targets[0].source_branch == "branch1"
assert pr.targets[0].destination_commit == "commit2"
assert pr.targets[0].destination_branch == "branch2"

def test_publish_comment_repo_not_exist(self):
api = CodeCommitClient()
api.boto_client = MagicMock()

error_response = {'Error': {'Code': 'RepositoryDoesNotExistException'}}
api.boto_client.post_comment_for_pull_request.side_effect = botocore.exceptions.ClientError(error_response, 'operation')

with pytest.raises(ValueError, match="Repository does not exist: test_repo"):
api.publish_comment(
"test_repo",
123,
"dest_commit",
"source_commit",
"Test comment"
)


def test_publish_comment_with_annotation(self):
api = CodeCommitClient()
api.boto_client = MagicMock()

api.publish_comment(
"test_repo",
123,
"dest_commit",
"source_commit",
"Test comment",
"test.py",
10
)

api.boto_client.post_comment_for_pull_request.assert_called_once_with(
pullRequestId="123",
repositoryName="test_repo",
beforeCommitId="dest_commit",
afterCommitId="source_commit",
content="Test comment",
location={
"filePath": "test.py",
"filePosition": 10,
"relativeFileVersion": "AFTER",
},
)


def test_get_differences_repo_not_exist(self):
api = CodeCommitClient()
api.boto_client = MagicMock()

error_response = {'Error': {'Code': 'RepositoryDoesNotExistException'}}
api.boto_client.get_paginator.return_value.paginate.side_effect = botocore.exceptions.ClientError(error_response, 'operation')

with pytest.raises(ValueError, match="CodeCommit cannot retrieve differences: Repository does not exist: test_repo"):
api.get_differences("test_repo", "commit1", "commit2")


def test_get_file_errors(self):
api = CodeCommitClient()
api.boto_client = MagicMock()

# Test repository does not exist
error_response = {'Error': {'Code': 'RepositoryDoesNotExistException'}}
api.boto_client.get_file.side_effect = botocore.exceptions.ClientError(error_response, 'operation')
with pytest.raises(ValueError, match="CodeCommit cannot retrieve PR: Repository does not exist: test_repo"):
api.get_file("test_repo", "test.py", "hash123")

# Test file does not exist but is optional
error_response = {'Error': {'Code': 'FileDoesNotExistException'}}
api.boto_client.get_file.side_effect = botocore.exceptions.ClientError(error_response, 'operation')
result = api.get_file("test_repo", "test.py", "hash123", optional=True)
assert result == ""


def test_publish_description_errors(self):
api = CodeCommitClient()
api.boto_client = MagicMock()

# Test PR does not exist
error_response = {'Error': {'Code': 'PullRequestDoesNotExistException'}}
api.boto_client.update_pull_request_title.side_effect = botocore.exceptions.ClientError(error_response, 'operation')
with pytest.raises(ValueError, match="PR number does not exist: 123"):
api.publish_description(123, "title", "body")

# Test invalid title
error_response = {'Error': {'Code': 'InvalidTitleException'}}
api.boto_client.update_pull_request_title.side_effect = botocore.exceptions.ClientError(error_response, 'operation')
with pytest.raises(ValueError, match="Invalid title for PR number: 123"):
api.publish_description(123, "title", "body")

# Test PR already closed
error_response = {'Error': {'Code': 'PullRequestAlreadyClosedException'}}
api.boto_client.update_pull_request_title.side_effect = botocore.exceptions.ClientError(error_response, 'operation')
with pytest.raises(ValueError, match="PR is already closed: PR number: 123"):
api.publish_description(123, "title", "body")


def test_connect_boto_client_failure(self):
api = CodeCommitClient()
with patch('boto3.client', side_effect=Exception("Connection failed")):
with pytest.raises(ValueError, match="Failed to connect to AWS CodeCommit: Connection failed"):
api._connect_boto_client()


def test_is_supported(self):
api = CodeCommitClient()
assert api.is_supported("gfm_markdown") is False
assert api.is_supported("other_capability") is True

24 changes: 24 additions & 0 deletions tests/unittest/test_codecommit_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,27 @@ def test_remove_markdown_html(self):
input = "## PR Feedback\n<details><summary>Code feedback:</summary>\nfile foo\n</summary>\n"
expect = "## PR Feedback\nCode feedback:\nfile foo\n\n"
assert CodeCommitProvider._remove_markdown_html(input) == expect

def test_get_languages(self):
with patch.object(CodeCommitProvider, "__init__", lambda x, y: None):
provider = CodeCommitProvider(None)

# Mock get_files() to return test files
test_files = [
CodeCommitFile("old.py", "blob1", "new.py", "blob2", EDIT_TYPE.MODIFIED),
CodeCommitFile("test.js", "blob3", "test.js", "blob4", EDIT_TYPE.MODIFIED),
CodeCommitFile("style.css", "blob5", "style.css", "blob6", EDIT_TYPE.MODIFIED)
]
provider.get_files = lambda: test_files

# Test language detection
languages = provider.get_languages()

assert isinstance(languages, dict)
assert "python" in languages
assert "javascript" in languages
assert "css" in languages
assert languages["python"] == 33
assert languages["javascript"] == 33
assert languages["css"] == 33

46 changes: 46 additions & 0 deletions tests/unittest/test_convert_to_markdown.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Generated by CodiumAI
from pr_agent.algo.utils import PRReviewHeader, convert_to_markdown_v2
from pr_agent.tools.pr_description import insert_br_after_x_chars
from pr_agent.algo.utils import process_description, PRDescriptionHeader
from pr_agent.algo.utils import ticket_markdown_logic
from pr_agent.algo.utils import get_setting

"""
Code Analysis
Expand Down Expand Up @@ -97,6 +100,49 @@ def test_br2(self):
# print("-----")
# print(file_change_description_br)

def test_process_description_with_walkthrough(self):
description = f"""Some base description
{PRDescriptionHeader.CHANGES_WALKTHROUGH.value}
<table>
<tr><td><details><summary><strong>file1.py</strong><dd><code>Added new feature</code></dd></summary><hr>path/to/file1.py<li>Added feature X</details></td></tr>
</table>
___"""

base_desc, files = process_description(description)

assert base_desc == "Some base description\n "
assert len(files) == 1
assert files[0]['short_file_name'] == 'file1.py'
assert files[0]['full_file_name'] == 'path/to/file1.py'
assert files[0]['short_summary'] == 'Added new feature'
assert files[0]['long_summary'] == '* Added feature X'


def test_ticket_markdown_logic_partially_compliant(self):
emoji = "🎫"
markdown_text = ""
value = [
{
"ticket_url": "http://jira.com/ticket-1",
"overall_compliance_level": "Partially compliant",
"fully_compliant_requirements": "Requirement 1",
"not_compliant_requirements": "Requirement 2"
}
]
result = ticket_markdown_logic(emoji, markdown_text, value, True)
assert "🔶" in result
assert "Partially compliant" in result
assert "Requirement 1" in result
assert "Requirement 2" in result


def test_get_setting_valid_key(self):
from pr_agent.config_loader import global_settings
global_settings["TEST_KEY"] = "test_value"
result = get_setting("test_key")
assert result == "test_value"


def test_br3(self):
file_change_description = 'Created a new class `ColorPaletteResourcesCollection` which extends `AvaloniaDictionary<ThemeVariant, ColorPaletteResources>` and implements aaa'
file_change_description_br = insert_br_after_x_chars(file_change_description)
Expand Down
25 changes: 25 additions & 0 deletions tests/unittest/test_extend_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,28 @@ def test_extend_patches_with_extra_lines(self, token_handler, pr_languages):

p0_extended = patches_extended_with_extra_lines[0].strip()
assert p0_extended == '## file1\n\n@@ -3,8 +3,8 @@ \n line0\n line1\n-original content\n+modified content\n line2\n line3\n line4\n line5\n line6'


def test_skip_patch_by_extension(self):
# Set up extension types to skip
get_settings().config.patch_extension_skip_types = ['.exe', '.bin']

original_file_str = 'line1\nline2\nline3'
patch_str = '@@ -1,2 +1,2 @@\n-line1\n+new_line1\n line2'

# Test with file that should be skipped
result = extend_patch(original_file_str, patch_str,
patch_extra_lines_before=1,
patch_extra_lines_after=1,
filename="test.exe")
assert result == patch_str # Should return original patch without changes

# Test with file that should not be skipped
result = extend_patch(original_file_str, patch_str,
patch_extra_lines_before=1,
patch_extra_lines_after=1,
filename="test.txt")
assert result != patch_str # Should return modified patch

# Reset settings
get_settings().config.patch_extension_skip_types = []
90 changes: 90 additions & 0 deletions tests/unittest/test_file_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,93 @@ def test_invalid_regex(self, monkeypatch):

filtered_files = filter_ignored(files)
assert filtered_files == expected, f"Expected {[file.filename for file in expected]}, but got {[file.filename for file in filtered_files]}."

def test_bitbucket_platform_old_paths(self, monkeypatch):
monkeypatch.setattr(global_settings.ignore, 'regex', ['^test.*\.py$'])

class BitbucketFile:
def __init__(self, old_path=None):
self.new = None
self.old = type('Path', (), {'path': old_path})() if old_path else None

files = [
BitbucketFile('test_old1.py'),
BitbucketFile('main_old.py'),
BitbucketFile(None), # Edge case with no path
BitbucketFile('src_old.py')
]

filtered_files = filter_ignored(files, platform='bitbucket')
assert len(filtered_files) == 2
assert any(f.old and f.old.path == 'main_old.py' for f in filtered_files)
assert any(f.old and f.old.path == 'src_old.py' for f in filtered_files)


def test_gitlab_platform_new_paths(self, monkeypatch):
monkeypatch.setattr(global_settings.ignore, 'regex', ['^test.*\.py$'])

files = [
{'new_path': 'test1.py'},
{'new_path': 'main.py'},
{'new_path': None}, # Edge case with no path
{'new_path': 'src.py'}
]

filtered_files = filter_ignored(files, platform='gitlab')
assert len(filtered_files) == 2
assert any(f['new_path'] == 'main.py' for f in filtered_files)
assert any(f['new_path'] == 'src.py' for f in filtered_files)


def test_bitbucket_platform_new_paths(self, monkeypatch):
monkeypatch.setattr(global_settings.ignore, 'regex', ['^test.*\.py$'])

class BitbucketFile:
def __init__(self, new_path=None):
self.new = type('Path', (), {'path': new_path})() if new_path else None
self.old = None

files = [
BitbucketFile('test1.py'),
BitbucketFile('main.py'),
BitbucketFile(None), # Edge case with no path
BitbucketFile('src.py')
]

filtered_files = filter_ignored(files, platform='bitbucket')
assert len(filtered_files) == 2
assert any(f.new and f.new.path == 'main.py' for f in filtered_files)
assert any(f.new and f.new.path == 'src.py' for f in filtered_files)


def test_string_pattern_handling(self, monkeypatch):
# Test single string regex pattern
monkeypatch.setattr(global_settings.ignore, 'regex', '^test.*\.py$')
monkeypatch.setattr(global_settings.ignore, 'glob', '[*.txt]')

files = [
type('', (object,), {'filename': 'test1.py'})(),
type('', (object,), {'filename': 'main.py'})(),
type('', (object,), {'filename': 'data.txt'})()
]

filtered_files = filter_ignored(files)
assert len(filtered_files) == 1
assert filtered_files[0].filename == 'main.py'


def test_azure_platform_and_error_handling(self, monkeypatch):
monkeypatch.setattr(global_settings.ignore, 'regex', ['^test.*\.py$'])

# Test Azure platform
files = ['test1.py', 'main.py', 'test2.py', 'src.py']
filtered_files = filter_ignored(files, platform='azure')
assert len(filtered_files) == 2
assert 'main.py' in filtered_files
assert 'src.py' in filtered_files

# Test error handling
monkeypatch.setattr(global_settings.ignore, 'regex', None)
result = filter_ignored(files, platform='azure')
assert result == files # Should return original files on error

Loading
Loading