Allow guests for multisession_mode>1. Resolves #2098. Fix unit tests
This commit is contained in:
parent
9d7461e21e
commit
a721760da6
5 changed files with 81 additions and 36 deletions
|
|
@ -649,6 +649,51 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
logger.log_sec(f"Password successfully changed for {self}.")
|
logger.log_sec(f"Password successfully changed for {self}.")
|
||||||
self.at_password_change()
|
self.at_password_change()
|
||||||
|
|
||||||
|
def create_character(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Create a character linked to this account.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key (str, optional): If not given, use the same name as the account.
|
||||||
|
typeclass (str, optional): Typeclass to use for this character. If
|
||||||
|
not given, use settings.BASE_CHARACTER_TYPECLASS.
|
||||||
|
permissions (list, optional): If not given, use the account's permissions.
|
||||||
|
ip (str, optiona): The client IP creating this character. Will fall back to the
|
||||||
|
one stored for the account if not given.
|
||||||
|
kwargs (any): Other kwargs will be used in the create_call.
|
||||||
|
Returns:
|
||||||
|
Object: A new character of the `character_typeclass` type. None on an error.
|
||||||
|
list or None: A list of errors, or None.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# parse inputs
|
||||||
|
character_key = kwargs.pop("key", self.key)
|
||||||
|
character_ip = kwargs.pop("ip", self.db.creator_ip)
|
||||||
|
character_permissions = kwargs.pop("permissions", self.permissions)
|
||||||
|
|
||||||
|
# Load the appropriate Character class
|
||||||
|
character_typeclass = kwargs.pop("typeclass", None)
|
||||||
|
character_typeclass = character_typeclass if character_typeclass else settings.BASE_CHARACTER_TYPECLASS
|
||||||
|
Character = class_from_module(character_typeclass)
|
||||||
|
|
||||||
|
# Create the character
|
||||||
|
character, errs = Character.create(
|
||||||
|
character_key,
|
||||||
|
self,
|
||||||
|
ip=character_ip,
|
||||||
|
typeclass=character_typeclass,
|
||||||
|
permissions=character_permissions,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
if character:
|
||||||
|
# Update playable character list
|
||||||
|
if character not in self.characters:
|
||||||
|
self.db._playable_characters.append(character)
|
||||||
|
|
||||||
|
# We need to set this to have @ic auto-connect to this character
|
||||||
|
self.db._last_puppet = character
|
||||||
|
return character, errs
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, *args, **kwargs):
|
def create(cls, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -755,31 +800,11 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
|
||||||
logger.log_err(string)
|
logger.log_err(string)
|
||||||
|
|
||||||
if account and settings.MULTISESSION_MODE < 2:
|
if account and settings.MULTISESSION_MODE < 2:
|
||||||
# Load the appropriate Character class
|
# Auto-create a character to go with this account
|
||||||
character_typeclass = kwargs.get(
|
|
||||||
"character_typeclass", settings.BASE_CHARACTER_TYPECLASS
|
|
||||||
)
|
|
||||||
character_home = kwargs.get("home")
|
|
||||||
Character = class_from_module(character_typeclass)
|
|
||||||
|
|
||||||
# Create the character
|
character, errs = account.create_character(typeclass=kwargs.get("character_typeclass"))
|
||||||
character, errs = Character.create(
|
if errs:
|
||||||
account.key,
|
errors.extend(errs)
|
||||||
account,
|
|
||||||
ip=ip,
|
|
||||||
typeclass=character_typeclass,
|
|
||||||
permissions=permissions,
|
|
||||||
home=character_home,
|
|
||||||
)
|
|
||||||
errors.extend(errs)
|
|
||||||
|
|
||||||
if character:
|
|
||||||
# Update playable character list
|
|
||||||
if character not in account.characters:
|
|
||||||
account.db._playable_characters.append(character)
|
|
||||||
|
|
||||||
# We need to set this to have @ic auto-connect to this character
|
|
||||||
account.db._last_puppet = character
|
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# We are in the middle between logged in and -not, so we have
|
# We are in the middle between logged in and -not, so we have
|
||||||
|
|
@ -1540,7 +1565,7 @@ class DefaultGuest(DefaultAccount):
|
||||||
try:
|
try:
|
||||||
# Find an available guest name.
|
# Find an available guest name.
|
||||||
for name in settings.GUEST_LIST:
|
for name in settings.GUEST_LIST:
|
||||||
if not AccountDB.objects.filter(username__iexact=name).count():
|
if not AccountDB.objects.filter(username__iexact=name).exists():
|
||||||
username = name
|
username = name
|
||||||
break
|
break
|
||||||
if not username:
|
if not username:
|
||||||
|
|
@ -1566,6 +1591,15 @@ class DefaultGuest(DefaultAccount):
|
||||||
ip=ip,
|
ip=ip,
|
||||||
)
|
)
|
||||||
errors.extend(errs)
|
errors.extend(errs)
|
||||||
|
|
||||||
|
if not account.characters:
|
||||||
|
# this can happen for multisession_mode > 1. For guests we
|
||||||
|
# always auto-create a character, regardless of multi-session-mode.
|
||||||
|
character, errs = account.create_character()
|
||||||
|
|
||||||
|
if errs:
|
||||||
|
errors.extend(errs)
|
||||||
|
|
||||||
return account, errors
|
return account, errors
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import datetime
|
||||||
from anything import Anything
|
from anything import Anything
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from mock import Mock, mock
|
from unittest.mock import patch, Mock, MagicMock
|
||||||
|
|
||||||
from evennia import DefaultRoom, DefaultExit, ObjectDB
|
from evennia import DefaultRoom, DefaultExit, ObjectDB
|
||||||
from evennia.commands.default.cmdset_character import CharacterCmdSet
|
from evennia.commands.default.cmdset_character import CharacterCmdSet
|
||||||
|
|
@ -56,6 +56,7 @@ _RE = re.compile(r"^\+|-+\+|\+-+|--+|\|(?:\s|$)", re.MULTILINE)
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@patch("evennia.server.portal.portal.LoopingCall", new=MagicMock())
|
||||||
class CommandTest(EvenniaTest):
|
class CommandTest(EvenniaTest):
|
||||||
"""
|
"""
|
||||||
Tests a command
|
Tests a command
|
||||||
|
|
@ -505,7 +506,7 @@ class TestBuilding(CommandTest):
|
||||||
self.call(building.CmdSetAttribute(), "Obj2/test2", "Attribute Obj2/test2 = value2")
|
self.call(building.CmdSetAttribute(), "Obj2/test2", "Attribute Obj2/test2 = value2")
|
||||||
self.call(building.CmdSetAttribute(), "Obj2/NotFound", "Obj2 has no attribute 'notfound'.")
|
self.call(building.CmdSetAttribute(), "Obj2/NotFound", "Obj2 has no attribute 'notfound'.")
|
||||||
|
|
||||||
with mock.patch("evennia.commands.default.building.EvEditor") as mock_ed:
|
with patch("evennia.commands.default.building.EvEditor") as mock_ed:
|
||||||
self.call(building.CmdSetAttribute(), "/edit Obj2/test3")
|
self.call(building.CmdSetAttribute(), "/edit Obj2/test3")
|
||||||
mock_ed.assert_called_with(self.char1, Anything, Anything, key="Obj2/test3")
|
mock_ed.assert_called_with(self.char1, Anything, Anything, key="Obj2/test3")
|
||||||
|
|
||||||
|
|
@ -789,7 +790,7 @@ class TestBuilding(CommandTest):
|
||||||
)
|
)
|
||||||
self.call(building.CmdDesc(), "", "Usage: ")
|
self.call(building.CmdDesc(), "", "Usage: ")
|
||||||
|
|
||||||
with mock.patch("evennia.commands.default.building.EvEditor") as mock_ed:
|
with patch("evennia.commands.default.building.EvEditor") as mock_ed:
|
||||||
self.call(building.CmdDesc(), "/edit")
|
self.call(building.CmdDesc(), "/edit")
|
||||||
mock_ed.assert_called_with(
|
mock_ed.assert_called_with(
|
||||||
self.char1,
|
self.char1,
|
||||||
|
|
@ -1004,9 +1005,9 @@ class TestBuilding(CommandTest):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
with mock.patch(
|
with patch(
|
||||||
"evennia.commands.default.building.protlib.search_prototype",
|
"evennia.commands.default.building.protlib.search_prototype",
|
||||||
new=mock.MagicMock(return_value=test_prototype),
|
new=MagicMock(return_value=test_prototype),
|
||||||
) as mprot:
|
) as mprot:
|
||||||
self.call(
|
self.call(
|
||||||
building.CmdTypeclass(),
|
building.CmdTypeclass(),
|
||||||
|
|
@ -1072,7 +1073,7 @@ class TestBuilding(CommandTest):
|
||||||
self.call(building.CmdFind(), "/exact Obj", "One Match")
|
self.call(building.CmdFind(), "/exact Obj", "One Match")
|
||||||
|
|
||||||
# Test multitype filtering
|
# Test multitype filtering
|
||||||
with mock.patch(
|
with patch(
|
||||||
"evennia.commands.default.building.CHAR_TYPECLASS",
|
"evennia.commands.default.building.CHAR_TYPECLASS",
|
||||||
"evennia.objects.objects.DefaultCharacter",
|
"evennia.objects.objects.DefaultCharacter",
|
||||||
):
|
):
|
||||||
|
|
@ -1540,11 +1541,11 @@ class TestSystemCommands(CommandTest):
|
||||||
|
|
||||||
self.call(multimatch, "look", "")
|
self.call(multimatch, "look", "")
|
||||||
|
|
||||||
@mock.patch("evennia.commands.default.syscommands.ChannelDB")
|
@patch("evennia.commands.default.syscommands.ChannelDB")
|
||||||
def test_channelcommand(self, mock_channeldb):
|
def test_channelcommand(self, mock_channeldb):
|
||||||
channel = mock.MagicMock()
|
channel = MagicMock()
|
||||||
channel.msg = mock.MagicMock()
|
channel.msg = MagicMock()
|
||||||
mock_channeldb.objects.get_channel = mock.MagicMock(return_value=channel)
|
mock_channeldb.objects.get_channel = MagicMock(return_value=channel)
|
||||||
|
|
||||||
self.call(syscommands.SystemSendToChannel(), "public:Hello")
|
self.call(syscommands.SystemSendToChannel(), "public:Hello")
|
||||||
channel.msg.assert_called()
|
channel.msg.assert_called()
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ class Portal(object):
|
||||||
self.server_info_dict = {}
|
self.server_info_dict = {}
|
||||||
|
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
|
|
||||||
self.maintenance_task = LoopingCall(_portal_maintenance)
|
self.maintenance_task = LoopingCall(_portal_maintenance)
|
||||||
self.maintenance_task.start(60, now=True) # call every minute
|
self.maintenance_task.start(60, now=True) # call every minute
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ Test AMP client
|
||||||
import pickle
|
import pickle
|
||||||
from model_mommy import mommy
|
from model_mommy import mommy
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
from twisted.trial.unittest import TestCase as TwistedTestCase
|
from twisted.trial.unittest import TestCase as TwistedTestCase
|
||||||
from evennia.server import amp_client
|
from evennia.server import amp_client
|
||||||
from evennia.server.portal import amp_server
|
from evennia.server.portal import amp_server
|
||||||
|
|
@ -36,6 +36,7 @@ class _TestAMP(TwistedTestCase):
|
||||||
self.server.sessions[1] = self.session
|
self.server.sessions[1] = self.session
|
||||||
|
|
||||||
self.portal = portal.Portal(MagicMock())
|
self.portal = portal.Portal(MagicMock())
|
||||||
|
self.portal.maintenance_task.stop()
|
||||||
self.portalsession = session.Session()
|
self.portalsession = session.Session()
|
||||||
self.portalsession.sessid = 1
|
self.portalsession.sessid = 1
|
||||||
self.portal.sessions[1] = self.portalsession
|
self.portal.sessions[1] = self.portalsession
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ Runs as part of the Evennia's test suite with 'evennia test evennia"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from django.test.runner import DiscoverRunner
|
from django.test.runner import DiscoverRunner
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
|
||||||
class EvenniaTestSuiteRunner(DiscoverRunner):
|
class EvenniaTestSuiteRunner(DiscoverRunner):
|
||||||
|
|
@ -21,9 +22,16 @@ class EvenniaTestSuiteRunner(DiscoverRunner):
|
||||||
Build a test suite for Evennia. test_labels is a list of apps to test.
|
Build a test suite for Evennia. test_labels is a list of apps to test.
|
||||||
If not given, a subset of settings.INSTALLED_APPS will be used.
|
If not given, a subset of settings.INSTALLED_APPS will be used.
|
||||||
"""
|
"""
|
||||||
|
# the portal looping call starts before the unit-test suite so we
|
||||||
|
# can't mock it - instead we stop it before starting the test - otherwise
|
||||||
|
# we'd get unclean reactor errors across test boundaries.
|
||||||
|
from evennia.server.portal.portal import PORTAL
|
||||||
|
PORTAL.maintenance_task.stop()
|
||||||
|
|
||||||
import evennia
|
import evennia
|
||||||
|
|
||||||
evennia._init()
|
evennia._init()
|
||||||
return super(EvenniaTestSuiteRunner, self).build_suite(
|
return super(EvenniaTestSuiteRunner, self).build_suite(
|
||||||
test_labels, extra_tests=extra_tests, **kwargs
|
test_labels, extra_tests=extra_tests, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue