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

Sweep: Write unit tests for ClonedRepo #2377

Closed
4 tasks done
wwzeng1 opened this issue Oct 21, 2023 · 1 comment · Fixed by #2378
Closed
4 tasks done

Sweep: Write unit tests for ClonedRepo #2377

wwzeng1 opened this issue Oct 21, 2023 · 1 comment · Fixed by #2378
Labels
sweep Assigns Sweep to an issue or pull request.

Comments

@wwzeng1
Copy link
Contributor

wwzeng1 commented Oct 21, 2023

Details

No response

Checklist
  • sweepai/utils/github_utils_test.py ✅ Commit 0ded79e
  • sweepai/utils/github_utils_test.py ❌ Sandbox failed so I made additional changes
  • sweepai/utils/github_utils_test.py ✅ Sandbox ran successfully
  • sweepai/utils/github_utils_test.py ✅ Sandbox ran successfully

Flowchart

@wwzeng1 wwzeng1 added the sweep Assigns Sweep to an issue or pull request. label Oct 21, 2023
@sweep-nightly
Copy link
Contributor

sweep-nightly bot commented Oct 21, 2023

Here's the PR! #2378.

💎 Sweep Pro: I'm creating this ticket using GPT-4. You have unlimited GPT-4 tickets.

Actions (click)

  • ↻ Restart Sweep

Step 1: 🔎 Searching

I found the following snippets in your repository. I will now analyze these snippets and come up with a plan.

Some code snippets I looked at (click to expand). If some file is missing from here, you can mention the path in the ticket description.

