Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[report]
exclude_lines =
pragma: no cover
@abstract
4 changes: 2 additions & 2 deletions notion_client/api_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from notion_client.helpers import pick
from notion_client.typing import SyncAsync

if TYPE_CHECKING:
if TYPE_CHECKING: # pragma: no cover
from notion_client.client import BaseClient


Expand Down Expand Up @@ -110,7 +110,7 @@ def delete(self, block_id: str, **kwargs: Any) -> SyncAsync[Any]:


class DatabasesEndpoint(Endpoint):
def list(self, **kwargs: Any) -> SyncAsync[Any]:
def list(self, **kwargs: Any) -> SyncAsync[Any]: # pragma: no cover
"""List all [Databases](https://developers.notion.com/reference/database) shared with the authenticated integration.

> ⚠️ **Deprecated endpoint**
Expand Down
24 changes: 24 additions & 0 deletions tests/cassettes/test_api_async_request_bad_request_error.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
interactions:
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate
authorization:
- secret_...
connection:
- keep-alive
host:
- httpstat.us
notion-version:
- '2022-06-28'
method: GET
uri: https://httpstat.us/400
response:
content: 400 Bad Request
headers: {}
http_version: HTTP/1.1
status_code: 400
version: 1
24 changes: 24 additions & 0 deletions tests/cassettes/test_api_http_response_error.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
interactions:
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate
authorization:
- secret_...
connection:
- keep-alive
host:
- httpstat.us
notion-version:
- '2022-06-28'
method: GET
uri: https://httpstat.us/400
response:
content: 400 Bad Request
headers: {}
http_version: HTTP/1.1
status_code: 400
version: 1
46 changes: 46 additions & 0 deletions tests/cassettes/test_api_response_error.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
interactions:
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate
connection:
- keep-alive
host:
- api.notion.com
notion-version:
- '2022-06-28'
method: GET
uri: https://api.notion.com/v1/invalid
response:
content: '{"object":"error","status":400,"code":"invalid_request_url","message":"Invalid
request URL."}'
headers: {}
http_version: HTTP/1.1
status_code: 400
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate
authorization:
- secret_...
connection:
- keep-alive
host:
- api.notion.com
notion-version:
- '2022-06-28'
method: GET
uri: https://api.notion.com/v1/users
response:
content: '{"object":"error","status":401,"code":"unauthorized","message":"API
token is invalid."}'
headers: {}
http_version: HTTP/1.1
status_code: 401
version: 1
46 changes: 46 additions & 0 deletions tests/cassettes/test_async_api_response_error.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
interactions:
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate
connection:
- keep-alive
host:
- api.notion.com
notion-version:
- '2022-06-28'
method: GET
uri: https://api.notion.com/v1/invalid
response:
content: '{"object":"error","status":400,"code":"invalid_request_url","message":"Invalid
request URL."}'
headers: {}
http_version: HTTP/1.1
status_code: 400
- request:
body: ''
headers:
accept:
- '*/*'
accept-encoding:
- gzip, deflate
authorization:
- secret_...
connection:
- keep-alive
host:
- api.notion.com
notion-version:
- '2022-06-28'
method: GET
uri: https://api.notion.com/v1/users
response:
content: '{"object":"error","status":401,"code":"unauthorized","message":"API
token is invalid."}'
headers: {}
http_version: HTTP/1.1
status_code: 401
version: 1
5 changes: 3 additions & 2 deletions tests/cassettes/test_async_client_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ interactions:
method: GET
uri: https://api.notion.com/v1/users
response:
content: '{"object":"list","results":[{"object":"user","id":"a4f789cc-7bc8-4cf0-82b9-a8ba7d985ecf","name":"Guillaume
Gelin","avatar_url":"https://s3-us-west-2.amazonaws.com/public.notion-static.com/1cddf30d-ef25-4372-a8bd-b510a11e26e8/avatar.jpeg","type":"person","person":{"email":"[email protected]"}},{"object":"user","id":"7775f3a3-893f-43fa-b625-460c61094c78","name":"notion-sdk-py","avatar_url":null,"type":"bot","bot":{"owner":{"type":"workspace","workspace":true}}}],"next_cursor":null,"has_more":false,"type":"user","user":{}}'
content: '{"object":"list","results":[{"object":"user","id":"04db0808-966a-4668-9a27-8be05465b9c9","name":"Notion
Testing Account","avatar_url":null,"type":"person","person":{}},{"object":"user","id":"823cf07b-34df-4ae6-9fbc-dbc600a815c6","name":"Testing","avatar_url":null,"type":"bot","bot":{"owner":{"type":"workspace","workspace":true},"workspace_name":"Notion
Testing Account''s Notion"}}],"next_cursor":null,"has_more":false,"type":"user","user":{}}'
headers: {}
http_version: HTTP/1.1
status_code: 200
Expand Down
7 changes: 5 additions & 2 deletions tests/cassettes/test_async_client_request_auth.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ interactions:
- '*/*'
accept-encoding:
- gzip, deflate
authorization:
- secret_...
connection:
- keep-alive
host:
Expand Down Expand Up @@ -38,8 +40,9 @@ interactions:
method: GET
uri: https://api.notion.com/v1/users
response:
content: '{"object":"list","results":[{"object":"user","id":"a4f789cc-7bc8-4cf0-82b9-a8ba7d985ecf","name":"Guillaume
Gelin","avatar_url":"https://s3-us-west-2.amazonaws.com/public.notion-static.com/1cddf30d-ef25-4372-a8bd-b510a11e26e8/avatar.jpeg","type":"person","person":{"email":"[email protected]"}},{"object":"user","id":"7775f3a3-893f-43fa-b625-460c61094c78","name":"notion-sdk-py","avatar_url":null,"type":"bot","bot":{"owner":{"type":"workspace","workspace":true}}}],"next_cursor":null,"has_more":false,"type":"user","user":{}}'
content: '{"object":"list","results":[{"object":"user","id":"04db0808-966a-4668-9a27-8be05465b9c9","name":"Notion
Testing Account","avatar_url":null,"type":"person","person":{}},{"object":"user","id":"823cf07b-34df-4ae6-9fbc-dbc600a815c6","name":"Testing","avatar_url":null,"type":"bot","bot":{"owner":{"type":"workspace","workspace":true},"workspace_name":"Notion
Testing Account''s Notion"}}],"next_cursor":null,"has_more":false,"type":"user","user":{}}'
headers: {}
http_version: HTTP/1.1
status_code: 200
Expand Down
5 changes: 3 additions & 2 deletions tests/cassettes/test_client_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ interactions:
method: GET
uri: https://api.notion.com/v1/users
response:
content: '{"object":"list","results":[{"object":"user","id":"a4f789cc-7bc8-4cf0-82b9-a8ba7d985ecf","name":"Guillaume
Gelin","avatar_url":"https://s3-us-west-2.amazonaws.com/public.notion-static.com/1cddf30d-ef25-4372-a8bd-b510a11e26e8/avatar.jpeg","type":"person","person":{"email":"[email protected]"}},{"object":"user","id":"7775f3a3-893f-43fa-b625-460c61094c78","name":"notion-sdk-py","avatar_url":null,"type":"bot","bot":{"owner":{"type":"workspace","workspace":true}}}],"next_cursor":null,"has_more":false,"type":"user","user":{}}'
content: '{"object":"list","results":[{"object":"user","id":"04db0808-966a-4668-9a27-8be05465b9c9","name":"Notion
Testing Account","avatar_url":null,"type":"person","person":{}},{"object":"user","id":"823cf07b-34df-4ae6-9fbc-dbc600a815c6","name":"Testing","avatar_url":null,"type":"bot","bot":{"owner":{"type":"workspace","workspace":true},"workspace_name":"Notion
Testing Account''s Notion"}}],"next_cursor":null,"has_more":false,"type":"user","user":{}}'
headers: {}
http_version: HTTP/1.1
status_code: 200
Expand Down
7 changes: 5 additions & 2 deletions tests/cassettes/test_client_request_auth.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ interactions:
- '*/*'
accept-encoding:
- gzip, deflate
authorization:
- secret_...
connection:
- keep-alive
host:
Expand Down Expand Up @@ -38,8 +40,9 @@ interactions:
method: GET
uri: https://api.notion.com/v1/users
response:
content: '{"object":"list","results":[{"object":"user","id":"a4f789cc-7bc8-4cf0-82b9-a8ba7d985ecf","name":"Guillaume
Gelin","avatar_url":"https://s3-us-west-2.amazonaws.com/public.notion-static.com/1cddf30d-ef25-4372-a8bd-b510a11e26e8/avatar.jpeg","type":"person","person":{"email":"[email protected]"}},{"object":"user","id":"7775f3a3-893f-43fa-b625-460c61094c78","name":"notion-sdk-py","avatar_url":null,"type":"bot","bot":{"owner":{"type":"workspace","workspace":true}}}],"next_cursor":null,"has_more":false,"type":"user","user":{}}'
content: '{"object":"list","results":[{"object":"user","id":"04db0808-966a-4668-9a27-8be05465b9c9","name":"Notion
Testing Account","avatar_url":null,"type":"person","person":{}},{"object":"user","id":"823cf07b-34df-4ae6-9fbc-dbc600a815c6","name":"Testing","avatar_url":null,"type":"bot","bot":{"owner":{"type":"workspace","workspace":true},"workspace_name":"Notion
Testing Account''s Notion"}}],"next_cursor":null,"has_more":false,"type":"user","user":{}}'
headers: {}
http_version: HTTP/1.1
status_code: 200
Expand Down
12 changes: 5 additions & 7 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

from notion_client import AsyncClient, Client

TEST_PAGE_NAME = f"Test Page - {datetime.now()}"


@pytest.fixture(scope="session")
def vcr_config():
Expand Down Expand Up @@ -119,13 +117,13 @@ def comment_id(client, page_id) -> str:
yield response["id"]


@pytest.fixture(scope="session")
@pytest.fixture(scope="module")
def client(token: Optional[str]):
return Client({"auth": token})
with Client({"auth": token}) as client:
yield client


@pytest.fixture
async def async_client(token: Optional[str]):
client = AsyncClient({"auth": token})
yield client
await client.aclose()
async with AsyncClient({"auth": token}) as client:
yield client
6 changes: 4 additions & 2 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,20 @@ async def test_async_client_request(async_client):

@pytest.mark.vcr()
def test_client_request_auth(token):
client = Client()
client = Client(auth="Invalid")

with pytest.raises(APIResponseError):
client.request("/users", "GET")

response = client.request("/users", "GET", auth=token)
assert response["results"]

client.close()


@pytest.mark.vcr()
async def test_async_client_request_auth(token):
async_client = AsyncClient()
async_client = AsyncClient(auth="Invalid")

with pytest.raises(APIResponseError):
await async_client.request("/users", "GET")
Expand Down
9 changes: 2 additions & 7 deletions tests/test_endpoints.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
from datetime import datetime

import pytest

from tests.conftest import TEST_PAGE_NAME


@pytest.mark.vcr()
def test_pages_create(client, parent_page_id):
response = client.pages.create(
parent={"page_id": parent_page_id},
properties={
"title": [{"text": {"content": TEST_PAGE_NAME}}],
"title": [{"text": {"content": "Test Page"}}],
},
children=[],
)
Expand Down Expand Up @@ -125,11 +121,10 @@ def test_search(client, page_id):

@pytest.mark.vcr()
def test_databases_create(client, page_id):
db_name = f"Test Database - {datetime.now()}"
properties = {
"Name": {"title": {}}, # required property
}
title = [{"type": "text", "text": {"content": db_name}}]
title = [{"type": "text", "text": {"content": "Test Database"}}]
parent = {"type": "page_id", "page_id": page_id}
response = client.databases.create(
**{"parent": parent, "title": title, "properties": properties}
Expand Down
66 changes: 66 additions & 0 deletions tests/test_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import pytest
from httpx import TimeoutException

from notion_client.errors import (
APIResponseError,
HTTPResponseError,
RequestTimeoutError,
is_api_error_code,
)

STATUS_PAGE_BAD_REQUEST = "https://httpstat.us/400"


@pytest.mark.vcr()
def test_api_response_error(client):
with pytest.raises(APIResponseError):
client.request("/invalid", "GET")
with pytest.raises(APIResponseError):
client.request("/users", "GET", auth="Invalid")


def test_api_request_timeout_error(monkeypatch, client):
def mock_timeout_request(*args):
raise TimeoutException("Mock Timeout")

monkeypatch.setattr(client.client, "send", mock_timeout_request)

with pytest.raises(RequestTimeoutError):
client.request("/users", "GET")


@pytest.mark.vcr()
def test_api_http_response_error(client):
with pytest.raises(HTTPResponseError):
client.request(STATUS_PAGE_BAD_REQUEST, "GET")


@pytest.mark.vcr()
async def test_async_api_response_error(async_client):
with pytest.raises(APIResponseError):
await async_client.request("/invalid", "GET")
with pytest.raises(APIResponseError):
await async_client.request("/users", "GET", auth="Invalid")


async def test_async_api_request_timeout_error(monkeypatch, async_client):
def mock_timeout_request(*args):
raise TimeoutException("Mock Timeout")

monkeypatch.setattr(async_client.client, "send", mock_timeout_request)

with pytest.raises(RequestTimeoutError):
await async_client.request("/users", "GET")


@pytest.mark.vcr()
async def test_api_async_request_bad_request_error(async_client):
with pytest.raises(HTTPResponseError):
await async_client.request(STATUS_PAGE_BAD_REQUEST, "GET")


async def test_is_api_error_code():
error_code = "unauthorized"
assert is_api_error_code(error_code)
assert not is_api_error_code(None)
assert not is_api_error_code(404)
2 changes: 2 additions & 0 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ def test_get_id():
assert get_id(db_url) == db_id
with pytest.raises(ValueError):
get_id("https://example.com")
with pytest.raises(ValueError):
get_id("https://notion.so/123")
with pytest.raises(ValueError):
get_id("https://notion.so/99572135464649b-d95a14ff08f79c7a5")


Expand Down