Do a functional doctest test on the app.
========================================

Let's first create an instance of gocept.lms.LMS at the top level:

>>> import gocept.lms.app
>>> root = getRootFolder()
>>> import zope.app.component.hooks
>>> old_site = zope.app.component.hooks.getSite()
>>> zope.app.component.hooks.setSite(root)

>>> root['app'] = gocept.lms.app.LMS()
>>> zope.app.component.hooks.setSite(root['app'])


Local utilities
---------------

>>> urls = zope.component.getUtility(gocept.lms.interfaces.IURLProvider)
>>> urls
<gocept.lms.url.URLContainer object at 0x...>

>>> clients = zope.component.getUtility(gocept.lms.interfaces.IClientProvider)
>>> clients
<gocept.lms.client.ClientContainer object at 0x...>


Clients
-------

>>> clients.add(gocept.lms.client.Client('fred'))
>>> fred = clients.get('fred')
>>> fred
<gocept.lms.client.Client object at 0x...>

>>> clients.get('asdf') is None
True

URLs
----

>>> fred.register_urls([urls.add('http://example.com/'),
...                     urls.add('http://example.com/asdf')])
>>> list(sorted(fred.iter_urls()))
[<gocept.lms.url.URL 'http://example.com/'>,
 <gocept.lms.url.URL 'http://example.com/asdf'>]

We can now find out that Fred is actually using both URLs:

>>> example = urls.get_url('http://example.com/')
>>> example.clients
InstrumentedSet([u'/app/ClientContainer/fred'])
>>> list(example.clients)
[<gocept.lms.client.Client object at 0x...>]
>>> len(list(example.clients))
1

The URLs can be unregistered using the ``unreigster_urls`` method. When an URL
is passed which is not registered for the client no error will be raised:

>>> fred.unregister_urls(
...     [urls.get_url('http://example.com/'),
...      urls.add('http://not-registered-yet/')])
>>> list(fred.iter_urls())
[<gocept.lms.url.URL 'http://example.com/asdf'>]

The URL http://example.com/ has no client now:

>>> list(example.clients)
[]

But /asdf still has
>>> list(urls.get_url('http://example.com/asdf').clients)
[<gocept.lms.client.Client object at 0x...>]


Clean up
--------

>>> zope.app.component.hooks.setSite(old_site)


Run tests in the testbrowser
----------------------------

The zope.testbrowser.browser module exposes a Browser class that
simulates a web browser similar to Mozilla Firefox or IE.  We use that
to test how our application behaves in a browser.  For more
information, see http://pypi.python.org/pypi/zope.testbrowser.

Create a browser and visit the instance you just created:

>>> from zope.testbrowser.testing import Browser
>>> browser = Browser()
>>> browser.open('http://localhost/app')

Check some basic information about the page you visit:

>>> browser.url
'http://localhost/app'
>>> browser.headers.get('Status').upper()
'200 OK'


Adding clients
==============

Adding clients requires authorization:

>>> browser.open('http://localhost/app/ClientContainer/addclient')
Traceback (most recent call last):
    ...
httperror_seek_wrapper: HTTP Error 401: Unauthorized

>>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
>>> browser.open('http://localhost/app/ClientContainer/addclient')
>>> browser.getControl('Id').value = 'testid'
>>> browser.getControl('Password').value = 'asdf'
>>> browser.getControl('Contact name').value = 'Hans Meiser'
>>> browser.getControl('Contact email').value = 'hans@meiser.de'
>>> browser.getControl('Callback').value = 'http://hans.meiser.de/foo'
>>> browser.getControl('Add').click()

Make sure the client is registered:

>>> list(clients)
[u'fred', u'testid']
>>> test = clients.get('testid')
>>> test.id
u'testid'
>>> test.password
u'asdf'
>>> test.contact_name
u'Hans Meiser'
>>> test.contact_email
u'hans@meiser.de'
>>> test.callback
'http://hans.meiser.de/foo'


Managing URLs
=============

There is a small UI that allows some management of URLs: listing which ones
are there and purging individual ones.

>>> from zope.app.testing.xmlrpc import ServerProxy
>>> rpc = ServerProxy('http://localhost/app/')
>>> rpc.registerURLs('testid', 'asdf',
...                   ['http://example.com/foo1', 'http://example.com/foo2'])
[]

>>> browser.open('http://localhost/app')
>>> browser.getLink('manage URLs').click()

As we expect tens of thousands or even hundreds of thousands of URLs to be
managed in an LMS, we don't show any URLs initially, but require the user to
search:

>>> print browser.contents
<html>
...
        <h1>Manage URLs</h1>
...
            <input type="submit" name="search" value="Search" />
...
        <table>
            <tr>
                <th>URL</th>
                <th>Status</th>
                <th>Clients</th>
                <th>&nbsp;</th>
            </tr>
        </table>
...

The search is a simple substring search which will return all URLs that
containe the string given by the user:

>>> browser.getControl('URL pattern').value = 'example.com'
>>> browser.getControl('Search').click()
>>> print browser.contents
<html>
...
        <table>
            <tr>
                <th>URL</th>
                <th>Status</th>
                <th>Clients</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>http://example.com/asdf</td>
                <td></td>
                <td>1</td>
                <td><a href="http://localhost/app/URLContainer/aHR0cDovL2V4YW1wbGUuY29tL2FzZGY%3D/purge">purge</a></td>
            </tr>
            <tr>
                <td>http://example.com/foo1</td>
                <td></td>
                <td>1</td>
                <td><a href="http://localhost/app/URLContainer/aHR0cDovL2V4YW1wbGUuY29tL2ZvbzE%3D/purge">purge</a></td>
            </tr>
            <tr>
                <td>http://example.com/foo2</td>
                <td></td>
                <td>1</td>
                <td><a href="http://localhost/app/URLContainer/aHR0cDovL2V4YW1wbGUuY29tL2ZvbzI%3D/purge">purge</a></td>
            </tr>
            <tr>
                <td>http://example.com/</td>
                <td></td>
                <td>0</td>
                <td><a href="http://localhost/app/URLContainer/aHR0cDovL2V4YW1wbGUuY29tLw%3D%3D/purge">purge</a></td>
            </tr>
        </table>
...


Individual URLs can be purged from the database. They get unregistered from the
clients and deleted from the database:

>>> browser.getControl('URL pattern').value = 'foo1'
>>> browser.getControl('Search').click()
>>> print browser.contents
<html>
...
        <table>
            <tr>
                <th>URL</th>
                <th>Status</th>
                <th>Clients</th>
                <th>&nbsp;</th>
            </tr>
            <tr>
                <td>http://example.com/foo1</td>
                <td></td>
                <td>1</td>
                <td><a href="http://localhost/app/URLContainer/aHR0cDovL2V4YW1wbGUuY29tL2ZvbzE%3D/purge">purge</a></td>
            </tr>
        </table>
...
>>> browser.getLink('purge').click()

We can see that the URL was purged by searching for it again and see that it
doesn't show up again:

>>> browser.getControl('URL pattern').value = 'foo1'
>>> browser.getControl('Search').click()
>>> print browser.contents
<html>
...
        <table>
            <tr>
                <th>URL</th>
                <th>Status</th>
                <th>Clients</th>
                <th>&nbsp;</th>
            </tr>
        </table>
...
