Пример #1
0
class CleanableCM(object):
    """Cleanable context manager (based on ExitStack)"""

    def __init__(self):
        super(CleanableCM, self).__init__()
        self.stack = ExitStack()

    def _enter(self):
        """Should be override"""
        raise NotImplementedError

    @contextmanager
    def _cleanup_on_error(self):
        with ExitStack() as stack:
            stack.push(self)
            yield
            stack.pop_all()

    def __enter__(self):
        with self._cleanup_on_error():
            self.stack.__enter__()
            return self._enter()

    def __exit__(self, exc_type, exc_value, traceback):
        self.stack.__exit__(exc_type, exc_value, traceback)
Пример #2
0
class LoggedExitStack(object):
    def __init__(self, logger, context_managers=None):
        self._logger = logger
        self._exit_stack = ExitStack()

        # TODO make this cleaner, types would be nice
        context_managers = context_managers if context_managers is not None else []

        self.context_managers = (context_managers if isinstance(
            context_managers, list) else [context_managers])

    def __enter__(self):
        self._exit_stack.__enter__()
        for context_manager in self.context_managers:
            self._exit_stack.enter_context(
                LogScope(self._logger, context_manager.__class__.__name__))
            self._exit_stack.enter_context(context_manager)
        return self

    def __exit__(self, *args):
        return self._exit_stack.__exit__(*args)
Пример #3
0
class _TestUser(object):
    def __init__(self, test_client, runestone_db_tools, username, password,
                 course_name):
        self.test_client = test_client
        self.runestone_db_tools = runestone_db_tools
        self.username = username
        self.first_name = 'test'
        self.last_name = 'user'
        self.email = self.username + '@foo.com'
        self.password = password
        self.course_name = course_name

    def __enter__(self):
        # Registration doesn't work unless we're logged out.
        self.test_client.logout()
        # Now, post the registration.
        self.test_client.validate(
            'default/user/register',
            'Course Selection',
            data=dict(
                username=self.username,
                first_name=self.first_name,
                last_name=self.last_name,
                # The e-mail address must be unique.
                email=self.email,
                password=self.password,
                password_two=self.password,
                # Note that ``course_id`` is (on the form) actually a course name.
                course_id=self.course_name,
                accept_tcp='on',
                donate='0',
                _next='/runestone/default/index',
                _formname='register',
            ))

        # Schedule this user for deletion.
        self.exit_stack_object = ExitStack()
        self.exit_stack = self.exit_stack_object.__enter__()
        self.exit_stack.callback(self._delete_user)

        # Record IDs
        db = self.runestone_db_tools.db
        self.course_id = db(db.courses.course_name == self.course_name).select(
            db.courses.id).first().id
        self.user_id = db(db.auth_user.username == self.username).select(
            db.auth_user.id).first().id

        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.exit_stack_object.__exit__(exc_type, exc_value, traceback)

    # Delete the user created by entering this context manager.
    def _delete_user(self):
        db = self.runestone_db_tools.db
        # Delete the course this user registered for.
        db((db.user_courses.course_id == self.course_id)
           & (db.user_courses.user_id == self.user_id)).delete()
        # Delete the user.
        db(db.auth_user.username == self.username).delete()
        db.commit()

    def login(self):
        self.test_client.post('default/user/login',
                              data=dict(
                                  username=self.username,
                                  password=self.password,
                                  _formname='login',
                              ))

    def make_instructor(self, course_id=None):
        # If ``course_id`` isn't specified, use this user's ``course_id``.
        course_id = course_id or self.course_id
        return self.runestone_db_tools.make_instructor(self.user_id, course_id)

    def add_user_to_course(self, course_id=None):
        # If ``course_id`` isn't specified, use this user's ``course_id``.
        course_id = course_id or self.course_id
        return self.runestone_db_tools.add_user_to_course(
            self.user_id, course_id)
Пример #4
0
class _TestUser(object):
    def __init__(
        self,
        test_client,
        runestone_db_tools,
        username,
        password,
        course_name,
        # True if the course is free (no payment required); False otherwise.
        is_free=True):

        self.test_client = test_client
        self.runestone_db_tools = runestone_db_tools
        self.username = username
        self.first_name = 'test'
        self.last_name = 'user'
        self.email = self.username + '@foo.com'
        self.password = password
        self.course_name = course_name
        self.is_free = is_free

    def __enter__(self):
        # Registration doesn't work unless we're logged out.
        self.test_client.logout()
        # Now, post the registration.
        self.test_client.validate(
            'default/user/register',
            'Support Runestone Interactive'
            if self.is_free else 'Payment Amount',
            data=dict(
                username=self.username,
                first_name=self.first_name,
                last_name=self.last_name,
                # The e-mail address must be unique.
                email=self.email,
                password=self.password,
                password_two=self.password,
                # Note that ``course_id`` is (on the form) actually a course name.
                course_id=self.course_name,
                accept_tcp='on',
                donate='0',
                _next='/runestone/default/index',
                _formname='register',
            ))

        # Schedule this user for deletion.
        self.exit_stack_object = ExitStack()
        self.exit_stack = self.exit_stack_object.__enter__()
        self.exit_stack.callback(self._delete_user)

        # Record IDs
        db = self.runestone_db_tools.db
        self.course_id = db(db.courses.course_name == self.course_name).select(
            db.courses.id).first().id
        self.user_id = db(db.auth_user.username == self.username).select(
            db.auth_user.id).first().id

        return self

    # Clean up on exit by invoking all ``__exit__`` methods.
    def __exit__(self, exc_type, exc_value, traceback):
        self.exit_stack_object.__exit__(exc_type, exc_value, traceback)

    # Delete the user created by entering this context manager. TODO: This doesn't delete all the chapter progress tracking stuff.
    def _delete_user(self):
        db = self.runestone_db_tools.db
        # Delete the course this user registered for.
        db((db.user_courses.course_id == self.course_id)
           & (db.user_courses.user_id == self.user_id)).delete()
        # Delete the user.
        db(db.auth_user.username == self.username).delete()
        db.commit()

    def login(self):
        self.test_client.post('default/user/login',
                              data=dict(
                                  username=self.username,
                                  password=self.password,
                                  _formname='login',
                              ))

    def make_instructor(self, course_id=None):
        # If ``course_id`` isn't specified, use this user's ``course_id``.
        course_id = course_id or self.course_id
        return self.runestone_db_tools.make_instructor(self.user_id, course_id)

    # A context manager to update this user's profile. If a course was added, it returns that course's ID; otherwise, it returns None.
    @contextmanager
    def update_profile(
        self,
        # This parameter is passed to ``test_clint.validate``.
        expected_string=None,
        # An updated username, or ``None`` to use ``self.username``.
        username=None,
        # An updated first name, or ``None`` to use ``self.first_name``.
        first_name=None,
        # An updated last name, or ``None`` to use ``self.last_name``.
        last_name=None,
        # An updated email, or ``None`` to use ``self.email``.
        email=None,
        # An updated last name, or ``None`` to use ``self.course_name``.
        course_name=None,
        section='',
        # A shortcut for specifying the ``expected_string``, which only applies if ``expected_string`` is not set. Use ``None`` if a course will not be added, ``True`` if the added course is free, or ``False`` if the added course is paid.
        is_free=None,
        # The value of the ``accept_tcp`` checkbox; provide an empty string to leave unchecked. The default value leaves it checked.
        accept_tcp='on'):

        if expected_string is None:
            if is_free is None:
                expected_string = 'Course Selection'
            else:
                expected_string = 'Support Runestone Interactive' \
                    if is_free else 'Payment Amount'
        username = username or self.username
        first_name = first_name or self.first_name
        last_name = last_name or self.last_name
        email = email or self.email
        course_name = course_name or self.course_name

        db = self.runestone_db_tools.db
        # Determine if we're adding a course. If so, delete it at the end of the test. To determine if a course is being added, the course must exist, but not be in the user's list of courses.
        course = db(db.courses.course_name == course_name).select(
            db.courses.id).first()
        delete_at_end = course and not db(
            (db.user_courses.user_id == self.user_id)
            & (db.user_courses.course_id == course.id)).select(
                db.user_courses.id).first()

        # Perform the update.
        try:
            self.test_client.validate(
                'default/user/profile',
                expected_string,
                data=dict(
                    username=username,
                    first_name=first_name,
                    last_name=last_name,
                    email=email,
                    # Though the field is ``course_id``, it's really the course name.
                    course_id=course_name,
                    accept_tcp=accept_tcp,
                    section=section,
                    _next='/runestone/default/index',
                    id=str(self.user_id),
                    _formname='auth_user/' + str(self.user_id),
                ))

            yield course.id if delete_at_end else None
        finally:
            if delete_at_end:
                db = self.runestone_db_tools.db
                db((db.user_courses.user_id == self.user_id)
                   & (db.user_courses.course_id == course.id)).delete()
                db.commit()

    # Call this after registering for a new course or adding a new course via ``update_profile`` to pay for the course.
    @contextmanager
    def make_payment(
        self,
        # The `Stripe test tokens <https://stripe.com/docs/testing#cards>`_ to use for payment.
        stripe_token,
        # The course ID of the course to pay for. None specifies ``self.course_id``.
        course_id=None):

        course_id = course_id or self.course_id

        # Get the signature from the HTML of the payment page.
        self.test_client.validate('default/payment')
        match = re.search(
            '<input type="hidden" name="signature" value="([^ ]*)" \/>',
            self.test_client.text)
        signature = match.group(1)

        try:
            self.test_client.validate(
                'default/payment',
                ['Thank you for your payment', 'Payment failed'],
                data=dict(stripeToken=stripe_token, signature=signature))

            yield None

        finally:
            db = self.runestone_db_tools.db
            db((db.user_courses.course_id == course_id)
               & (db.user_courses.user_id == self.user_id)).delete()