mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Change save/search_prototype, extend unittests
This commit is contained in:
parent
dac10eef2b
commit
0dfea46d5c
12 changed files with 154 additions and 39 deletions
|
|
@ -53,6 +53,15 @@ Web/Django standard initiative (@strikaco)
|
|||
- Bugfixes
|
||||
- Fixes bug on login page where error messages were not being displayed
|
||||
|
||||
### Prototypes
|
||||
|
||||
- `evennia.prototypes.save_prototype` now takes the prototype as a normal
|
||||
argument (`prototype`) instead of having to give it as `**prototype`.
|
||||
- `evennia.prototypes.search_prototype` has a new kwarg `require_single=False` that
|
||||
raises a KeyError exception if query gave 0 or >1 results.
|
||||
- `evennia.prototypes.spawner` can now spawn by passing a `prototype_key`
|
||||
|
||||
|
||||
### Typeclasses
|
||||
|
||||
- Add new methods on all typeclasses, useful specifically for object handling from the website/admin:
|
||||
|
|
|
|||
|
|
@ -1494,7 +1494,6 @@ class DefaultGuest(DefaultAccount):
|
|||
characters = self.db._playable_characters
|
||||
for character in characters:
|
||||
if character:
|
||||
print "deleting Character:", character
|
||||
character.delete()
|
||||
|
||||
def at_post_disconnect(self, **kwargs):
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from mock import Mock, MagicMock
|
||||
import sys
|
||||
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 EvenniaTest
|
||||
from evennia.utils.test_resources import EvenniaTest, unload_module
|
||||
from evennia.utils import create
|
||||
|
||||
from django.conf import settings
|
||||
|
|
@ -60,6 +61,7 @@ class TestAccountSessionHandler(TestCase):
|
|||
"Check count method"
|
||||
self.assertEqual(self.handler.count(), len(self.handler.get()))
|
||||
|
||||
|
||||
class TestDefaultGuest(EvenniaTest):
|
||||
"Check DefaultGuest class"
|
||||
|
||||
|
|
@ -162,6 +164,7 @@ class TestDefaultAccountAuth(EvenniaTest):
|
|||
self.assertFalse(account.set_password('Mxyzptlk'))
|
||||
account.delete()
|
||||
|
||||
|
||||
class TestDefaultAccount(TestCase):
|
||||
"Check DefaultAccount class"
|
||||
|
||||
|
|
@ -279,13 +282,36 @@ class TestAccountPuppetDeletion(EvenniaTest):
|
|||
@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.')
|
||||
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.')
|
||||
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, 'Playable character list is not empty! %s' % self.account.db._playable_characters)
|
||||
self.assertFalse(self.account.db._playable_characters,
|
||||
'Playable character list is not empty! %s' % self.account.db._playable_characters)
|
||||
|
||||
|
||||
class TestDefaultAccountEv(EvenniaTest):
|
||||
"""
|
||||
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):
|
||||
unload_module(DefaultAccount)
|
||||
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.")
|
||||
|
|
|
|||
|
|
@ -2978,7 +2978,7 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS):
|
|||
|
||||
# all seems ok. Try to save.
|
||||
try:
|
||||
prot = protlib.save_prototype(**prototype)
|
||||
prot = protlib.save_prototype(prototype)
|
||||
if not prot:
|
||||
caller.msg("|rError saving:|R {}.|n".format(prototype_key))
|
||||
return
|
||||
|
|
|
|||
|
|
@ -680,9 +680,9 @@ class TestBuilding(CommandTest):
|
|||
goblin.delete()
|
||||
|
||||
# create prototype
|
||||
protlib.create_prototype(**{'key': 'Ball',
|
||||
'typeclass': 'evennia.objects.objects.DefaultCharacter',
|
||||
'prototype_key': 'testball'})
|
||||
protlib.create_prototype({'key': 'Ball',
|
||||
'typeclass': 'evennia.objects.objects.DefaultCharacter',
|
||||
'prototype_key': 'testball'})
|
||||
|
||||
# Tests "@spawn <prototype_name>"
|
||||
self.call(building.CmdSpawn(), "testball", "Spawned Ball")
|
||||
|
|
|
|||
|
|
@ -262,7 +262,13 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
con = self.contents_cache.get(exclude=exclude)
|
||||
# print "contents_get:", self, con, id(self), calledby() # DEBUG
|
||||
return con
|
||||
contents = property(contents_get)
|
||||
|
||||
def contents_set(self, *args):
|
||||
"You cannot replace this property"
|
||||
raise AttributeError("{}.contents is read-only. Use obj.move_to or "
|
||||
"obj.location to move an object here.".format(self.__class__))
|
||||
|
||||
contents = property(contents_get, contents_set, contents_set)
|
||||
|
||||
@property
|
||||
def exits(self):
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ from evennia import prototypes
|
|||
|
||||
goblin = {"prototype_key": "goblin:, ... }
|
||||
|
||||
prototype = prototypes.save_prototype(caller, **goblin)
|
||||
prototype = prototypes.save_prototype(goblin)
|
||||
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -2138,7 +2138,7 @@ def node_prototype_save(caller, **kwargs):
|
|||
# we already validated and accepted the save, so this node acts as a goto callback and
|
||||
# should now only return the next node
|
||||
prototype_key = prototype.get("prototype_key")
|
||||
protlib.save_prototype(**prototype)
|
||||
protlib.save_prototype(prototype)
|
||||
|
||||
spawned_objects = protlib.search_objects_with_prototype(prototype_key)
|
||||
nspawned = spawned_objects.count()
|
||||
|
|
|
|||
|
|
@ -147,13 +147,13 @@ class DbPrototype(DefaultScript):
|
|||
# Prototype manager functions
|
||||
|
||||
|
||||
def save_prototype(**kwargs):
|
||||
def save_prototype(prototype):
|
||||
"""
|
||||
Create/Store a prototype persistently.
|
||||
|
||||
Kwargs:
|
||||
prototype_key (str): This is required for any storage.
|
||||
All other kwargs are considered part of the new prototype dict.
|
||||
Args:
|
||||
prototype (dict): The prototype to save. A `prototype_key` key is
|
||||
required.
|
||||
|
||||
Returns:
|
||||
prototype (dict or None): The prototype stored using the given kwargs, None if deleting.
|
||||
|
|
@ -166,8 +166,8 @@ def save_prototype(**kwargs):
|
|||
is expected to have valid permissions.
|
||||
|
||||
"""
|
||||
|
||||
kwargs = homogenize_prototype(kwargs)
|
||||
in_prototype = prototype
|
||||
in_prototype = homogenize_prototype(in_prototype)
|
||||
|
||||
def _to_batchtuple(inp, *args):
|
||||
"build tuple suitable for batch-creation"
|
||||
|
|
@ -176,7 +176,7 @@ def save_prototype(**kwargs):
|
|||
return inp
|
||||
return (inp, ) + args
|
||||
|
||||
prototype_key = kwargs.get("prototype_key")
|
||||
prototype_key = in_prototype.get("prototype_key")
|
||||
if not prototype_key:
|
||||
raise ValidationError("Prototype requires a prototype_key")
|
||||
|
||||
|
|
@ -192,21 +192,21 @@ def save_prototype(**kwargs):
|
|||
stored_prototype = DbPrototype.objects.filter(db_key=prototype_key)
|
||||
prototype = stored_prototype[0].prototype if stored_prototype else {}
|
||||
|
||||
kwargs['prototype_desc'] = kwargs.get("prototype_desc", prototype.get("prototype_desc", ""))
|
||||
prototype_locks = kwargs.get(
|
||||
in_prototype['prototype_desc'] = in_prototype.get("prototype_desc", prototype.get("prototype_desc", ""))
|
||||
prototype_locks = in_prototype.get(
|
||||
"prototype_locks", prototype.get('prototype_locks', "spawn:all();edit:perm(Admin)"))
|
||||
is_valid, err = validate_lockstring(prototype_locks)
|
||||
if not is_valid:
|
||||
raise ValidationError("Lock error: {}".format(err))
|
||||
kwargs['prototype_locks'] = prototype_locks
|
||||
in_prototype['prototype_locks'] = prototype_locks
|
||||
|
||||
prototype_tags = [
|
||||
_to_batchtuple(tag, _PROTOTYPE_TAG_META_CATEGORY)
|
||||
for tag in make_iter(kwargs.get("prototype_tags",
|
||||
for tag in make_iter(in_prototype.get("prototype_tags",
|
||||
prototype.get('prototype_tags', [])))]
|
||||
kwargs["prototype_tags"] = prototype_tags
|
||||
in_prototype["prototype_tags"] = prototype_tags
|
||||
|
||||
prototype.update(kwargs)
|
||||
prototype.update(in_prototype)
|
||||
|
||||
if stored_prototype:
|
||||
# edit existing prototype
|
||||
|
|
@ -261,19 +261,25 @@ def delete_prototype(prototype_key, caller=None):
|
|||
return True
|
||||
|
||||
|
||||
def search_prototype(key=None, tags=None):
|
||||
def search_prototype(key=None, tags=None, require_single=False):
|
||||
"""
|
||||
Find prototypes based on key and/or tags, or all prototypes.
|
||||
|
||||
Kwargs:
|
||||
key (str): An exact or partial key to query for.
|
||||
tags (str or list): Tag key or keys to query for. These
|
||||
tags (str or list): Tag key or keys to query for. These
|
||||
will always be applied with the 'db_protototype'
|
||||
tag category.
|
||||
require_single (bool): If set, raise KeyError if the result
|
||||
was not found or if there are multiple matches.
|
||||
|
||||
Return:
|
||||
matches (list): All found prototype dicts. If no keys
|
||||
or tags are given, all available prototypes will be returned.
|
||||
matches (list): All found prototype dicts. Empty list if
|
||||
no match was found. Note that if neither `key` nor `tags`
|
||||
were given, *all* available prototypes will be returned.
|
||||
|
||||
Raises:
|
||||
KeyError: If `require_single` is True and there are 0 or >1 matches.
|
||||
|
||||
Note:
|
||||
The available prototypes is a combination of those supplied in
|
||||
|
|
@ -329,6 +335,10 @@ def search_prototype(key=None, tags=None):
|
|||
if mta.get('prototype_key') and mta['prototype_key'] == key]
|
||||
if filter_matches and len(filter_matches) < nmatches:
|
||||
matches = filter_matches
|
||||
|
||||
nmatches = len(matches)
|
||||
if nmatches != 1 and require_single:
|
||||
raise KeyError("Found {} matching prototypes.".format(nmatches))
|
||||
|
||||
return matches
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ prot = {
|
|||
"attrs": [("weapon", "sword")]
|
||||
}
|
||||
|
||||
prot = prototypes.create_prototype(**prot)
|
||||
prot = prototypes.create_prototype(prot)
|
||||
|
||||
```
|
||||
|
||||
|
|
@ -662,8 +662,9 @@ def spawn(*prototypes, **kwargs):
|
|||
Spawn a number of prototyped objects.
|
||||
|
||||
Args:
|
||||
prototypes (dict): Each argument should be a prototype
|
||||
dictionary.
|
||||
prototypes (str or dict): Each argument should either be a
|
||||
prototype_key (will be used to find the prototype) or a full prototype
|
||||
dictionary. These will be batched-spawned as one object each.
|
||||
Kwargs:
|
||||
prototype_modules (str or list): A python-path to a prototype
|
||||
module, or a list of such paths. These will be used to build
|
||||
|
|
@ -686,6 +687,11 @@ def spawn(*prototypes, **kwargs):
|
|||
`return_parents` is set, instead return dict of prototype parents.
|
||||
|
||||
"""
|
||||
# search string (=prototype_key) from input
|
||||
prototypes = [protlib.search_prototype(prot, require_single=True)[0]
|
||||
if isinstance(prot, basestring) else prot
|
||||
for prot in prototypes]
|
||||
|
||||
# get available protparents
|
||||
protparents = {prot['prototype_key'].lower(): prot for prot in protlib.search_prototype()}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class TestSpawner(EvenniaTest):
|
|||
self.prot1 = {"prototype_key": "testprototype",
|
||||
"typeclass": "evennia.objects.objects.DefaultObject"}
|
||||
|
||||
def test_spawn(self):
|
||||
def test_spawn_from_prot(self):
|
||||
obj1 = spawner.spawn(self.prot1)
|
||||
# check spawned objects have the right tag
|
||||
self.assertEqual(list(protlib.search_objects_with_prototype("testprototype")), obj1)
|
||||
|
|
@ -62,6 +62,14 @@ class TestSpawner(EvenniaTest):
|
|||
_PROTPARENTS["GOBLIN"], _PROTPARENTS["GOBLIN_ARCHWIZARD"],
|
||||
prototype_parents=_PROTPARENTS)], ['goblin grunt', 'goblin archwizard'])
|
||||
|
||||
def test_spawn_from_str(self):
|
||||
protlib.save_prototype(self.prot1)
|
||||
obj1 = spawner.spawn(self.prot1['prototype_key'])
|
||||
self.assertEqual(list(protlib.search_objects_with_prototype("testprototype")), obj1)
|
||||
self.assertEqual([o.key for o in spawner.spawn(
|
||||
_PROTPARENTS["GOBLIN"], _PROTPARENTS["GOBLIN_ARCHWIZARD"],
|
||||
prototype_parents=_PROTPARENTS)], ['goblin grunt', 'goblin archwizard'])
|
||||
|
||||
|
||||
class TestUtils(EvenniaTest):
|
||||
|
||||
|
|
@ -245,6 +253,7 @@ class TestProtLib(EvenniaTest):
|
|||
super(TestProtLib, self).setUp()
|
||||
self.obj1.attributes.add("testattr", "testval")
|
||||
self.prot = spawner.prototype_from_object(self.obj1)
|
||||
|
||||
|
||||
def test_prototype_to_str(self):
|
||||
prstr = protlib.prototype_to_str(self.prot)
|
||||
|
|
@ -253,6 +262,22 @@ class TestProtLib(EvenniaTest):
|
|||
def test_check_permission(self):
|
||||
pass
|
||||
|
||||
def test_save_prototype(self):
|
||||
result = protlib.save_prototype(self.prot)
|
||||
self.assertEqual(result, self.prot)
|
||||
# faulty
|
||||
self.prot['prototype_key'] = None
|
||||
self.assertRaises(protlib.ValidationError, protlib.save_prototype, self.prot)
|
||||
|
||||
def test_search_prototype(self):
|
||||
protlib.save_prototype(self.prot)
|
||||
match = protlib.search_prototype("NotFound")
|
||||
self.assertFalse(match)
|
||||
match = protlib.search_prototype()
|
||||
self.assertTrue(match)
|
||||
match = protlib.search_prototype(self.prot['prototype_key'])
|
||||
self.assertEqual(match, [self.prot])
|
||||
|
||||
|
||||
@override_settings(PROT_FUNC_MODULES=['evennia.prototypes.protfuncs'], CLIENT_DEFAULT_WIDTH=20)
|
||||
class TestProtFuncs(EvenniaTest):
|
||||
|
|
@ -424,7 +449,7 @@ class TestPrototypeStorage(EvenniaTest):
|
|||
def test_prototype_storage(self):
|
||||
|
||||
# from evennia import set_trace;set_trace(term_size=(180, 50))
|
||||
prot1 = protlib.create_prototype(**self.prot1)
|
||||
prot1 = protlib.create_prototype(self.prot1)
|
||||
|
||||
self.assertTrue(bool(prot1))
|
||||
self.assertEqual(prot1, self.prot1)
|
||||
|
|
@ -436,7 +461,7 @@ class TestPrototypeStorage(EvenniaTest):
|
|||
protlib.DbPrototype.objects.get_by_tag(
|
||||
"foo1", _PROTOTYPE_TAG_META_CATEGORY)[0].db.prototype, prot1)
|
||||
|
||||
prot2 = protlib.create_prototype(**self.prot2)
|
||||
prot2 = protlib.create_prototype(self.prot2)
|
||||
self.assertEqual(
|
||||
[pobj.db.prototype
|
||||
for pobj in protlib.DbPrototype.objects.get_by_tag(
|
||||
|
|
@ -445,7 +470,7 @@ class TestPrototypeStorage(EvenniaTest):
|
|||
|
||||
# add to existing prototype
|
||||
prot1b = protlib.create_prototype(
|
||||
prototype_key='testprototype1', foo='bar', prototype_tags=['foo2'])
|
||||
{"prototype_key": 'testprototype1', "foo": 'bar', "prototype_tags": ['foo2']})
|
||||
|
||||
self.assertEqual(
|
||||
[pobj.db.prototype
|
||||
|
|
@ -457,7 +482,7 @@ class TestPrototypeStorage(EvenniaTest):
|
|||
self.assertNotEqual(list(protlib.search_prototype("testprototype1")), [prot1])
|
||||
self.assertEqual(list(protlib.search_prototype("testprototype1")), [prot1b])
|
||||
|
||||
prot3 = protlib.create_prototype(**self.prot3)
|
||||
prot3 = protlib.create_prototype(self.prot3)
|
||||
|
||||
# partial match
|
||||
with mock.patch("evennia.prototypes.prototypes._MODULE_PROTOTYPES", {}):
|
||||
|
|
@ -606,7 +631,7 @@ class TestMenuModule(EvenniaTest):
|
|||
self.assertEqual(olc_menus._display_tag(olc_menus._get_menu_prototype(caller)['tags'][0]), Something)
|
||||
self.assertEqual(olc_menus._caller_tags(caller), ["foo2", "foo3"])
|
||||
|
||||
protlib.save_prototype(**self.test_prot)
|
||||
protlib.save_prototype(self.test_prot)
|
||||
|
||||
# locks helpers
|
||||
self.assertEqual(olc_menus._lock_add(caller, "foo:false()"), "Added lock 'foo:false()'.")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
"""
|
||||
Various helper resources for writing unittests.
|
||||
|
||||
"""
|
||||
import sys
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from mock import Mock
|
||||
|
|
@ -14,6 +19,35 @@ SESSIONS.data_out = Mock()
|
|||
SESSIONS.disconnect = Mock()
|
||||
|
||||
|
||||
def unload_module(module_or_object):
|
||||
"""
|
||||
Reset import so one can mock global constants.
|
||||
|
||||
Args:
|
||||
module_or_object (module or object): The module will
|
||||
be removed so it will have to be imported again.
|
||||
|
||||
Example:
|
||||
# (in a test method)
|
||||
unload_module(foo)
|
||||
with mock.patch("foo.GLOBALTHING", "mockval"):
|
||||
import foo
|
||||
... # test code using foo.GLOBALTHING, now set to 'mockval'
|
||||
|
||||
|
||||
This allows for mocking constants global to the module, since
|
||||
otherwise those would not be mocked (since a module is only
|
||||
loaded once).
|
||||
|
||||
"""
|
||||
if hasattr(module_or_object, "__module__"):
|
||||
modulename = module_or_object.__module__
|
||||
else:
|
||||
modulename = module_or_object.__name__
|
||||
if modulename in sys.modules:
|
||||
del sys.modules[modulename]
|
||||
|
||||
|
||||
class EvenniaTest(TestCase):
|
||||
"""
|
||||
Base test for Evennia, sets up a basic environment.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue