Testing
=======

Dependencies
------------

Test suite is using +nose+, +httpretty+ and +mock+ for testing. You need these
three python modules installed to run tests. In openSUSE, you can do it using
the following command as a root:

--------------------------------------------------------------------------------
zypper in python-nose python-httpretty python-mock
--------------------------------------------------------------------------------

Running tests
-------------

To run the tests, you need to be in the topmost directory of your checkout and
run the following command there:

--------------------------------------------------------------------------------
nosetests
--------------------------------------------------------------------------------

Structure of the suite
----------------------

Each object contains functions for the individual tests so split them per
area of interest.

In directory fixtures there are resulting xml files obtained from the OBS with
osc api calls.

Writing tests
-------------

There are a few nice building stones available to implement test.

OBS class
~~~~~~~~~

+OBS+ class provides simulation of OBS including keeping internal states. It
supports only a limited number of commands, but that can be extended.

Extending OBS class
^^^^^^^^^^^^^^^^^^^

You can extend the OBS mockup class creating new method, and
decorating it with one of the @GET, @PUT, @POST or @DELETE.  The
parameter of the decorator is the PATH of the URL or a regular
expression that matches one of the possible paths.

If the new response can be implemented as a simple fixture, you can
create the file in the +fixtures/+ directory in a place compatible in
the expected path.

Because we are decorating methods, we can maintain an internal status
inside the OBS mock-up instance. 

example
^^^^^^^

First we create a template for our response. We will be using pythons string
Template class therefore XML has some values replaced with +${variable}+ and we
will assign those later.

.Template
[source,xml]
--------------------------------------------------------------------------------
<request id="${id}">
  <action type="submit">
    <source project="home:Admin" package="${package}" rev="59f0f46262d7b57b9cdc720c06d5e317"/>
    <target project="openSUSE:Factory" package="${package}"/>
  </action>
  <state name="${request}" who="Admin" when="2014-02-17T12:38:52">
    <comment>...</comment>
  </state>
  <review state="${review}" when="2014-02-17T12:34:10" who="${who}" by_${by}="${by_who}">
    <comment>...</comment>
  </review>
  <description>test</description>
</request>
--------------------------------------------------------------------------------

We can also define helpful local data structure representing actual state of OBS

[source,python]
--------------------------------------------------------------------------------
# Initial request data
self.requests = {
    '123': {
        'request': 'new',
        'review': 'accepted',
        'who': 'Admin',
        'by': 'group',
        'id': '123',
        'by_who': 'opensuse-review-team',
        'package': 'gcc',
    },
    '321': {
        'request': 'review',
        'review': 'new',
        'who': 'Admin',
        'by': 'group',
        'id': '321',
        'by_who': 'factory-staging',
        'package': 'puppet',
    },
}
--------------------------------------------------------------------------------

And the most important part is implementing OBS behaviour.

[source,python]
--------------------------------------------------------------------------------
@GET(re.compile(r'/request/\d+'))
def request(self, request, uri, headers):
    """Return a request XML description."""
    request_id = re.search(r'(\d+)', uri).group(1)
    response = (404, headers, '<result>Not found</result>')
    try:
        template = string.Template(self._fixture(uri))
        response = (200, headers, template.substitute(self.requests[request_id]))
    except Exception as e:
        if DEBUG:
            print uri, e

    if DEBUG:
        print 'REQUEST', uri, response

    return response
--------------------------------------------------------------------------------

The method +request+ will be called when a request to /request/NUMBER
is made. The previous code will load the XML template and replace
variables with the request dictionary content.
