part, quit, kick are all going to have their own need to log out the character without a penalty (because they apply their own), so to avoid double penalties, the log out penalty should be moved into the bot command and managed that way. this was the only place where an action method was also applying a penalty, so hopefully this remains consistent too Signed-off-by: Brian S. Stephan <bss@incorporeal.org>
179 lines
7.1 KiB
Python
179 lines
7.1 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)
|
|
|
|
logout_time = timezone.now()
|
|
with patch('django.utils.timezone.now', return_value=logout_time):
|
|
char.log_out()
|
|
|
|
assert char.status == Character.CHARACTER_STATUS_OFFLINE
|
|
assert char.last_login == logout_time
|
|
|
|
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()
|