-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from kayvane1/staging
Adding tests to production
- Loading branch information
Showing
9 changed files
with
678 additions
and
325 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,7 @@ | ||
from __future__ import annotations | ||
|
||
from typing import Any | ||
from typing import Optional | ||
from typing import cast | ||
|
||
import httpx | ||
|
||
|
||
class BraveError(Exception): | ||
"""Base exception class for all Brave Search API errors.""" | ||
|
||
pass | ||
|
||
|
||
class APIError(BraveError): | ||
"""Exception raised when the API returns an error response.""" | ||
|
||
message: str | ||
request: httpx.Request | ||
|
||
body: object | None | ||
|
||
"""The API response body. | ||
If the API responded with a valid JSON structure then this property will be the | ||
decoded result. | ||
If it isn't a valid JSON structure then this will be the raw response. | ||
If there was no response associated with this error then it will be `None`. | ||
""" | ||
|
||
code: Optional[str] | ||
param: Optional[str] | ||
type: Optional[str] | ||
|
||
def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None: | ||
super().__init__(message) | ||
self.request = request | ||
self.message = message | ||
|
||
if isinstance(body, dict): | ||
self.code = cast(Any, body.get("code")) | ||
self.param = cast(Any, body.get("param")) | ||
self.type = cast(Any, body.get("type")) | ||
else: | ||
self.code = None | ||
self.param = None | ||
self.type = None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
{ | ||
"query": { | ||
"original": "Blue tack", | ||
"show_strict_warning": false, | ||
"is_navigational": false, | ||
"is_news_breaking": false, | ||
"local_decision": "drop", | ||
"local_locations_idx": 0, | ||
"spellcheck_off": true, | ||
"country": "us", | ||
"bad_results": false, | ||
"should_fallback": false, | ||
"postal_code": "", | ||
"city": "", | ||
"state": "", | ||
"header_country": "", | ||
"more_results_available": true | ||
}, | ||
"mixed": { | ||
"type": "mixed", | ||
"main": [ | ||
{ | ||
"type": "web", | ||
"index": 0, | ||
"all": false | ||
}, | ||
{ | ||
"type": "web", | ||
"index": 1, | ||
"all": false | ||
} | ||
], | ||
"top": [], | ||
"side": [] | ||
}, | ||
"type": "search", | ||
"web": { | ||
"type": "search", | ||
"results": [ | ||
{ | ||
"title": "Blu Tack - Wikipedia", | ||
"url": "https://en.wikipedia.org/wiki/Blu_Tack", | ||
"is_source_local": false, | ||
"is_source_both": false, | ||
"description": "Blu Tack is a <strong>reusable putty-like pressure-sensitive adhesive produced by Bostik</strong>, commonly used to attach lightweight objects (such as posters or sheets of paper) to walls, doors or other dry surfaces. Traditionally blue, it is also available in other colours.", | ||
"page_age": "2023-11-23T09:26:19", | ||
"language": "en", | ||
"profile": { | ||
"name": "Wikipedia", | ||
"url": "https://en.wikipedia.org/wiki/Blu_Tack", | ||
"long_name": "en.wikipedia.org", | ||
"img": "https://imgs.search.brave.com/0kxnVOiqv-faZvOJc7zpym4Zin1CTs1f1svfNZSzmfU/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNjQwNGZhZWY0/ZTQ1YWUzYzQ3MDUw/MmMzMGY3NTQ0ZjNj/NDUwMDk5ZTI3MWRk/NWYyNTM4N2UwOTE0/NTI3ZDQzNy9lbi53/aWtpcGVkaWEub3Jn/Lw" | ||
}, | ||
"family_friendly": true, | ||
"type": "search_result", | ||
"subtype": "generic", | ||
"deep_results": {}, | ||
"meta_url": { | ||
"scheme": "https", | ||
"netloc": "en.wikipedia.org", | ||
"hostname": "en.wikipedia.org", | ||
"favicon": "https://imgs.search.brave.com/0kxnVOiqv-faZvOJc7zpym4Zin1CTs1f1svfNZSzmfU/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNjQwNGZhZWY0/ZTQ1YWUzYzQ3MDUw/MmMzMGY3NTQ0ZjNj/NDUwMDk5ZTI3MWRk/NWYyNTM4N2UwOTE0/NTI3ZDQzNy9lbi53/aWtpcGVkaWEub3Jn/Lw", | ||
"path": "› wiki › Blu_Tack" | ||
}, | ||
"age": "1 month ago" | ||
} | ||
], | ||
"family_friendly": true | ||
}, | ||
"videos": { | ||
"type": "videos", | ||
"results": [ | ||
{ | ||
"title": "10 Amazing Uses for Blu Tack - YouTube", | ||
"url": "https://www.youtube.com/watch?v=yLsGCdWmqfs", | ||
"description": "Blu Tack has to be one of the World's most versatile products so here are 10 little-known uses for the malleable substance.", | ||
"page_age": "2015-12-02T16:06:16", | ||
"type": "video_result", | ||
"video": {}, | ||
"meta_url": { | ||
"scheme": "https", | ||
"netloc": "youtube.com", | ||
"hostname": "www.youtube.com", | ||
"favicon": "https://imgs.search.brave.com/Ux4Hee4evZhvjuTKwtapBycOGjGDci2Gvn2pbSzvbC0/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTkyZTZiMWU3/YzU3Nzc5YjExYzUy/N2VhZTIxOWNlYjM5/ZGVjN2MyZDY4Nzdh/ZDYzMTYxNmI5N2Rk/Y2Q3N2FkNy93d3cu/eW91dHViZS5jb20v", | ||
"path": "› watch" | ||
}, | ||
"thumbnail": { | ||
"src": "https://imgs.search.brave.com/iZOYyMN6JdLCMaQtGLzBZF0Rpq_HokKmVlPmZHT7UFY/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9pLnl0/aW1nLmNvbS92aS95/THNHQ2RXbXFmcy9o/cWRlZmF1bHQuanBn" | ||
}, | ||
"age": "December 2, 2015" | ||
}, | ||
{ | ||
"title": "Several usages of Blu Tack | How to use Blu Tack | Blu tack uses ...", | ||
"url": "https://www.youtube.com/watch?v=nUoEC1ucl7o", | ||
"description": "In this video I have shown some amazing usages of Blu Tack. Blu Tack is a reusable adhesive and is commonly used to stick light weight objects to the walls, ...", | ||
"page_age": "2018-07-13T10:09:03", | ||
"type": "video_result", | ||
"video": {}, | ||
"meta_url": { | ||
"scheme": "https", | ||
"netloc": "youtube.com", | ||
"hostname": "www.youtube.com", | ||
"favicon": "https://imgs.search.brave.com/Ux4Hee4evZhvjuTKwtapBycOGjGDci2Gvn2pbSzvbC0/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTkyZTZiMWU3/YzU3Nzc5YjExYzUy/N2VhZTIxOWNlYjM5/ZGVjN2MyZDY4Nzdh/ZDYzMTYxNmI5N2Rk/Y2Q3N2FkNy93d3cu/eW91dHViZS5jb20v", | ||
"path": "› watch" | ||
}, | ||
"thumbnail": { | ||
"src": "https://imgs.search.brave.com/pK3CozSg3Nqqeh0NqSk0qwdVTN488h0RUg-RNZPKu84/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9pLnl0/aW1nLmNvbS92aS9u/VW9FQzF1Y2w3by9t/YXhyZXNkZWZhdWx0/LmpwZw" | ||
}, | ||
"age": "July 13, 2018" | ||
} | ||
], | ||
"mutated_by_goggles": false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import json | ||
|
||
from unittest.mock import AsyncMock | ||
|
||
import httpx | ||
import pytest | ||
import tenacity | ||
|
||
from brave.async_brave import AsyncBrave | ||
from brave.types import WebSearchApiResponse | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_async_brave_initialization(): | ||
client = AsyncBrave(api_key="test_key") | ||
assert client.api_key == "test_key" | ||
assert client.endpoint == "web" # Assuming "web" is the default endpoint | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_async_get_success(monkeypatch): | ||
async def mock_get(*args, **kwargs): | ||
# Create a Mock Response | ||
mock_response = httpx.Response(200, json={"data": "test response"}) | ||
# Setting the request attribute | ||
mock_response._request = httpx.Request(method="GET", url=args[0]) | ||
return mock_response | ||
|
||
monkeypatch.setattr(httpx.AsyncClient, "get", AsyncMock(side_effect=mock_get)) | ||
|
||
client = AsyncBrave(api_key="test_key") | ||
response = await client._get(params={"q": "test query"}) | ||
assert response.json() == {"data": "test response"} | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_async_get_with_retries(monkeypatch): | ||
call_count = 0 | ||
|
||
async def mock_get(*args, **kwargs): | ||
nonlocal call_count | ||
call_count += 1 | ||
if call_count < 3: | ||
raise httpx.HTTPError("Temporary failure") | ||
# Create a Mock Response | ||
mock_response = httpx.Response(200, json={"data": "test response after retries"}) | ||
# Setting the request attribute | ||
mock_response._request = httpx.Request(method="GET", url=args[0]) | ||
return mock_response | ||
|
||
monkeypatch.setattr(httpx.AsyncClient, "get", AsyncMock(side_effect=mock_get)) | ||
|
||
client = AsyncBrave(api_key="test_key") | ||
response = await client._get(params={"q": "test query"}) | ||
assert call_count == 3 | ||
assert response.json() == {"data": "test response after retries"} | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_async_get_failure(monkeypatch): | ||
# Mocking httpx.AsyncClient.get to always raise an HTTPError | ||
monkeypatch.setattr(httpx.AsyncClient, "get", AsyncMock(side_effect=httpx.HTTPError("Permanent failure"))) | ||
|
||
client = AsyncBrave(api_key="test_key") | ||
|
||
# Expecting tenacity.RetryError instead of httpx.HTTPError | ||
with pytest.raises(tenacity.RetryError): | ||
await client._get(params={"q": "test query"}) | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_response_validation(monkeypatch): | ||
|
||
with open("tests/test_responses/blue_tack_minimal.json", "r") as f: | ||
_mock_response = json.load(f) | ||
|
||
async def mock_get(*args, **kwargs): | ||
mock_response = httpx.Response(200, json=_mock_response) | ||
mock_response._request = httpx.Request(method="GET", url=args[0]) | ||
return mock_response | ||
|
||
monkeypatch.setattr(httpx.AsyncClient, "get", AsyncMock(side_effect=mock_get)) | ||
|
||
client = AsyncBrave(api_key="test_key") | ||
response = await client.search("Blue tack") # Replace with the actual async method | ||
assert isinstance(response, WebSearchApiResponse) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,56 @@ | ||
def test_placeholder(): | ||
pass | ||
import json | ||
|
||
from unittest.mock import patch | ||
|
||
import pytest | ||
|
||
from brave.client import BraveAPIClient | ||
from brave.exceptions import BraveError | ||
from brave.types import WebSearchApiResponse | ||
|
||
|
||
with open("tests/test_responses/blue_tack_minimal.json", "r") as f: | ||
_mock_response = json.load(f) | ||
|
||
mock_response = WebSearchApiResponse.model_validate(_mock_response) | ||
|
||
|
||
def test_init_with_explicit_api_key(): | ||
api_key = "test_api_key" | ||
client = BraveAPIClient(api_key=api_key) | ||
assert client.api_key == api_key | ||
|
||
|
||
def test_init_with_env_var_api_key(monkeypatch): | ||
monkeypatch.setenv("BRAVE_API_KEY", "env_api_key") | ||
client = BraveAPIClient() | ||
assert client.api_key == "env_api_key" | ||
|
||
|
||
def test_init_error_without_api_key(monkeypatch): | ||
monkeypatch.delenv("BRAVE_API_KEY", raising=False) | ||
with pytest.raises(BraveError): | ||
BraveAPIClient(api_key=None) | ||
|
||
|
||
def test_default_endpoint_on_init(): | ||
client = BraveAPIClient(api_key="test_api_key") | ||
assert client.endpoint == "web" | ||
|
||
|
||
def test_custom_endpoint_on_init(): | ||
client = BraveAPIClient(api_key="test_api_key", endpoint="custom_endpoint") | ||
assert client.endpoint == "custom_endpoint" | ||
|
||
|
||
def test_api_key_none_and_env_var_not_set(monkeypatch): | ||
monkeypatch.delenv("BRAVE_API_KEY", raising=False) | ||
with pytest.raises(BraveError): | ||
BraveAPIClient(api_key=None) | ||
|
||
|
||
def test_response_type_validation(): | ||
with patch.object(BraveAPIClient, "search", return_value=mock_response): | ||
client = BraveAPIClient(api_key="test_api_key") | ||
response = client.search(q="Blue tack") | ||
assert isinstance(response, WebSearchApiResponse) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from unittest.mock import Mock | ||
from unittest.mock import patch | ||
|
||
from brave.sync import Brave | ||
|
||
|
||
def test_brave_initialization(): | ||
client = Brave(api_key="test_key") | ||
assert client.api_key == "test_key" | ||
assert client.endpoint == "web" # Assuming "web" is the default endpoint | ||
|
||
|
||
def test_sync_get_success(): | ||
with patch("requests.get") as mock_get: | ||
mock_get.return_value = Mock(status_code=200, json=lambda: {"data": "test response"}) | ||
|
||
client = Brave(api_key="test_key") | ||
response = client._get(params={"q": "test query"}) | ||
assert response.json() == {"data": "test response"} |