link: "https://github.com/sweepai/sweep",
},
{
label: "Discord",
icon: <FaDiscord />,
link: "https://discord.gg/sweep",
},
{
label: "Email",
icon: <EmailIcon />,
link: "mailto:team@sweep.dev",
},
// {
// label: "Buy Sweep Pro",
// icon: <p>Buy Sweep Pro</p>,
// link: "https://buy.stripe.com/fZe03512h99u0AE6os",
// },
];\
"""
sweep_bot = SweepBot.from_system_message_string(
"",
repo=github.Github().get_repo("sweepai/sweep"),
chat_logger=ChatLogger(data={"username": "kevinlu1248"}),
)
sweep_bot.messages.extend(
[
Message(
role="assistant",
content="""
Contextual thoughts:
* The Navbar component in src/components/Navbar.tsx is currently a functional component. I need to understand its current structure and functionality in order to refactor it into a class component.
* The useBreakpointValue hook is being used in the Navbar component. I need to understand how it's being used in order to replace it with this.state and this.setState in the class component.
* The current unit tests in src/App.test.tsx provide a starting point for writing the new unit tests for the refactored component. I need to understand what functionality they're testing.
Relevant snippets:
<snippet source="src/App.test.tsx:1-11">
import { screen } from "@testing-library/react"
import { render } from "./test-utils"
import { App } from "./App"
test("renders learn react link", () => {
render(<App />)
const linkElement = screen.getByText(/learn chakra/i)
expect(linkElement).toBeInTheDocument()
})
</snippet>""",
),
Message(
role="user",
content="""# Repo & Issue Metadata
Repo: landing-page: No description provided.
Issue Url: https://github.com/sweepai/landing-page/issues/420
Username: sweep-nightly[bot]
Issue Title: Refactor Navbar to class component
Issue Description: * In src/components/Navbar.tsx, define a class Navbar that extends React.Component.
* In src/components/Navbar.tsx, implement a render method that returns the same JSX as the functional component.
* In src/components/Navbar.tsx, replace the use of useBreakpointValue with this.state and this.setState.
* In src/components/Navbar.tsx, write unit tests for the refactored component and run them to verify that they pass.
Parent issue: #335
Additional instructions:
* In src/components/Navbar.tsx, define a class Navbar that extends React.Component. The class should have a constructor that initializes the state and binds any necessary methods.
* In the render method of the Navbar class, return the same JSX as the functional component. Make sure to replace any hooks with their class component equivalents.
* Replace the use of useBreakpointValue with this.state and this.setState. This may involve adding a resize event listener in componentDidMount and componentWillUnmount to update the state when the window size changes.
* Write unit tests for the Navbar class in a new file, src/components/Navbar.test.tsx. The tests should cover the same functionality as the existing tests for the functional component, as well as any new functionality introduced by the refactoring.
* Run the tests using the command `npm test` to verify that they pass.""",
),
]
)
# print(
# sweep_bot.rewrite_section(
# FileChangeRequest(
# filename="main.py",
# instructions="Rewrite as class component",
# change_type="rewrite",
# ),
# old_file,
# section,
# ).section
# )
print(
sweep_bot.rewrite_file(
FileChangeRequest(
filename="index.js",
instructions="Rewrite as class component",
change_type="rewrite",
),
old_file,
)

"site_admin": false
},
"assignees": [
{
"login": "kevinlu1248",
"id": 26889185,
"node_id": "MDQ6VXNlcjI2ODg5MTg1",
"avatar_url": "https://avatars.githubusercontent.com/u/26889185?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kevinlu1248",
"html_url": "https://github.com/kevinlu1248",
"followers_url": "https://api.github.com/users/kevinlu1248/followers",
"following_url": "https://api.github.com/users/kevinlu1248/following{/other_user}",
"gists_url": "https://api.github.com/users/kevinlu1248/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kevinlu1248/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kevinlu1248/subscriptions",
"organizations_url": "https://api.github.com/users/kevinlu1248/orgs",
"repos_url": "https://api.github.com/users/kevinlu1248/repos",
"events_url": "https://api.github.com/users/kevinlu1248/events{/privacy}",
"received_events_url": "https://api.github.com/users/kevinlu1248/received_events",
"type": "User",
"site_admin": false
}
],
"milestone": null,
"comments": 1,
"created_at": "2023-08-24T09:49:43Z",
"updated_at": "2023-09-07T23:44:44Z",
"closed_at": null,
"author_association": "CONTRIBUTOR",
"active_lock_reason": null,
"body": "* In src/components/Navbar.tsx, define a class Navbar that extends React.Component.\n* In src/components/Navbar.tsx, implement a render method that returns the same JSX as the functional component.\n* In src/components/Navbar.tsx, replace the use of useBreakpointValue with this.state and this.setState.\n* In src/components/Navbar.tsx, write unit tests for the refactored component and run them to verify that they pass.\n\nParent issue: #335\n\n\n<details open>\n<summary>Checklist</summary>\n\n- [X] `src/components/Navbar.tsx`\n> • Refactor the Navbar component from a functional component to a class component. Define a new class that extends React.Component, implement a constructor to initialize state and bind methods, and implement a render method that returns the same JSX as the functional component.\n> • Replace the use of useBreakpointValue with this.state and this.setState. Initialize state in the constructor with properties that correspond to the values returned by useBreakpointValue, and update these state properties in response to window resize events.\n\n- [X] `src/components/Navbar.test.tsx`\n> • Write unit tests for the new Navbar class component. These tests should verify that the Navbar renders correctly for different window sizes and that it updates its state in response to window resize events.\n\n</details>\n",
"reactions": {
"url": "https://api.github.com/repos/sweepai/landing-page/issues/420/reactions",
"total_count": 1,
"+1": 0,
"-1": 0,
"laugh": 0,
"hooray": 0,
"confused": 0,
"heart": 0,
"rocket": 1,
"eyes": 0
},
"timeline_url": "https://api.github.com/repos/sweepai/landing-page/issues/420/timeline",
"performed_via_github_app": null,
"state_reason": null
},
"changes": {
"body": {
"from": "* In src/components/Navbar.tsx, define a class Navbar that extends React.Component.\n* In src/components/Navbar.tsx, implement a render method that returns the same JSX as the functional component.\n* In src/components/Navbar.tsx, replace the use of useBreakpointValue with this.state and this.setState.\n* In src/components/Navbar.tsx, write unit tests for the refactored component and run them to verify that they pass.\n\nParent issue: #335\n\n\n<details open>\n<summary>Checklist</summary>\n\n- [X] `src/components/Navbar.tsx`\n> • Refactor the Navbar component from a functional component to a class component. Define a new class that extends React.Component, implement a constructor to initialize state and bind methods, and implement a render method that returns the same JSX as the functional component.\n> • Replace the use of useBreakpointValue with this.state and this.setState. Initialize state in the constructor with properties that correspond to the values returned by useBreakpointValue, and update these state properties in response to window resize events.\n\n- [X] `src/components/Navbar.test.tsx`\n> • Write unit tests for the new Navbar class component. These tests should verify that the Navbar renders correctly for different window sizes and that it updates its state in response to window resize events.\n\n</details>\n"
},
"title": {
"from": "Sweep: Refactor Navbar to class component."
}
},
"repository": {
"id": 631774068,
"node_id": "R_kgDOJagbdA",
"name": "landing-page",
"full_name": "sweepai/landing-page",
"private": false,
"owner": {
"login": "sweepai",
"id": 127925974,
"node_id": "O_kgDOB5_-1g",
"avatar_url": "https://avatars.githubusercontent.com/u/127925974?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/sweepai",
"html_url": "https://github.com/sweepai",
"followers_url": "https://api.github.com/users/sweepai/followers",
"following_url": "https://api.github.com/users/sweepai/following{/other_user}",
"gists_url": "https://api.github.com/users/sweepai/gists{/gist_id}",
"starred_url": "https://api.github.com/users/sweepai/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/sweepai/subscriptions",
"organizations_url": "https://api.github.com/users/sweepai/orgs",
"repos_url": "https://api.github.com/users/sweepai/repos",
"events_url": "https://api.github.com/users/sweepai/events{/privacy}",
"received_events_url": "https://api.github.com/users/sweepai/received_events",
"type": "Organization",
"site_admin": false

embeddings = []
for batch in tqdm(chunk(texts, batch_size=BATCH_SIZE), disable=False):
embeddings.extend(embed_huggingface(texts))
return embeddings
else:
raise Exception("Hugging Face URL and token not set")
case "replicate":
if REPLICATE_API_KEY:
embeddings = []
for batch in tqdm(chunk(texts, batch_size=BATCH_SIZE)):
embeddings.extend(embed_replicate(batch))
return embeddings
else:
raise Exception("Replicate URL and token not set")
case _:
raise Exception("Invalid vector embedding mode")
logger.info(
f"Computed embeddings for {len(texts)} texts using {VECTOR_EMBEDDING_SOURCE}"
)
def embedding_function(texts: list[str]):
# For LRU cache to work
return embed_texts(tuple(texts))
def get_deeplake_vs_from_repo(
cloned_repo: ClonedRepo,
sweep_config: SweepConfig = SweepConfig(),
):
deeplake_vs = None
repo_full_name = cloned_repo.repo_full_name
repo = cloned_repo.repo
commits = repo.get_commits()
commit_hash = commits[0].sha
logger.info(f"Downloading repository and indexing for {repo_full_name}...")
start = time.time()
logger.info("Recursively getting list of files...")
snippets, file_list = repo_to_chunks(cloned_repo.cache_dir, sweep_config)
logger.info(f"Found {len(snippets)} snippets in repository {repo_full_name}")
# prepare lexical search
index = prepare_index_from_snippets(
snippets, len_repo_cache_dir=len(cloned_repo.cache_dir) + 1
)
logger.print("Prepared index from snippets")
# scoring for vector search
files_to_scores = {}
score_factors = []
for file_path in tqdm(file_list):
if not redis_client:
score_factor = compute_score(
file_path[len(cloned_repo.cache_dir) + 1 :], cloned_repo.git_repo
)
score_factors.append(score_factor)
continue
cache_key = hash_sha256(file_path) + CACHE_VERSION
try:
cache_value = redis_client.get(cache_key)
except Exception as e:
logger.exception(e)
cache_value = None
if cache_value is not None:
score_factor = json.loads(cache_value)
score_factors.append(score_factor)
else:
score_factor = compute_score(
file_path[len(cloned_repo.cache_dir) + 1 :], cloned_repo.git_repo
)
score_factors.append(score_factor)
redis_client.set(cache_key, json.dumps(score_factor))
# compute all scores
all_scores = get_scores(score_factors)
files_to_scores = {
file_path: score for file_path, score in zip(file_list, all_scores)
}
logger.info(f"Found {len(file_list)} files in repository {repo_full_name}")
documents = []
metadatas = []
ids = []
for snippet in snippets:
documents.append(snippet.get_snippet(add_ellipsis=False, add_lines=False))
metadata = {
"file_path": snippet.file_path[len(cloned_repo.cache_dir) + 1 :],
"start": snippet.start,
"end": snippet.end,
"score": files_to_scores[snippet.file_path],
}
metadatas.append(metadata)
gh_file_path = snippet.file_path[len("repo/") :]
ids.append(f"{gh_file_path}:{snippet.start}:{snippet.end}")
logger.info(f"Getting list of all files took {time.time() - start}")
logger.info(f"Received {len(documents)} documents from repository {repo_full_name}")
collection_name = parse_collection_name(repo_full_name)
deeplake_vs = deeplake_vs or compute_deeplake_vs(
collection_name, documents, ids, metadatas, commit_hash
)
return deeplake_vs, index, len(documents)
def compute_deeplake_vs(collection_name, documents, ids, metadatas, sha):
if len(documents) > 0:
logger.info(f"Computing embeddings with {VECTOR_EMBEDDING_SOURCE}...")

if redis_client and len(documents_to_compute) > 0:
logger.info(f"Updating cache with {len(computed_embeddings)} embeddings")
cache_keys = [
hash_sha256(doc)
+ SENTENCE_TRANSFORMERS_MODEL
+ VECTOR_EMBEDDING_SOURCE
+ CACHE_VERSION
for doc in documents_to_compute
]
redis_client.mset(
{
key: json.dumps(
embedding.tolist()
if isinstance(embedding, np.ndarray)
else embedding
)
for key, embedding in zip(cache_keys, computed_embeddings)
}
)
return deeplake_vs
else:
logger.error("No documents found in repository")
return deeplake_vs
# Only works on functions without side effects
@file_cache(ignore_params=["cloned_repo", "sweep_config", "token"])
def get_relevant_snippets(
cloned_repo: ClonedRepo,
query: str,
username: str | None = None,
sweep_config: SweepConfig = SweepConfig(),
lexical=True,
):
repo_name = cloned_repo.repo_full_name
installation_id = cloned_repo.installation_id
logger.info("Getting query embedding...")
query_embedding = embedding_function([query]) # pylint: disable=no-member
logger.info("Starting search by getting vector store...")
deeplake_vs, lexical_index, num_docs = get_deeplake_vs_from_repo(
cloned_repo, sweep_config=sweep_config
)
content_to_lexical_score = search_index(query, lexical_index)
logger.info(f"Found {len(content_to_lexical_score)} lexical results")
logger.info(f"Searching for relevant snippets... with {num_docs} docs")
results = {"metadata": [], "text": []}
try:
results = deeplake_vs.search(embedding=query_embedding, k=num_docs)
except SystemExit:
raise SystemExit
except Exception:
logger.exception("Exception occurred while fetching relevant snippets")
logger.info("Fetched relevant snippets...")
if len(results["text"]) == 0:
logger.info(f"Results query {query} was empty")
logger.info(f"Results: {results}")
if username is None:
username = "anonymous"
posthog.capture(
username,
"failed",
{
"reason": "Results query was empty",
"repo_name": repo_name,
"installation_id": installation_id,
"query": query,
},
)
return []
metadatas = results["metadata"]
code_scores = [metadata["score"] for metadata in metadatas]
lexical_scores = []
for metadata in metadatas:
key = f"{metadata['file_path']}:{str(metadata['start'])}:{str(metadata['end'])}"
if key in content_to_lexical_score:
lexical_scores.append(content_to_lexical_score[key])
else:
lexical_scores.append(0.3)
vector_scores = results["score"]
combined_scores = [
code_score * 4
+ vector_score
+ lexical_score * 2.5 # increase weight of lexical search
for code_score, vector_score, lexical_score in zip(
code_scores, vector_scores, lexical_scores
)
]
combined_list = list(zip(combined_scores, metadatas))
sorted_list = sorted(combined_list, key=lambda x: x[0], reverse=True)
sorted_metadatas = [metadata for _, metadata in sorted_list]
relevant_paths = [metadata["file_path"] for metadata in sorted_metadatas]
logger.info("Relevant paths: {}".format(relevant_paths[:5]))
return [
Snippet(
content="",


Step 2: ⌨️ Coding

  • sweepai/utils/github_utils_test.py ✅ Commit 0ded79e
Create sweepai/utils/github_utils_test.py with contents:
• Import the necessary libraries and modules. This includes `unittest`, `unittest.mock`, and the `ClonedRepo` class from `sweepai.utils.github_utils`.
• Create a new class `TestClonedRepo` that inherits from `unittest.TestCase`. This class will contain all the unit tests for the `ClonedRepo` class.
• Within the `TestClonedRepo` class, create a setup method (`setUp`) that initializes a `ClonedRepo` instance with mock data. Use `unittest.mock.Mock` or `unittest.mock.create_autospec` to create mock objects as needed.
• For each method in the `ClonedRepo` class, create a corresponding test method in the `TestClonedRepo` class. The test method should call the `ClonedRepo` method with various inputs and assert that the output is as expected. Use `unittest.mock.patch` as needed to mock any external resources or side effects.
• Ensure that each test method has a descriptive name that follows the `test_` pattern. This makes it clear which `ClonedRepo` method the test is for.
• For each test method, write a docstring that briefly describes what the test is checking.
  • sweepai/utils/github_utils_test.py ✅ Sandbox ran successfully
The following are the logs from running the sandbox:
Sandbox logs for 6c1e9b1
git add . && pre-commit run --files sweepai/utils/github_utils_test.py || exit 0 1/3 ✓
trim trailing whitespace.................................................Passed
fix end of files.........................................................Passed
check yaml...........................................(no files to check)Skipped
black....................................................................Passed
isort....................................................................Passed
autoflake................................................................Passed
if [[ "sweepai/utils/github_utils_test.py" == *.py ]]; then poetry run pylint --errors-only sweepai/utils/github_utils_test.py; else return 0; fi 2/3 ✓
(nothing was outputted)
if [[ "sweepai/utils/github_utils_test.py" == *.py ]]; then PYTHONPATH=. poetry run python sweepai/utils/github_utils_test.py; else return 0; fi 3/3 ✓
2023-10-21 20:00:44.757 | INFO     | sweepai.config.server::197 - No Sandbox URL found.
  • sweepai/utils/github_utils_test.py ✅ Sandbox ran successfully
The following are the logs from running the sandbox:
Sandbox logs for 0ded79e
git add . && pre-commit run --files sweepai/utils/github_utils_test.py || exit 0 1/3 ✓
trim trailing whitespace.................................................Passed
fix end of files.........................................................Passed
check yaml...........................................(no files to check)Skipped
black....................................................................Failed
- hook id: black
- files were modified by this hook
reformatted sweepai/utils/github_utils_test.py
All done! ✨ 🍰 ✨
1 file reformatted.
isort....................................................................Failed
- hook id: isort
- files were modified by this hook
Fixing /repo/sweepai/utils/github_utils_test.py
autoflake................................................................Passed
if [[ "sweepai/utils/github_utils_test.py" == *.py ]]; then poetry run pylint --errors-only sweepai/utils/github_utils_test.py; else return 0; fi 2/3 ✓
(nothing was outputted)
if [[ "sweepai/utils/github_utils_test.py" == *.py ]]; then PYTHONPATH=. poetry run python sweepai/utils/github_utils_test.py; else return 0; fi 3/3 ✓
2023-10-21 20:00:25.047 | INFO     | sweepai.config.server::197 - No Sandbox URL found.
  • sweepai/utils/github_utils_test.py ❌ Sandbox failed so I made additional changes
The following are the logs from running the sandbox:
Sandbox logs for c948c97
git add . && pre-commit run --files sweepai/utils/github_utils_test.py || exit 0 1/3 ✓
trim trailing whitespace.................................................Passed
fix end of files.........................................................Passed
check yaml...........................................(no files to check)Skipped
black....................................................................Failed
- hook id: black
- files were modified by this hook
reformatted sweepai/utils/github_utils_test.py
All done! ✨ 🍰 ✨
1 file reformatted.
isort....................................................................Failed
- hook id: isort
- files were modified by this hook
Fixing /repo/sweepai/utils/github_utils_test.py
autoflake................................................................Passed
if [[ "sweepai/utils/github_utils_test.py" == *.py ]]; then poetry run pylint --errors-only sweepai/utils/github_utils_test.py; else return 0; fi 2/3 ✓
(nothing was outputted)
if [[ "sweepai/utils/github_utils_test.py" == *.py ]]; then PYTHONPATH=. poetry run python sweepai/utils/github_utils_test.py; else return 0; fi 3/3 ❌ (`1`)
2023-10-21 19:59:16.919 | INFO     | sweepai.config.server::197 - No Sandbox URL found.
Traceback (most recent call last):
  File "/usr/lib/python3.10/unittest/mock.py", line 1614, in _get_target
    target, attribute = target.rsplit('.', 1)
ValueError: not enough values to unpack (expected 2, got 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/repo/sweepai/utils/github_utils_test.py", line 7, in 
    class TestClonedRepo(unittest.TestCase):
  File "/repo/sweepai/utils/github_utils_test.py", line 45, in TestClonedRepo
    @patch("open")
  File "/usr/lib/python3.10/unittest/mock.py", line 1775, in patch
    getter, attribute = _get_target(target)
  File "/usr/lib/python3.10/unittest/mock.py", line 1616, in _get_target
    raise TypeError(
TypeError: Need a valid target to patch. You supplied: 'open'

Step 3: 🔁 Code Review

I have finished reviewing the code for completeness. I did not find errors for sweep/add-cloned-repo-tests_2.


🎉 Latest improvements to Sweep:

  • Sweep can now passively improve your repository! Check out Rules to learn more.

💡 To recreate the pull request edit the issue title or description. To tweak the pull request, leave a comment on the pull request.
Join Our Discord

wwzeng1 pushed a commit that referenced this issue Oct 21, 2023
## Description
This PR adds unit tests for the `ClonedRepo` class in the
`sweepai/utils/github_utils.py` file. The `ClonedRepo` class is
responsible for various operations related to cloning a GitHub
repository, listing its directory tree, getting file contents, and more.
The added unit tests ensure that these functionalities are working as
expected.

The tests have been added in a new file
`sweepai/utils/github_utils_test.py`. Each method in the `ClonedRepo`
class has a corresponding test case. Mocking is used to isolate the
`ClonedRepo` class and its methods for testing.

## Summary of Changes
- Created a new file `sweepai/utils/github_utils_test.py` for the unit
tests.
- Added a `TestClonedRepo` class that contains all the unit tests for
the `ClonedRepo` class.
- For each method in the `ClonedRepo` class, a corresponding test method
has been added in the `TestClonedRepo` class.
- Used `unittest.mock` to create mock objects and patch external
resources as needed.

Fixes #2377.

---

### 🎉 Latest improvements to Sweep:

* Sweep can now passively improve your repository! Check out
[Rules](https://docs.sweep.dev/usage/config#rules) to learn more.


---

### 💡 To get Sweep to edit this pull request, you can:
* Comment below, and Sweep can edit the entire PR
* Comment on a file, Sweep will only modify the commented file
* Edit the original issue to get Sweep to recreate the PR from scratch

---------

Co-authored-by: sweep-nightly[bot] <131841235+sweep-nightly[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
sweep Assigns Sweep to an issue or pull request.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant