grung-db/test/test_db.py

237 lines
7.2 KiB
Python
Raw Normal View History

2025-10-18 14:23:16 -07:00
import tempfile
2025-10-04 15:22:07 -07:00
from datetime import datetime
2025-10-18 14:23:16 -07:00
from pathlib import Path
2025-09-29 23:10:33 -07:00
from pprint import pprint as print
2025-10-04 15:22:07 -07:00
from time import sleep
2025-09-29 23:10:33 -07:00
2025-09-24 19:51:39 -07:00
import pytest
2025-09-28 11:11:34 -07:00
from tinydb import Query
2025-09-24 19:51:39 -07:00
from tinydb.storages import MemoryStorage
from grung import examples
2025-09-24 19:51:39 -07:00
from grung.db import GrungDB
2025-11-01 22:25:15 -07:00
from grung.exceptions import (
CircularReferenceError,
InvalidFieldTypeError,
InvalidLengthError,
InvalidSizeError,
PatternMatchError,
UniqueConstraintError,
)
2025-09-24 19:51:39 -07:00
@pytest.fixture
def db():
2025-10-18 14:23:16 -07:00
with tempfile.TemporaryDirectory() as path:
_db = GrungDB.with_schema(examples, path=Path(path), storage=MemoryStorage)
yield _db
print(_db)
2025-09-24 19:51:39 -07:00
def test_crud(db):
2025-09-27 15:13:17 -07:00
user = examples.User(name="john", number=23, email="john@foo")
2025-10-08 00:25:02 -07:00
assert user._metadata.fields[user._metadata.primary_key].unique
assert user._metadata.fields[user._metadata.primary_key].primary_key
2025-09-24 19:51:39 -07:00
# insert
john_something = db.save(user)
last_insert_id = john_something.doc_id
# read back
assert db.User.get(doc_id=last_insert_id) == john_something
assert john_something.name == user.name
2025-09-27 15:13:17 -07:00
assert john_something.number == 23
2025-09-24 19:51:39 -07:00
assert john_something.email == user.email
# update
john_something.name = "james?"
before_update = db.User.get(doc_id=john_something.doc_id)
after_update = db.save(john_something)
assert after_update == john_something
assert before_update != after_update
2025-09-27 15:13:17 -07:00
# delete
db.delete(john_something)
assert len(db.User) == 0
2025-09-24 19:51:39 -07:00
2025-09-27 15:13:17 -07:00
def test_pointers(db):
2025-10-30 19:56:56 -07:00
user = db.save(examples.User(name="john", email="john@foo"))
2025-09-29 23:10:33 -07:00
players = db.save(examples.Group(name="players", members=[user]))
2025-09-29 23:21:33 -07:00
user = db.table("User").get(doc_id=user.doc_id)
2025-10-08 00:25:02 -07:00
assert user.groups.name == players.name
2025-09-27 15:13:17 -07:00
2025-10-08 00:25:02 -07:00
assert players.members[0].groups.name == players.name
def test_subgroups(db):
kirk = db.save(examples.User(name="James T. Kirk", email="riskybiznez@starfleet"))
pike = db.save(examples.User(name="Christopher Pike", email="hitit@starfleet"))
tos = db.save(examples.Group(name="The Original Series", members=[kirk]))
snw = db.save(examples.Group(name="Strange New Worlds", members=[pike]))
2025-09-29 23:50:23 -07:00
trek = db.save(examples.Group(name="trek", groups=[tos, snw]))
2025-09-29 23:40:25 -07:00
tos = db.table("Group").get(doc_id=tos.doc_id)
snw = db.table("Group").get(doc_id=snw.doc_id)
2025-09-29 23:50:23 -07:00
assert tos in trek.groups
assert snw in trek.groups
2025-09-29 23:10:33 -07:00
2025-09-29 23:50:23 -07:00
assert trek.parent is None
2025-10-08 00:25:02 -07:00
assert tos.parent.name == trek.name
assert snw.parent.name == trek.name
2025-09-29 23:40:25 -07:00
2025-09-29 23:50:23 -07:00
unique_users = set([user for group in trek.groups for user in group.members])
2025-09-29 23:10:33 -07:00
2025-09-29 23:21:33 -07:00
kirk = db.table("User").get(doc_id=kirk.doc_id)
2025-09-30 00:52:37 -07:00
assert kirk.reference in unique_users
2025-09-27 12:09:43 -07:00
2025-10-30 19:56:56 -07:00
# recursion!
with pytest.raises(CircularReferenceError):
2025-11-01 22:25:15 -07:00
tos.groups = [tos]
2025-10-30 19:56:56 -07:00
db.save(tos)
2025-09-27 12:09:43 -07:00
def test_unique(db):
user1 = examples.User(name="john", email="john@foo")
user2 = examples.User(name="john", email="john@foo")
user1 = db.save(user1)
with pytest.raises(UniqueConstraintError):
user2 = db.save(user2)
db.save(user1)
2025-09-28 11:11:34 -07:00
def test_search(db):
2025-09-29 23:10:33 -07:00
# create crew members
2025-09-28 11:11:34 -07:00
kirk = db.save(examples.User(name="Captain James T. Kirk", email="riskybiznez@starfleet"))
bones = db.save(examples.User(name="Doctor McCoy", email="dammitjim@starfleet"))
ricky = db.save(examples.User(name="Ensign Ricky Redshirt", email="invincible@starfleet"))
2025-09-29 23:10:33 -07:00
# create the crew record
2025-09-28 11:11:34 -07:00
crew = db.save(examples.Group(name="Crew", members=[kirk, bones, ricky]))
User = Query()
captains = db.User.search(User.name.matches("Captain"))
assert len(captains) == 1
2025-09-29 23:10:33 -07:00
# update the crew members so they have the backreference to crew
2025-09-29 23:21:33 -07:00
kirk = db.table("User").get(doc_id=kirk.doc_id)
bones = db.table("User").get(doc_id=bones.doc_id)
ricky = db.table("User").get(doc_id=ricky.doc_id)
2025-09-29 23:10:33 -07:00
2025-09-28 11:11:34 -07:00
assert kirk in crew.members
assert bones in crew.members
assert ricky in crew.members
2025-09-28 12:12:05 -07:00
Group = Query()
2025-10-04 13:36:29 -07:00
crew = db.Group.get(Group.name == "Crew", recurse=False)
assert kirk.reference in crew.members
2025-10-04 12:01:59 -07:00
def test_password(db):
user = db.save(examples.User(name="john", email="john@foo", password="fnord"))
2025-10-04 13:27:38 -07:00
# make sure we don't compute the digest on an existing digest
user = db.save(user)
2025-10-04 12:01:59 -07:00
assert ":" in user.password
assert user.password != "fnord"
check = user._metadata.fields["password"].compare
assert check("fnord", user.password)
assert not check("wrong password", user.password)
assert not check("", user.password)
2025-10-04 15:22:07 -07:00
def test_datetime(db):
user = db.save(examples.User(name="john", email="john@foo", password="fnord", created=datetime.utcnow()))
assert user.created > datetime.utcfromtimestamp(0)
assert user.created < datetime.utcnow()
assert user.last_updated == user.created
sleep(1)
user = db.save(user)
assert user.last_updated >= user.created
def test_mapping(db):
album = db.save(
examples.Album(
name="The Impossible Kid",
credits={"Produced By": "Aesop Rock", "Lyrics By": "Aesop Rock", "Puke in the MeowMix By": "Kirby"},
tracks=["Mystery Fish", "Rings", "Lotta Years", "Dorks"],
)
)
assert album.credits["Produced By"] == "Aesop Rock"
assert album.tracks[0] == "Mystery Fish"
aes = db.save(
examples.Artist(
name="Aesop Rock",
albums={"The Impossible Kid": album},
)
)
album = db.Album.get(doc_id=album.doc_id)
assert album.artist.uid == aes.uid
assert album.name in aes.albums
assert aes.albums[album.name].uid == album.uid
assert "Kirby" in aes.albums[album.name].credits.values()
2025-10-18 14:23:16 -07:00
def test_file_pointers(db):
album = db.save(
examples.Album(
name="The Impossible Kid",
credits={"Produced By": "Aesop Rock", "Lyrics By": "Aesop Rock", "Puke in the MeowMix By": "Kirby"},
tracks=["Mystery Fish", "Rings", "Lotta Years", "Dorks"],
cover=b"some jpg data",
review="10/10 no notes",
)
)
assert album.cover == b"some jpg data"
assert album.review == "10/10 no notes"
db.save(
examples.Artist(
name="Aesop Rock",
albums={"The Impossible Kid": album},
)
)
album = db.Album.get(doc_id=album.doc_id)
2025-10-18 14:23:16 -07:00
location_on_disk = db.path / album._metadata.fields["cover"].relpath(album)
assert location_on_disk.read_bytes() == album.cover
location_on_disk = db.path / album._metadata.fields["review"].relpath(album)
assert location_on_disk.read_text() == album.review
2025-11-01 22:25:15 -07:00
@pytest.mark.parametrize(
"updates, expected",
[
({"name": ""}, InvalidLengthError),
({"name": "a name longer than 30 characters is what we have here"}, InvalidLengthError),
({"name": 23}, InvalidFieldTypeError),
({"number": -1}, InvalidSizeError),
({"number": 256}, InvalidSizeError),
({"email": "foo+alias@"}, PatternMatchError),
],
ids=[
"name too short",
"name too long",
"name is not a string",
"number too small",
"number too big",
"invalid email addres",
],
)
def test_validators(updates, expected, db):
user = db.save(examples.User(name="john", email="john@foo", password="fnord", created=datetime.utcnow()))
with pytest.raises(expected):
user.update(**updates)
db.save(user)