Add article backend #5

Merged
Jaculabilis merged 2 commits from tvb/article-backend into develop 2021-06-02 02:02:50 +00:00
5 changed files with 165 additions and 9 deletions

View File

@ -0,0 +1,62 @@
"""
Article query interface
"""
from sqlalchemy import select
from amanuensis.db import *
from amanuensis.errors import ArgumentError
def create(
db: DbContext,
lexicon_id: int,
user_id: int,
character_id: int) -> Article:
"""
Create a new article in a lexicon.
"""
# Verify argument types are correct
if not isinstance(lexicon_id, int):
raise ArgumentError('lexicon_id')
if not isinstance(user_id, int):
raise ArgumentError('user_id')
if character_id is not None and not isinstance(character_id, int):
raise ArgumentError('character_id')
# Check that the user is a member of this lexicon
mem: Membership = db(
select(Membership)
.where(Membership.user_id == user_id)
.where(Membership.lexicon_id == lexicon_id)
).scalar_one_or_none()
if not mem:
raise ArgumentError('User is not a member of lexicon')
# If the character id is provided, check that the user owns the character
# and the character belongs to the lexicon
if character_id is not None:
character: Character = db(
select(Character)
.where(Character.id == character_id)
).scalar_one_or_none()
if not character:
raise ArgumentError('Character does not exist')
if character.user.id != user_id:
raise ArgumentError('Character is owned by the wrong player')
if character.lexicon.id != lexicon_id:
raise ArgumentError('Character belongs to the wrong lexicon')
signature = character.signature
else:
signature = '~Ersatz Scrivener'
new_article = Article(
lexicon_id=lexicon_id,
user_id=user_id,
character_id=character_id,
title='Article title',
body=f'\n\n{signature}',
)
db.session.add(new_article)
db.session.commit()
return new_article

View File

@ -4,6 +4,7 @@ pytest test fixtures
import pytest
from amanuensis.db import DbContext
import amanuensis.backend.character as charq
import amanuensis.backend.lexicon as lexiq
import amanuensis.backend.membership as memq
import amanuensis.backend.user as userq
@ -62,12 +63,52 @@ def make_membership(db: DbContext):
@pytest.fixture
def lexicon_with_editor(make_user, make_lexicon, make_membership):
def make_character(db: DbContext):
"""Provides a factory function for creating characters, with valid default values."""
def character_factory(state={'nonce': 1}, **kwargs):
default_kwargs = {
'name': f'Character {state["nonce"]}',
'signature': None,
}
state['nonce'] += 1
updated_kwargs = {**default_kwargs, **kwargs}
return charq.create(db, **updated_kwargs)
return character_factory
class TestFactory:
def __init__(self, db, **factories):
self.db = db
self.factories = factories
def __getattr__(self, name):
return self.factories[name]
@pytest.fixture
def make(
db: DbContext,
make_user,
make_lexicon,
make_membership,
make_character) -> TestFactory:
"""Fixture that groups all factory fixtures together."""
return TestFactory(
db,
user=make_user,
lexicon=make_lexicon,
membership=make_membership,
character=make_character,
)
@pytest.fixture
def lexicon_with_editor(make):
"""Shortcut setup for a lexicon game with an editor."""
editor = make_user()
editor = make.user()
assert editor
lexicon = make_lexicon()
lexicon = make.lexicon()
assert lexicon
membership = make_membership(user_id=editor.id, lexicon_id=lexicon.id, is_editor=True)
membership = make.membership(user_id=editor.id, lexicon_id=lexicon.id, is_editor=True)
assert membership
return (lexicon, editor)

53
tests/test_article.py Normal file
View File

@ -0,0 +1,53 @@
import pytest
from amanuensis.db import DbContext
import amanuensis.backend.article as artiq
from amanuensis.errors import ArgumentError
def test_create_article(db: DbContext, make):
"""Test new article creation"""
# Create two users in a shared lexicon
user1 = make.user()
user2 = make.user()
lexicon1 = make.lexicon()
make.membership(user_id=user1.id, lexicon_id=lexicon1.id)
make.membership(user_id=user2.id, lexicon_id=lexicon1.id)
char1_1 = make.character(lexicon_id=lexicon1.id, user_id=user1.id)
char1_2 = make.character(lexicon_id=lexicon1.id, user_id=user2.id)
# Create a lexicon that only one user is in
lexicon2 = make.lexicon()
make.membership(user_id=user2.id, lexicon_id=lexicon2.id)
char2_2 = make.character(lexicon_id=lexicon2.id, user_id=user2.id)
# User cannot create article for another user's character
with pytest.raises(ArgumentError):
artiq.create(db, lexicon1.id, user1.id, char1_2.id)
with pytest.raises(ArgumentError):
artiq.create(db, lexicon1.id, user2.id, char1_1.id)
# User cannot create article for their character in the wrong lexicon
with pytest.raises(ArgumentError):
artiq.create(db, lexicon1.id, user2.id, char2_2.id)
with pytest.raises(ArgumentError):
artiq.create(db, lexicon2.id, user2.id, char1_2.id)
# User cannot create article in a lexicon they aren't in
with pytest.raises(ArgumentError):
artiq.create(db, lexicon2.id, user1.id, char1_1.id)
# User cannot create anonymous articles in a lexicon they aren't in
with pytest.raises(ArgumentError):
artiq.create(db, lexicon2.id, user1.id, character_id=None)
# Users can create character-owned articles
assert artiq.create(db, lexicon1.id, user1.id, char1_1.id)
assert artiq.create(db, lexicon1.id, user2.id, char1_2.id)
assert artiq.create(db, lexicon2.id, user2.id, char2_2.id)
# Users can create anonymous articles
assert artiq.create(db, lexicon1.id, user1.id, character_id=None)
assert artiq.create(db, lexicon1.id, user2.id, character_id=None)
assert artiq.create(db, lexicon2.id, user2.id, character_id=None)

View File

@ -5,7 +5,7 @@ import amanuensis.backend.character as charq
from amanuensis.errors import ArgumentError
def test_create_character(db: DbContext, lexicon_with_editor, make_user):
def test_create_character(db: DbContext, lexicon_with_editor, make):
"""Test creating a character."""
lexicon, user = lexicon_with_editor
kwargs = {
@ -33,7 +33,7 @@ def test_create_character(db: DbContext, lexicon_with_editor, make_user):
assert char.signature is not None
# User must be in lexicon
new_user = make_user()
new_user = make.user()
with pytest.raises(ArgumentError):
charq.create(**{**kwargs, 'user_id': new_user.id})

View File

@ -7,12 +7,12 @@ from amanuensis.errors import ArgumentError
import amanuensis.backend.membership as memq
def test_create_membership(db: DbContext, make_user, make_lexicon):
def test_create_membership(db: DbContext, make):
"""Test joining a game."""
# Set up a user and a lexicon
new_user = make_user()
new_user = make.user()
assert new_user.id, 'Failed to create user'
new_lexicon = make_lexicon()
new_lexicon = make.lexicon()
assert new_lexicon.id, 'Failed to create lexicon'
# Add the user to the lexicon as an editor