Skip to content

Commit

Permalink
create integration test suite (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
thrau authored Mar 31, 2024
1 parent 77734b4 commit 098adee
Show file tree
Hide file tree
Showing 14 changed files with 352 additions and 29 deletions.
42 changes: 20 additions & 22 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
- main

jobs:
test:
unit-tests:
runs-on: ${{ matrix.os }}
strategy:
matrix:
Expand All @@ -27,27 +27,25 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

- name: Download LocalStack
run: docker pull localstack/localstack

- name: Run tests
- name: Run unit tests
run: |
make test-coverage
make test-unit
integration-tests:
runs-on: "ubuntu-latest"

steps:
- name: Checkout
uses: actions/checkout@v2

- name: Coveralls Parallel
uses: coverallsapp/github-action@master
- name: Set up Python
uses: actions/setup-python@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
flag-name: run-${{ matrix.os }}-${{ matrix.python_version }}
path-to-lcov: ./.coverage.lcov
parallel: true

# report:
# needs: test
# runs-on: ubuntu-latest
# steps:
# - name: Report coveralls
# uses: coverallsapp/github-action@master
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# parallel-finished: true
python-version: "3.11"

- name: Run integration tests
env:
NOTION_OBJECTS_TOKEN: ${{ secrets.NOTION_OBJECTS_TOKEN }}
NOTION_OBJECTS_TEST_DATABASE: ${{ secrets.NOTION_OBJECTS_TEST_DATABASE }}
run: |
make test-integration
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ clean:
format:
$(VENV_RUN); python -m isort .; python -m black .

test: venv
$(VENV_RUN); python -m pytest --cov notion_objects/
test: test-unit

test-unit: venv
$(VENV_RUN); python -m pytest tests/unit

test-integration: venv
$(VENV_RUN); python -m pytest tests/integration

test-coverage: venv
$(VENV_RUN); coverage run --source=notion_objects -m pytest tests && coverage lcov -o .coverage.lcov
Expand Down
6 changes: 4 additions & 2 deletions notion_objects/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,17 @@ def __init__(
mapped_type: Union[
Type[_N], Type[DynamicNotionObject], Callable[[DatabaseRecord], _N]
] = None,
): ...
):
...

@overload
def __init__(
self,
mapped_type: Union[Type[_N], Type[DynamicNotionObject], Callable[[DatabaseRecord], _N]],
database_id: str,
client: Client,
): ...
):
...

def __init__(self, *args, **kwargs):
if len(args) > 0 and type(args[0]) == str:
Expand Down
4 changes: 3 additions & 1 deletion notion_objects/encode.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from datetime import date, datetime
from typing import Iterable

from .properties import DateRange, DateTimeRange, Property
from .properties import DateRange, DateTimeRange, Property, RichTextProperty
from .values import DateValue, UserValue


Expand Down Expand Up @@ -48,6 +48,8 @@ def to_dict(self, flat: bool = False) -> dict:
result[f"{key}_end"] = value[1]
else:
result[key] = {"start": value[0], "end": value[1]}
elif isinstance(prop, RichTextProperty):
result[key] = "".join([text.plain_text for text in value])
else:
result[key] = value

Expand Down
5 changes: 3 additions & 2 deletions notion_objects/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,14 +289,15 @@ def set(
if not value:
obj[field] = {self.type: []}
return
if isinstance(value, rich_text.RichTextObject):
if isinstance(value, str):
value = [rich_text.RichText(value)]
elif isinstance(value, rich_text.RichTextObject):
value = [value]

obj[field] = {"rich_text": [rt.to_dict() for rt in value]}


class _SimpleStringProperty(Property[Optional[str]]):

def get(self, field: str, obj: dict) -> Optional[str]:
return obj["properties"][field][self.type]

Expand Down
3 changes: 3 additions & 0 deletions notion_objects/rich_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ def to_dict(self) -> dict:
def __repr__(self):
return str(self.to_dict())

def __eq__(self, other):
return self.to_dict() == other.to_dict()


class RichText(RichTextObject):
type = "text"
Expand Down
Empty file added tests/integration/__init__.py
Empty file.
59 changes: 59 additions & 0 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import copy
import os
import time
import uuid

import pytest
from notion_client import Client

import notion_objects


@pytest.fixture(scope="session")
def notion_client():
return Client(auth=os.environ["NOTION_OBJECTS_TOKEN"])


@pytest.fixture(scope="session")
def test_run_database_id():
return os.environ["NOTION_OBJECTS_TEST_DATABASE"]


@pytest.fixture(scope="session")
def test_run_page(notion_client, test_run_database_id):
name = f"pytest-{int(time.time() * 1000)}"

page = notion_client.pages.create(
parent={
"database_id": test_run_database_id,
},
properties={
"Name": {"title": [{"text": {"content": name}}]},
"Version": {"rich_text": [{"text": {"content": f"{notion_objects.__version__}"}}]},
"Ref": {"rich_text": [{"text": {"content": f"{os.getenv('GITHUB_REF', '')}"}}]},
},
)

yield page

# notion_client.pages.update(page["id"], archived=True)


@pytest.fixture(scope="session")
def create_database(notion_client, test_run_page):
def _create(**kwargs):
kwargs = copy.deepcopy(kwargs)

if "parent" not in kwargs:
kwargs["parent"] = {
"type": "page_id",
"page_id": test_run_page["id"],
}

if "title" not in kwargs:
name = f"pytest-test-database-{uuid.uuid4()}"
kwargs["title"] = [{"text": {"content": name, "link": None}}]

return notion_client.databases.create(**kwargs)

yield _create
160 changes: 160 additions & 0 deletions tests/integration/test_basic_properties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import datetime

import pytest

from notion_objects import (
Checkbox,
Database,
Date,
MultiSelect,
Number,
Page,
People,
RichTextProperty,
Select,
Text,
TitleText,
rich_text,
)

# TODO: add more properties to test

test_db_properties = {
"Name": {"title": {}},
"Description": {"rich_text": {}},
"In stock": {"checkbox": {}},
"Food group": {
"select": {
"options": [
{"name": "Vegetable", "color": "green"},
{"name": "Fruit", "color": "red"},
{"name": "Protein", "color": "yellow"},
]
}
},
"Price": {"number": {"format": "dollar"}},
"Last ordered": {"date": {}},
"Store availability": {
"type": "multi_select",
"multi_select": {
"options": [
{"name": "Duc Loi Market", "color": "blue"},
{"name": "Rainbow Grocery", "color": "gray"},
{"name": "Nijiya Market", "color": "purple"},
{"name": "Gus'''s Community Market", "color": "yellow"},
]
},
},
"+1": {"people": {}},
}


class FoodRecord(Page):
name = TitleText("Name")
description = RichTextProperty("Description")
in_stock = Checkbox("In stock")
food_group = Select("Food group")
Price = Number()
last_ordered = Date("Last ordered")
store_availability = MultiSelect("Store availability")
upvoted = People("+1")
description_plain = Text("Description")


@pytest.fixture()
def food_db(notion_client, create_database) -> Database[FoodRecord]:
database = create_database(properties=test_db_properties)

return Database(FoodRecord, database["id"], notion_client)


def test_create_and_list_records(food_db):
now = datetime.date(year=1969, month=4, day=20)
food_db.create(
FoodRecord.new(
name="My New Food Record",
description=rich_text.RichText(
"hot food record", link="https://example.com", color="red", bold=True
),
in_stock=True,
Price=123.45,
last_ordered=now,
food_group="Fruit",
store_availability=["Duc Loi Market", "Rainbow Grocery"],
)
)
food_db.create(
FoodRecord.new(
name="My empty food record",
)
)

records = list(food_db)
assert len(records) == 2

assert records[0].name == "My empty food record"
assert records[0].in_stock is False
assert records[0].food_group is None
assert records[0].store_availability == []
assert records[0].Price is None
assert records[0].last_ordered is None
assert records[0].description == []
assert records[0].description_plain == ""

assert records[1].name == "My New Food Record"
assert records[1].in_stock
assert records[1].food_group == "Fruit"
assert records[1].store_availability == ["Duc Loi Market", "Rainbow Grocery"]
assert records[1].Price == 123.45
assert records[1].last_ordered == now
assert records[1].description_plain == "hot food record"
assert records[1].description[0].to_dict() == {
"annotations": {
"bold": True,
"code": False,
"color": "red",
"italic": False,
"strikethrough": False,
"underline": False,
},
"href": "https://example.com/",
"plain_text": "hot food record",
"text": {"content": "hot food record", "link": {"url": "https://example.com/"}},
"type": "text",
}


def test_create_to_dict(food_db):
now = datetime.date(year=1969, month=4, day=20)

record = food_db.create(
FoodRecord.new(
name="My New Food Record",
description=rich_text.RichText(
"hot food record", link="https://example.com", color="red", bold=True
),
in_stock=True,
last_ordered=now,
Price=123.45,
food_group="Fruit",
store_availability=["Duc Loi Market", "Rainbow Grocery"],
)
)

doc = record.to_dict()
# remove dynamic properties we can't match directly
doc.pop("created_time")
doc.pop("last_edited_time")
doc.pop("id")

assert doc == {
"Price": 123.45,
"description": "hot food record",
"description_plain": "hot food record",
"food_group": "Fruit",
"in_stock": True,
"last_ordered": now,
"name": "My New Food Record",
"store_availability": ["Duc Loi Market", "Rainbow Grocery"],
"upvoted": [],
}
Loading

0 comments on commit 098adee

Please sign in to comment.