Make tests pass when running on cms

This commit is contained in:
Calen Pennington
2012-06-18 13:21:06 -04:00
parent 328b2df7c5
commit 5404345b1f
26 changed files with 57 additions and 64 deletions

View File

@@ -1,61 +0,0 @@
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
import contentstore.tasks
from pyftpdlib import ftpserver
import os
class DjangoAuthorizer(object):
def validate_authentication(self, username, password):
try:
u=User.objects.get(username=username)
except User.DoesNotExist:
return False
# TODO: Check security groups
return u.check_password(password)
def has_user(self, username):
print "????",username
return True
def has_perm(self, username, perm, path=None):
print "!!!!!",username, perm, path
return True
def get_home_dir(self, username):
d = "/tmp/ftp/"+username
try:
os.mkdir(d)
except OSError:
pass
return "/tmp/ftp/"+username
def get_perms(self, username):
return 'elradfmw'
def get_msg_login(self, username):
return 'Hello'
def get_msg_quit(self, username):
return 'Goodbye'
def __init__(self):
pass
def impersonate_user(self, username, password):
pass
def terminate_impersonation(self, username):
pass
def on_upload(ftp_handler, filename):
source = ftp_handler.remote_ip
author = ftp_handler.username
print filename, author, source
# We pass on this for now:
# contentstore.tasks.on_upload
# It is a changing API, and it makes testing the FTP server slow.
class Command(BaseCommand):
help = \
''' Run FTP server.'''
def handle(self, *args, **options):
authorizer = DjangoAuthorizer() #ftpserver.DummyAuthorizer()
handler = ftpserver.FTPHandler
handler.on_file_received = on_upload
handler.authorizer = authorizer
address = ("127.0.0.1", 2121)
ftpd = ftpserver.FTPServer(address, handler)
ftpd.serve_forever()

View File

@@ -38,6 +38,6 @@ CACHES = {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'KEY_PREFIX': 'general',
'VERSION': 4,
'KEY_FUNCTION': 'util.cache.memcache_safe_key',
'KEY_FUNCTION': 'util.memcache.safe_key',
}
}

54
cms/envs/test.py Normal file
View File

@@ -0,0 +1,54 @@
"""
This config file runs the simplest dev environment using sqlite, and db-based
sessions. Assumes structure:
/envroot/
/db # This is where it'll write the database file
/mitx # The location of this repo
/log # Where we're going to write log files
"""
from .common import *
import os
# Nose Test Runner
INSTALLED_APPS += ('django_nose',)
NOSE_ARGS = ['--cover-erase', '--with-xunit', '--with-xcoverage', '--cover-html', '--cover-inclusive']
for app in os.listdir(PROJECT_ROOT / 'djangoapps'):
NOSE_ARGS += ['--cover-package', app]
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
KEYSTORE = {
'host': 'localhost',
'db': 'mongo_base',
'collection': 'key_store',
}
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "mitx.db",
}
}
CACHES = {
# This is the cache used for most things. Askbot will not work without a
# functioning cache -- it relies on caching to load its settings in places.
# In staging/prod envs, the sessions also live here.
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'mitx_loc_mem_cache',
'KEY_FUNCTION': 'util.memcache.safe_key',
},
# The general cache is what you get if you use our util.cache. It's used for
# things like caching the course.xml file for different A/B test groups.
# We set it to be a DummyCache to force reloading of course.xml in dev.
# In staging environments, we would grab VERSION from data uploaded by the
# push process.
'general': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'KEY_PREFIX': 'general',
'VERSION': 4,
'KEY_FUNCTION': 'util.memcache.safe_key',
}
}

View File

@@ -1,113 +0,0 @@
"""
This module provides an abstraction for working objects that conceptually have
the following attributes:
location: An identifier for an item, of which there might be many revisions
children: A list of urls for other items required to fully define this object
data: A set of nested data needed to define this object
editor: The editor/owner of the object
parents: Url pointers for objects that this object was derived from
revision: What revision of the item this is
"""
class Location(object):
''' Encodes a location.
Can be:
* String (url)
* Tuple
* Dictionary
'''
def __init__(self, location):
self.update(location)
def update(self, location):
if isinstance(location, basestring):
self.tag = location.split('/')[0][:-1]
(self.org, self.course, self.category, self.name) = location.split('/')[2:]
elif isinstance(location, list):
(self.tag, self.org, self.course, self.category, self.name) = location
elif isinstance(location, dict):
self.tag = location['tag']
self.org = location['org']
self.course = location['course']
self.category = location['category']
self.name = location['name']
elif isinstance(location, Location):
self.update(location.list())
def url(self):
return "{tag}://{org}/{course}/{category}/{name}".format(**self.dict())
def list(self):
return [self.tag, self.org, self.course, self.category, self.name]
def dict(self):
return {'tag': self.tag,
'org': self.org,
'course': self.course,
'category': self.category,
'name': self.name}
def to_json(self):
return self.dict()
class KeyStore(object):
def get_item(self, location):
"""
Returns an XModuleDescriptor instance for the item at location
If no object is found at that location, raises keystore.exceptions.ItemNotFoundError
Searches for all matches of a partially specifed location, but raises an
keystore.exceptions.InsufficientSpecificationError if more
than a single object matches the query.
location: Something that can be passed to Location
"""
raise NotImplementedError
def create_item(self, location, editor):
"""
Create an empty item at the specified location with the supplied editor
location: Something that can be passed to Location
"""
raise NotImplementedError
def update_item(self, location, data):
"""
Set the data in the item specified by the location to
data
location: Something that can be passed to Location
data: A nested dictionary of problem data
"""
raise NotImplementedError
def update_children(self, location, children):
"""
Set the children for the item specified by the location to
data
location: Something that can be passed to Location
children: A list of child item identifiers
"""
raise NotImplementedError
class KeyStoreItem(object):
"""
An object from a KeyStore, which can be saved back to that keystore
"""
def __init__(self, location, children, data, editor, parents, revision):
self.location = location
self.children = children
self.data = data
self.editor = editor
self.parents = parents
self.revision = revision
def save(self):
raise NotImplementedError

View File

@@ -1,12 +0,0 @@
"""
Module that provides a connection to the keystore specified in the django settings.
Passes settings.KEYSTORE as kwargs to MongoKeyStore
"""
from __future__ import absolute_import
from django.conf import settings
from .mongo import MongoKeyStore
keystore = MongoKeyStore(**settings.KEYSTORE)

View File

@@ -1,11 +0,0 @@
"""
Exceptions thrown by KeyStore objects
"""
class ItemNotFoundError(Exception):
pass
class InsufficientSpecificationError(Exception):
pass

View File

@@ -1,86 +0,0 @@
import pymongo
from . import KeyStore, Location
from .exceptions import ItemNotFoundError, InsufficientSpecificationError
from xmodule.x_module import XModuleDescriptor
class MongoKeyStore(KeyStore):
"""
A Mongodb backed KeyStore
"""
def __init__(self, host, db, collection, port=27017):
self.collection = pymongo.connection.Connection(
host=host,
port=port
)[db][collection]
# Force mongo to report errors, at the expense of performance
self.collection.safe = True
def get_item(self, location):
"""
Returns an XModuleDescriptor instance for the item at location
If no object is found at that location, raises keystore.exceptions.ItemNotFoundError
Searches for all matches of a partially specifed location, but raises an
keystore.exceptions.InsufficientSpecificationError if more
than a single object matches the query.
location: Something that can be passed to Location
"""
query = dict(
('location.{key}'.format(key=key), val)
for (key, val)
in Location(location).dict().items()
if val is not None
)
items = self.collection.find(
query,
sort=[('revision', pymongo.ASCENDING)],
limit=1,
)
if items.count() > 1:
raise InsufficientSpecificationError(location)
if items.count() == 0:
raise ItemNotFoundError(location)
return XModuleDescriptor.load_from_json(items[0], self.get_item)
def create_item(self, location, editor):
"""
Create an empty item at the specified location with the supplied editor
location: Something that can be passed to Location
"""
self.collection.insert({
'location': Location(location).dict(),
'editor': editor
})
def update_item(self, location, data):
"""
Set the data in the item specified by the location to
data
location: Something that can be passed to Location
data: A nested dictionary of problem data
"""
self.collection.update(
{'location': Location(location).dict()},
{'$set': {'data': data}}
)
def update_children(self, location, children):
"""
Set the children for the item specified by the location to
data
location: Something that can be passed to Location
children: A list of child item identifiers
"""
self.collection.update(
{'location': Location(location).dict()},
{'$set': {'children': children}}
)