# -*- coding: utf-8 -*- from mock import Mock, MagicMock, patch from random import randint from unittest import TestCase from django.test import override_settings from evennia.accounts.accounts import AccountSessionHandler from evennia.accounts.accounts import DefaultAccount, DefaultGuest from evennia.utils.test_resources import BaseEvenniaTest from evennia.utils import create from evennia.utils.utils import uses_database class TestAccountSessionHandler(TestCase): "Check AccountSessionHandler class" def setUp(self): self.account = create.create_account( f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount, ) self.handler = AccountSessionHandler(self.account) def tearDown(self): if hasattr(self, "account"): self.account.delete() def test_get(self): "Check get method" self.assertEqual(self.handler.get(), []) self.assertEqual(self.handler.get(100), []) import evennia.server.sessionhandler s1 = MagicMock() s1.logged_in = True s1.uid = self.account.uid evennia.server.sessionhandler.SESSIONS[s1.uid] = s1 s2 = MagicMock() s2.logged_in = True s2.uid = self.account.uid + 1 evennia.server.sessionhandler.SESSIONS[s2.uid] = s2 s3 = MagicMock() s3.logged_in = False s3.uid = self.account.uid + 2 evennia.server.sessionhandler.SESSIONS[s3.uid] = s3 self.assertEqual([s.uid for s in self.handler.get()], [s1.uid]) self.assertEqual([s.uid for s in [self.handler.get(self.account.uid)]], [s1.uid]) self.assertEqual([s.uid for s in self.handler.get(self.account.uid + 1)], []) def test_all(self): "Check all method" self.assertEqual(self.handler.get(), self.handler.all()) def test_count(self): "Check count method" self.assertEqual(self.handler.count(), len(self.handler.get())) @override_settings(GUEST_ENABLED=True, GUEST_LIST=["bruce_wayne"]) class TestDefaultGuest(BaseEvenniaTest): "Check DefaultGuest class" ip = "212.216.134.22" @override_settings(GUEST_ENABLED=False) def test_create_not_enabled(self): # Guest account should not be permitted account, errors = DefaultGuest.authenticate(ip=self.ip) self.assertFalse(account, "Guest account was created despite being disabled.") def test_authenticate(self): # Create a guest account account, errors = DefaultGuest.authenticate(ip=self.ip) self.assertTrue(account, "Guest account should have been created.") # Create a second guest account account, errors = DefaultGuest.authenticate(ip=self.ip) self.assertFalse( account, "Two guest accounts were created with a single entry on the guest list!" ) @patch("evennia.accounts.accounts.ChannelDB.objects.get_channel") def test_create(self, get_channel): get_channel.connect = MagicMock(return_value=True) with override_settings(GUEST_HOME=self.room1.dbref): account, errors = DefaultGuest.create() self.assertTrue(account, "Guest account should have been created.") self.assertFalse(errors) def test_at_post_login(self): self.account.db._last_puppet = self.char1 self.account.at_post_login(self.session) self.account.at_post_login() def test_at_server_shutdown(self): account, errors = DefaultGuest.create(ip=self.ip) self.char1.delete = MagicMock() account.db._playable_characters = [self.char1] account.at_server_shutdown() self.char1.delete.assert_called() def test_at_post_disconnect(self): account, errors = DefaultGuest.create(ip=self.ip) self.char1.delete = MagicMock() account.db._playable_characters = [self.char1] account.at_post_disconnect() self.char1.delete.assert_called() class TestDefaultAccountAuth(BaseEvenniaTest): def setUp(self): super().setUp() self.password = "testpassword" self.account.delete() self.account = create.create_account( f"TestAccount{randint(100000, 999999)}", email="test@test.com", password=self.password, typeclass=DefaultAccount, ) def test_authentication(self): "Confirm Account authentication method is authenticating/denying users." # Valid credentials obj, errors = DefaultAccount.authenticate(self.account.name, self.password) self.assertTrue(obj, "Account did not authenticate given valid credentials.") # Invalid credentials obj, errors = DefaultAccount.authenticate(self.account.name, "xyzzy") self.assertFalse(obj, "Account authenticated using invalid credentials.") def test_create(self): "Confirm Account creation is working as expected." # Create a normal account account, errors = DefaultAccount.create(username="ziggy", password="stardust11") self.assertTrue(account, "New account should have been created.") # Try creating a duplicate account account2, errors = DefaultAccount.create(username="Ziggy", password="starman11") self.assertFalse(account2, "Duplicate account name should not have been allowed.") account.delete() def test_throttle(self): "Confirm throttle activates on too many failures." for x in range(20): obj, errors = DefaultAccount.authenticate(self.account.name, "xyzzy", ip="12.24.36.48") self.assertFalse( obj, "Authentication was provided a bogus password; this should NOT have returned an account!", ) self.assertTrue( "too many login failures" in errors[-1].lower(), "Failed logins should have been throttled.", ) def test_username_validation(self): "Check username validators deny relevant usernames" # Should not accept Unicode by default, lest users pick names like this if not uses_database("mysql"): # TODO As of Mar 2019, mysql does not pass this test due to collation problems # that has not been possible to resolve result, error = DefaultAccount.validate_username("¯\_(ツ)_/¯") self.assertFalse(result, "Validator allowed kanji in username.") # Should not allow duplicate username result, error = DefaultAccount.validate_username(self.account.name) self.assertFalse(result, "Duplicate username should not have passed validation.") # Should not allow username too short result, error = DefaultAccount.validate_username("xx") self.assertFalse(result, "2-character username passed validation.") def test_password_validation(self): "Check password validators deny bad passwords" account = create.create_account( f"TestAccount{randint(100000, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount, ) for bad in ("", "123", "password", "TestAccount", "#", "xyzzy"): self.assertFalse(account.validate_password(bad, account=self.account)[0]) "Check validators allow sufficiently complex passwords" for better in ("Mxyzptlk", "j0hn, i'M 0n1y d4nc1nG"): self.assertTrue(account.validate_password(better, account=self.account)[0]) account.delete() def test_password_change(self): "Check password setting and validation is working as expected" account = create.create_account( f"TestAccount{randint(100000, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount, ) from django.core.exceptions import ValidationError # Try setting some bad passwords for bad in ("", "#", "TestAccount", "password"): valid, error = account.validate_password(bad, account) self.assertFalse(valid) # Try setting a better password (test for False; returns None on success) self.assertFalse(account.set_password("Mxyzptlk")) account.delete() class TestDefaultAccount(TestCase): "Check DefaultAccount class" def setUp(self): self.s1 = MagicMock() self.s1.puppet = None self.s1.sessid = 0 def test_puppet_object_no_object(self): "Check puppet_object method called with no object param" try: DefaultAccount().puppet_object(self.s1, None) self.fail("Expected error: 'Object not found'") except RuntimeError as re: self.assertEqual("Object not found", str(re)) def test_puppet_object_no_session(self): "Check puppet_object method called with no session param" try: DefaultAccount().puppet_object(None, Mock()) self.fail("Expected error: 'Session not found'") except RuntimeError as re: self.assertEqual("Session not found", str(re)) def test_puppet_object_already_puppeting(self): "Check puppet_object method called, already puppeting this" import evennia.server.sessionhandler account = create.create_account( f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount, ) self.s1.uid = account.uid evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1 self.s1.logged_in = True self.s1.data_out = Mock(return_value=None) obj = Mock() self.s1.puppet = obj account.puppet_object(self.s1, obj) self.s1.data_out.assert_called_with( options=None, text="You are already puppeting this object." ) self.assertIsNone(obj.at_post_puppet.call_args) def test_puppet_object_no_permission(self): "Check puppet_object method called, no permission" import evennia.server.sessionhandler account = create.create_account( f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount, ) self.s1.uid = account.uid evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1 self.s1.data_out = MagicMock() obj = Mock() obj.access = Mock(return_value=False) account.puppet_object(self.s1, obj) self.assertTrue( self.s1.data_out.call_args[1]["text"].startswith("You don't have permission to puppet") ) self.assertIsNone(obj.at_post_puppet.call_args) @override_settings(MULTISESSION_MODE=0) def test_puppet_object_joining_other_session(self): "Check puppet_object method called, joining other session" import evennia.server.sessionhandler account = create.create_account( f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount, ) self.s1.uid = account.uid evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1 self.s1.puppet = None self.s1.logged_in = True self.s1.data_out = MagicMock() obj = Mock() obj.access = Mock(return_value=True) obj.account = account obj.sessions.all = MagicMock(return_value=[self.s1]) account.puppet_object(self.s1, obj) # works because django.conf.settings.MULTISESSION_MODE is not in (1, 3) self.assertTrue( self.s1.data_out.call_args[1]["text"].endswith("from another of your sessions.|n") ) self.assertTrue(obj.at_post_puppet.call_args[1] == {}) def test_puppet_object_already_puppeted(self): "Check puppet_object method called, already puppeted" import evennia.server.sessionhandler account = create.create_account( f"TestAccount{randint(0, 999999)}", email="test@test.com", password="testpassword", typeclass=DefaultAccount, ) self.account = account self.s1.uid = account.uid evennia.server.sessionhandler.SESSIONS[self.s1.uid] = self.s1 self.s1.puppet = None self.s1.logged_in = True self.s1.data_out = Mock(return_value=None) obj = Mock() obj.access = Mock(return_value=True) obj.account = Mock() obj.at_post_puppet = Mock() account.puppet_object(self.s1, obj) self.assertTrue( self.s1.data_out.call_args[1]["text"].endswith( "is already puppeted by another Account." ) ) self.assertIsNone(obj.at_post_puppet.call_args) class TestAccountPuppetDeletion(BaseEvenniaTest): @override_settings(MULTISESSION_MODE=2) def test_puppet_deletion(self): # Check for existing chars self.assertFalse( self.account.db._playable_characters, "Account should not have any chars by default." ) # Add char1 to account's playable characters self.account.db._playable_characters.append(self.char1) self.assertTrue(self.account.db._playable_characters, "Char was not added to account.") # See what happens when we delete char1. self.char1.delete() # Playable char list should be empty. self.assertFalse( self.account.db._playable_characters, f"Playable character list is not empty! {self.account.db._playable_characters}", ) class TestDefaultAccountEv(BaseEvenniaTest): """ Testing using the EvenniaTest parent """ def test_characters_property(self): "test existence of None in _playable_characters Attr" self.account.db._playable_characters = [self.char1, None] chars = self.account.characters self.assertEqual(chars, [self.char1]) self.assertEqual(self.account.db._playable_characters, [self.char1]) def test_puppet_success(self): self.account.msg = MagicMock() with patch("evennia.accounts.accounts._MULTISESSION_MODE", 2): self.account.puppet_object(self.session, self.char1) self.account.msg.assert_called_with("You are already puppeting this object.") @patch("evennia.accounts.accounts.time.time", return_value=10000) def test_idle_time(self, mock_time): self.session.cmd_last_visible = 10000 - 10 idle = self.account.idle_time self.assertEqual(idle, 10) # test no sessions with patch( "evennia.accounts.accounts._SESSIONS.sessions_from_account", return_value=[] ) as mock_sessh: idle = self.account.idle_time self.assertEqual(idle, None) @patch("evennia.accounts.accounts.time.time", return_value=10000) def test_connection_time(self, mock_time): self.session.conn_time = 10000 - 10 conn = self.account.connection_time self.assertEqual(conn, 10) # test no sessions with patch( "evennia.accounts.accounts._SESSIONS.sessions_from_account", return_value=[] ) as mock_sessh: idle = self.account.connection_time self.assertEqual(idle, None) def test_create_account(self): acct = create.account( "TestAccount3", "test@test.com", "testpassword123", locks="test:all()", tags=[("tag1", "category1"), ("tag2", "category2", "data1"), ("tag3", None)], attributes=[("key1", "value1", "category1", "edit:false()", True), ("key2", "value2")], ) acct.save() self.assertTrue(acct.pk) def test_at_look(self): ret = self.account.at_look() self.assertTrue("Out-of-Character" in ret) ret = self.account.at_look(target=self.obj1) self.assertTrue("Obj" in ret) ret = self.account.at_look(session=self.session) self.assertTrue("*" in ret) # * marks session is active in list ret = self.account.at_look(target=self.obj1, session=self.session) self.assertTrue("Obj" in ret) ret = self.account.at_look(target="Invalid", session=self.session) self.assertEqual(ret, "Invalid has no in-game appearance.") def test_msg(self): self.account.msg