Add very basic Share handling
This commit is contained in:
parent
b585814ab8
commit
d602d9c3ba
61
earthsnake/share.py
Normal file
61
earthsnake/share.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
"""Share handling"""
|
||||||
|
|
||||||
|
from random import choice
|
||||||
|
import re
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from .exc import ValidationError
|
||||||
|
from .types import ALPHA_LOWER, ALPHA_LOWER_OR_DIGIT
|
||||||
|
|
||||||
|
|
||||||
|
class Share:
|
||||||
|
"""Class to handle a share (space or workspace in older terminology)"""
|
||||||
|
|
||||||
|
_NAME_PATTERN = f'[{ALPHA_LOWER}][{ALPHA_LOWER_OR_DIGIT}]{{0,14}}'
|
||||||
|
_SUFFIX_PATTERN = f'[{ALPHA_LOWER}][{ALPHA_LOWER_OR_DIGIT}]{{0,52}}'
|
||||||
|
PATTERN = f'^[+]{_NAME_PATTERN}[.]{_SUFFIX_PATTERN}$'
|
||||||
|
|
||||||
|
def __init__(self, name: str, suffix: Optional[str] = None) -> None:
|
||||||
|
suffix = suffix or self.generate_random_suffix()
|
||||||
|
|
||||||
|
if not re.match(f'^{self._NAME_PATTERN}$', name):
|
||||||
|
raise ValidationError(f'Invalid name "{name}"')
|
||||||
|
|
||||||
|
if not re.match(f'^{self._SUFFIX_PATTERN}$', suffix):
|
||||||
|
raise ValidationError(f'Invalid suffix "{suffix}"')
|
||||||
|
|
||||||
|
self.name = name
|
||||||
|
self.suffix = suffix
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_address(cls, share_address: str) -> None:
|
||||||
|
"""Check if share_address is a valid share address"""
|
||||||
|
|
||||||
|
if not re.match(cls.PATTERN, share_address):
|
||||||
|
raise ValidationError(f'Invalid share address {share_address}')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_address(cls, share_address: str) -> 'Share':
|
||||||
|
"""Create a Share object from a share address"""
|
||||||
|
|
||||||
|
cls.validate_address(share_address)
|
||||||
|
|
||||||
|
name, suffix = share_address[1:].split('.')
|
||||||
|
|
||||||
|
return cls(name, suffix)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate_random_suffix(length: int = 53) -> str:
|
||||||
|
"""Generate a random share suffix of the desired length"""
|
||||||
|
|
||||||
|
assert 54 > length > 0
|
||||||
|
|
||||||
|
return choice(ALPHA_LOWER) + ''.join(
|
||||||
|
choice(ALPHA_LOWER_OR_DIGIT) for _ in range(length - 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f'+{self.name}.{self.suffix}'
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'<Share {self}>'
|
113
tests/test_share.py
Normal file
113
tests/test_share.py
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
"""Tests for the Share class"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from earthsnake.types import ALPHA_LOWER, ALPHA_LOWER_OR_DIGIT
|
||||||
|
from earthsnake.exc import ValidationError
|
||||||
|
from earthsnake.share import Share
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'name,suffix,error',
|
||||||
|
[
|
||||||
|
pytest.param('80smusic', 'suffix', 'Invalid name "80smusic"', id='name_digitstart'),
|
||||||
|
pytest.param('a.b', 'suffix', 'Invalid name "a.b"', id='name_period'),
|
||||||
|
pytest.param('PARTY', 'suffix', 'Invalid name "PARTY"', id='name_uppercase'),
|
||||||
|
pytest.param('test', 'b.c', 'Invalid suffix "b.c"', id='suffix_period'),
|
||||||
|
pytest.param('test', '4ever', 'Invalid suffix "4ever"', id='suffix_digitstart'),
|
||||||
|
pytest.param('test', 'TIME', 'Invalid suffix "TIME"', id='suffix_uppercase'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_create_invalid(name: str, suffix: str, error: str) -> None:
|
||||||
|
"""Test if share creation fails with invalid values"""
|
||||||
|
|
||||||
|
with pytest.raises(ValidationError) as ctx:
|
||||||
|
Share(name, suffix=suffix)
|
||||||
|
|
||||||
|
assert str(ctx.value) == error
|
||||||
|
|
||||||
|
|
||||||
|
def test_create() -> None:
|
||||||
|
"""Test if creating a share with a name and a suffix succeeds"""
|
||||||
|
|
||||||
|
share = Share('test', suffix='suffix')
|
||||||
|
|
||||||
|
assert share.name == 'test'
|
||||||
|
assert share.suffix == 'suffix'
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_no_suffix() -> None:
|
||||||
|
"""Test if creating succeeds if no suffix is given, and the suffix is randomly chosen"""
|
||||||
|
|
||||||
|
share = Share('test')
|
||||||
|
|
||||||
|
assert share.name == 'test'
|
||||||
|
assert len(share.suffix) == 53
|
||||||
|
|
||||||
|
|
||||||
|
def test_str() -> None:
|
||||||
|
"""Test the __str__ method"""
|
||||||
|
|
||||||
|
share = Share('test', 'suffix')
|
||||||
|
|
||||||
|
assert str(share) == '+test.suffix'
|
||||||
|
|
||||||
|
|
||||||
|
def test_repr() -> None:
|
||||||
|
"""Test the __repr__ method"""
|
||||||
|
|
||||||
|
share = Share('test', 'suffix')
|
||||||
|
|
||||||
|
assert repr(share) == '<Share +test.suffix>'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'address',
|
||||||
|
(
|
||||||
|
pytest.param('test.suffix', id='no_prefix'),
|
||||||
|
pytest.param('+t.st.suffix', id='invalid_name_char'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_validate_invalid(address: str) -> None:
|
||||||
|
"""Test if validate_address fails if the address is invalid"""
|
||||||
|
|
||||||
|
with pytest.raises(ValidationError) as ctx:
|
||||||
|
Share.validate_address(address)
|
||||||
|
|
||||||
|
assert str(ctx.value) == f'Invalid share address {address}'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'address',
|
||||||
|
(
|
||||||
|
pytest.param('test.suffix', id='no_prefix'),
|
||||||
|
pytest.param('+t.st.suffix', id='invalid_name_char'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_from_address_invalid(address: str) -> None:
|
||||||
|
"""Test if from_address fails if the address is invalid"""
|
||||||
|
|
||||||
|
with pytest.raises(ValidationError) as ctx:
|
||||||
|
Share.from_address(address)
|
||||||
|
|
||||||
|
assert str(ctx.value) == f'Invalid share address {address}'
|
||||||
|
|
||||||
|
|
||||||
|
def test_from_address() -> None:
|
||||||
|
"""Test constructing a share from a string address"""
|
||||||
|
|
||||||
|
share = Share.from_address('+test.suffix')
|
||||||
|
|
||||||
|
assert share.name == 'test'
|
||||||
|
assert share.suffix == 'suffix'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('length', range(1, 54))
|
||||||
|
def test_generate_suffix(length: int) -> None:
|
||||||
|
"""Test the random suffix generator"""
|
||||||
|
|
||||||
|
suffix = Share.generate_random_suffix(length=length)
|
||||||
|
|
||||||
|
assert suffix[0] in ALPHA_LOWER
|
||||||
|
assert all(char in ALPHA_LOWER_OR_DIGIT for char in suffix)
|
||||||
|
assert len(suffix) == length
|
Loading…
Reference in New Issue
Block a user