= Imprimatur
:toc:
:toclevels: 1


Imprimatur is a tool for performing automated functional testing on web
applications. The tests are described in a simple test script. Along with the
standard HTTP methods, Imprimatur handles authentication, file uploads and
HTTPS. The responses are validated using regular expressions.
 
Imprimatur runs on Python 3.5+ on both CPython and PyPy.


== Licence

Imprimatur is released under the
https://www.gnu.org/licenses/gpl-3.0.en.html[GPL].


== Installation

It's a good idea to set up a virtualenv:

 virtualenv venv
 source venv/bin/activate

then install Imprimatur with pip:

 pip install imprimatur


== Quickstart

To run an example test script save the following as a file called `test.py`:

 [
   {
     'host': 'www.google.com',
     'status_code': 200
   }
 ] 

then run it with:

 imprimatur test.py

and it should come back saying all tests are passed. Here's another script with
some more examples:

 [
     {
         'name': "Single regex",
         'port': 5000,
         'host': "localhost",
         'path': "/text_2",
         'status_code': 200,
         'regexes': ['inexhaustible']
     },
     {
         'name': "Basic auth",
         'path': "/auth",
         'auth': ("conrad", "kurtz"),
         'status_code': 200
     },
     {
         'name': "File upload",
         'path': "/echo",
         'method': "post",
         'files': {"yellow": "tests/2/crome-file.txt"},
         'regexes': ["yellow", "on the leads"],
         'status_code': 200
     },
     {
         'name': "Post name and value with regex.",
         'path': "/echo",
         'method': "post",
         'data': {"quote": "Leisure is the mother of philosophy."},
         'status_code': 200,
         'regexes': ["Leisure"]
     },
     {
         'name': "Repeat until successful.",
         'path': "/counter",
         # 'max' is maximum number of times to try
         # 'period' is the number of seconds to wait between tries
         'tries': {'max': 3, 'period': 1},
         'status_code': 200,
         'regexes': ["The Decline And Fall Of The Roman Empire"]
     },
     {
         'name': "Regex on header.",
         'path': "/redirect?location=http://localhost:5000/here.html",
         'status_code': 302,
         'regexes': ["here.html"]
     },
     {
         'name': "HTTP HEAD request.",
         'path': "/text_1",
         'method': "head",
         'status_code': 200
     },
     {
         'name': "Allow redirects",
         'path': "/text_1",
         'allow_redirects': 'True',
         'status_code': 200
     }
 ]


== Reference

The full list of properties for each request is given below. A * after the
property name means that it is carried over to subsequent requests.

|===
| Name          | Notes

| scheme*
| Can be `'http'` (default) or `'https'`.

| host*
| Default is `'localhost'`.

| port*
| Default is `80`.

| auth*
| Sequence of username and password eg. `('conrad', 'kurtz')`

| verify*
| For an HTTPS request, verify the certificate. Can be `True` or `False`
  (default).

| name
| Used to label the request.

| path
| End of the URL eg. `'/retrieve?product_id=45'`. Default is `'/'`.

| method
| An HTTP method. eg. `'post'`. Default is `'get'`.

| data
| Dictionary of post values eg. `{'id': 45, 'name': 'agnez'}`

| files
| File name and path to upload. Eg. `{'yellow': 'crome.txt'}`

| tries
| Maximum number of times to try the URL. Eg. `{'max': 5, 'period': 2}`. The
  `max` value is the number of times to try, and the `period` is the number of
  seconds to wait between tries. If `max` is omitted it defaults to `10` and if
  `period` is omitted it defaults to `1`, so `{}` is equivalent to
  `{'max': 10, 'period': 1}`.

| regexes
| Sequence of https://docs.python.org/3.5/howto/regex.html[regular expressions]
  eg. `[r'go\s*ths', r'vandals']`

| status_code
| https://tools.ietf.org/html/rfc7231#section-6[HTTP status code] to
  check for, eg. `200`

| headers
| Dictionary of HTTP headers eg. `{'Accept-Charset': 'utf-8'}`

| unzip
| If `True` then unzip the body of the response.

| allow_redirects
| If `True` then follow redirects.
|===

Cookies are always retained between requests.


== Web Interface

Imprimatur comes with a very basic web interface. You can try it out using
Imprimatur's built-in web server by doing:

 python -m imprimatur.web

Don't run it on a public facing production web server, as it is entirely
unsecure.


== Release Notes

=== Version 0.24.0, 2020-04-09

- Drop support for Python 2.
- The webserver now listens for external connections.
- Upgrade dependent libraries.
- Use TravisCI for testing.


=== Version 0.23.26, 2019-01-19

- Upgrade dependecies `requests` and `flask`.


=== Version 0.23.25, 2018-04-10

- The `path` doesn't default to `/`.


=== Version 0.23.24, 2016-10-16

- The new `unzip: True` option will decompress the response.


