this provides character-level operations such as character creation, logging in/out, leveling them, and penalizing them. this isn't a game, yet, but it does implement and test a lot of the core functionality, just without triggers or bulk operations Signed-off-by: Brian S. Stephan <bss@incorporeal.org>
183 lines
7.3 KiB
Python
183 lines
7.3 KiB
Python
"""Test IdleRPG character operations.
|
|
|
|
SPDX-FileCopyrightText: © 2024 Brian S. Stephan <bss@incorporeal.org>
|
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
"""
|
|
import logging
|
|
from datetime import timedelta
|
|
from unittest.mock import patch
|
|
|
|
from django.test import TestCase
|
|
from django.utils import timezone
|
|
|
|
from idlerpg.models import Character, Game
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class CharacterTest(TestCase):
|
|
"""Test the Character model."""
|
|
|
|
fixtures = ['tests/fixtures/simple_character.json']
|
|
|
|
def test_string_repr(self):
|
|
"""Test the basic string summary."""
|
|
char = Character.objects.get(pk=1)
|
|
logger.debug(str(char))
|
|
assert str(char) == "bss, level 0 tester"
|
|
|
|
def test_log_out(self):
|
|
"""Test basic log out functionality and end result."""
|
|
char = Character.objects.get(pk=1)
|
|
# retain some data for comparison after logging out
|
|
old_next_level = char.next_level
|
|
|
|
logout_time = timezone.now()
|
|
with patch('django.utils.timezone.now', return_value=logout_time):
|
|
char.log_out()
|
|
|
|
assert char.next_level == old_next_level + timedelta(seconds=20)
|
|
assert char.status == Character.CHARACTER_STATUS_OFFLINE
|
|
assert char.last_login == logout_time
|
|
assert char.time_penalized_logout == 20
|
|
|
|
def test_cant_log_out_offline(self):
|
|
"""Test that we error if trying to log out a character already offline."""
|
|
char = Character.objects.get(pk=1)
|
|
char.log_out()
|
|
with self.assertRaises(ValueError):
|
|
char.log_out()
|
|
|
|
def test_log_in(self):
|
|
"""Test the result of logging in."""
|
|
char = Character.objects.get(pk=1)
|
|
|
|
logout_time = timezone.now()
|
|
login_time = logout_time + timedelta(seconds=300)
|
|
# manipulate the time so that we log out and then log in 300 seconds later, which
|
|
# adds 20 seconds to the next level time
|
|
with patch('django.utils.timezone.now', return_value=logout_time):
|
|
char.log_out()
|
|
# logout has a penalty of its own, so this post-logout value is what will be altered
|
|
old_next_level = char.next_level
|
|
with patch('django.utils.timezone.now', return_value=login_time):
|
|
char.log_in('bss', 'bss!bss@test_log_in')
|
|
|
|
assert char.next_level == old_next_level + timedelta(seconds=300)
|
|
assert char.status == Character.CHARACTER_STATUS_ONLINE
|
|
assert char.hostmask == 'bss!bss@test_log_in'
|
|
|
|
def test_cant_log_in_when_already_online(self):
|
|
"""Test that we can't log in the character if they're already online."""
|
|
char = Character.objects.get(pk=1)
|
|
with self.assertRaises(ValueError):
|
|
char.log_in('bss', 'bss!bss@test_online')
|
|
|
|
def test_cant_log_in_bad_password(self):
|
|
"""Test that we can't log in the character if we don't have the right password."""
|
|
char = Character.objects.get(pk=1)
|
|
char.log_out()
|
|
with self.assertRaises(ValueError):
|
|
char.log_in('bad pass', 'bss!bss@test_bad_password')
|
|
|
|
def test_set_password(self):
|
|
"""Test that the password is actually changed when requested."""
|
|
char = Character.objects.get(pk=1)
|
|
old_password = char.password
|
|
char.set_password('something')
|
|
assert old_password != char.password
|
|
assert char.password[0:13] == 'pbkdf2_sha256'
|
|
|
|
def test_penalize(self):
|
|
"""Test that penalties apply properly, including level adjustments."""
|
|
char = Character.objects.get(pk=1)
|
|
next_level = char.next_level
|
|
|
|
# level 0
|
|
next_level += timedelta(seconds=20)
|
|
char.penalize(20, 'test')
|
|
assert char.next_level == next_level
|
|
# level 1
|
|
char.level = 1
|
|
next_level += timedelta(seconds=23)
|
|
char.penalize(20, 'test')
|
|
assert char.next_level == next_level
|
|
# level 5
|
|
char.level = 5
|
|
next_level += timedelta(seconds=39)
|
|
char.penalize(20, 'test')
|
|
assert char.next_level == next_level
|
|
# level 25
|
|
char.level = 25
|
|
next_level += timedelta(seconds=530)
|
|
char.penalize(20, 'test')
|
|
assert char.next_level == next_level
|
|
|
|
def test_calculate_datetime_to_next_level(self):
|
|
"""Test that the next level calculation behaves as expected in a number of situations."""
|
|
char = Character.objects.get(pk=1)
|
|
|
|
# level 0 -> 1
|
|
assert char.calculate_datetime_to_next_level() == char.next_level + timedelta(seconds=600)
|
|
# level 1 -> 2
|
|
char.level = 1
|
|
assert char.calculate_datetime_to_next_level() == char.next_level + timedelta(seconds=696)
|
|
# level 24 -> 25
|
|
char.level = 24
|
|
assert char.calculate_datetime_to_next_level() == char.next_level + timedelta(seconds=21142)
|
|
# level 59 -> 60
|
|
char.level = 59
|
|
assert char.calculate_datetime_to_next_level() == char.next_level + timedelta(seconds=3812174)
|
|
# level 60 -> 61
|
|
char.level = 60
|
|
assert char.calculate_datetime_to_next_level() == char.next_level + (timedelta(seconds=3812174) +
|
|
timedelta(seconds=86400))
|
|
# level 61 -> 62
|
|
char.level = 61
|
|
assert char.calculate_datetime_to_next_level() == char.next_level + (timedelta(seconds=3812174) +
|
|
timedelta(seconds=86400*2))
|
|
|
|
def test_calculate_datetime_to_next_level_not_time_yet(self):
|
|
"""Test that we just bail with the current next_level if it hasn't been reached yet."""
|
|
char = Character.objects.get(pk=1)
|
|
with patch('django.utils.timezone.now', return_value=char.next_level - timedelta(days=1)):
|
|
assert char.calculate_datetime_to_next_level() == char.next_level
|
|
|
|
def test_register(self):
|
|
"""Test the primary way to create a character with proper initial values."""
|
|
game = Game.objects.get(pk=1)
|
|
|
|
register_time = timezone.now()
|
|
with patch('django.utils.timezone.now', return_value=register_time):
|
|
new_char = Character.objects.register('new', game, 'pass', 'bss!bss@test_register', 'unit tester')
|
|
|
|
assert new_char.status == Character.CHARACTER_STATUS_ONLINE
|
|
assert new_char.next_level == register_time + timedelta(seconds=600)
|
|
assert new_char.last_login == register_time
|
|
assert new_char.password[0:13] == 'pbkdf2_sha256'
|
|
|
|
def test_level_up(self):
|
|
"""Test the level up functionality."""
|
|
char = Character.objects.get(pk=1)
|
|
|
|
old_level = char.level
|
|
old_next_level = char.next_level
|
|
char.level_up()
|
|
|
|
assert char.level == old_level + 1
|
|
assert char.next_level == old_next_level + timedelta(seconds=600)
|
|
|
|
def test_level_up_fail_not_time(self):
|
|
"""Test the failure condition for trying to level up before the timestamp."""
|
|
char = Character.objects.get(pk=1)
|
|
with patch('django.utils.timezone.now', return_value=char.next_level-timedelta(minutes=30)):
|
|
with self.assertRaises(ValueError):
|
|
char.level_up()
|
|
|
|
def test_level_up_fail_offline(self):
|
|
"""Test the failure condition for trying to level up a character not online."""
|
|
char = Character.objects.get(pk=1)
|
|
char.log_out()
|
|
with self.assertRaises(ValueError):
|
|
char.level_up()
|