=== Version 0.23.23, 2016-10-12

- Give a better error message if a regular expression is invalid.


=== Version 0.23.22, 2016-09-24

- Have all the docs in the readme.adoc file. This means that the Python Hosted
  site isn't used any more, since the readme.adoc file is rendered on GitHub.


=== Version 0.23.21, 2016-09-21

- Fixed bug where one couldn't send a binary file as part of an Imprimatur
  request.


=== Version 0.23.20, 2016-07-21

- In the absence of a character encoding in the response, rather than guess
  assume utf-8 and ignore any errors.


=== Version 0.23.19, 2016-07-07

- Fixed bug with custom HTTP headers.


=== Version 0.23.18, 2016-07-03

- Added support for specifying HTTP headers.


=== Version 0.23.17, 2016-05-09

- Fixed a bug where if a URL is malformed the test is skipped whereas it should
  fail.


=== Version 0.23.16, 2015-06-11

- Now holds a session across requests.


=== Version 0.23.15, 2015-05-20

- Fixes a bug where if there's no body to a response, and it has to be printed
  out, then fails.


=== Version 0.23.14, 2015-02-19

- Make sure `templates` directory is included in the distribution.


=== Version 0.23.13, 2015-02-15

- Added the `verify` flag for controlling whether to verify SSL certificates or
  not. Can be `True` or `False`, the default is `False`.


=== Version 0.23.11, 2015-02-10

- Include 'templates' directory in the distribution, this is necessary for the
  web server.

- A list of runs is now shown on the home page.

- Give a good error message if there's a syntax error in the script.

- The wheel format distribution if Imprimatur now has the 'universal' flag set
  which denotes that it runs on Python 2 and 3.


=== Version 0.23.10, 2015-02-03

- Fixed bug where regex pattern wasn't searching the headers.

- Added the re.DOTALL flag so that a `.` in regular expressions matches line
  ending characters.

- Added a rudimentary web interface.


=== Version 0.23.9, 2015-01-31

- Various improvements to the converter from old style XML test scripts to new
  style ones.

- Renamed the '`tries`' attribute '`number`' to '`max`' as it's a better
  description of what it does.

- Fixed bug where Imprimatur always retried max times, even when a request was
  successful.


=== Version 0.23.8, 2015-01-26

- The converter from old style XML test scripts to new style ones now carries
  over the comments as well.


=== Version 0.23.7, 2015-01-25

- Added a converter to convert from old style XML test scripts to new style
  Python ones.


=== Version 0.23.5, 2015-01-22

- The `status_code` attribute is now allowed to be either a `str` or an `int`.
  Previously it could only be an `int`.

- The '`Passed all tests!`' message at the end is now followed by a newline
  character.

- The `auth` attribute is now carried over from previous requests so that it
  doesn't have to be specified explicitly in each subsequent request.


=== Version 0.23.4, 2015-01-21

- Imprimatur now requires version 2.5.1 of the '`requests`' library. It was
  found that old versions of 'requests' didn't work.

- Fixed a bug where the status code check isn't working.
     
- Added in a check for extraneous keys in the test script.
 
- Included a lot more examples in the docs.


=== Version 0.23.3, 2015-01-19

- Fixed various problems with Python 3. 


=== Version 0.23.2, 2015-01-18

- Added make sure dependencies ('`flask`' and '`requests`') are automatically
  installed.


=== Version 0.23.1, 2015-01-18

- Added `imprimatur` as a command-line script that is automatically installed.


=== Version 0.23.0, 2015-01-17

- Ported to Python.
- Moved to GitHub.
- Has the same features as before, but the script format is no longer an XML
  file, but evaluatable Python.
- Can be used as a Python library.


=== Version 22

- Removed <session> element.


=== Version 20

- Added support for HTTP HEAD requests.


=== Version 18

- Gets don't follow redirects by default.
- Fixed example given in tests directory.
- If no arguments are given on the command line, throws an exception saying no
  file specified.


=== Version 17

- Can now set a request to follow redirects.


=== Version 15

- Changed so that a regex matches if it's found anywhere within the string.
- In regexes, a dot character now matches line terminators as well.


== Regression Tests

To run the regression tests, install http://testrun.org/tox/latest/[tox]:

 pip install tox


then run `tox` from the `imprimatur` directory:

 tox


== Building The Documentation

The docs are written using Asciidoctor. To build them, install asciidoctor:

 apt-get install asciidoctor

Then type:

 asciidoctor readme.adoc

and the doc will appear at `docs/index.html`.


== Doing A Release Of Imprimatur

Run `tox` make sure all tests pass, then update the release notes in
`readme.adoc` then do:

....
git tag -a x.y.z -m "version x.y.z"
rm -r build
rm -r dist
python setup.py sdist bdist_wheel
for f in dist/*; do gpg --detach-sign -a $f; done
twine upload dist/*
....
