device-registry
This project is the Device Registry Service, an example REST API web service for registering smart devices. It is written in Python using Flask, and it stores data in a SQLite database. It is the web service used for Chapter 6 in The Way To Test Software by Andrew Knight. Note that it is not a "real" web service, but rather one to use as a teaching example. This project also contains integration tests to test the REST API endpoints.
What does the Device Registry Service do? It stores records for all smart devices a user owns in one central place. A home could have multiple kinds of smart devices: WiFi routers, voice assistants, thermostats, light switches, and even appliances. This service stores information like name, location, type, model, and serial number for each device. Its API enables callers to practice CRUD (Create, Retrieve, Update, Delete) operations. In theory, a dashboard or monitoring app could use a registry service like this to quickly access devices.
Installation
The Device Registry Service is designed to run on your local machine. It should run on any operating system (Windows, macOS, Linux). To install it:
- Install Python 3.8 or higher.
- Clone the GitHub repository on to your local machine.
- Install dependency packages from the command line:
- Change directory to the project's root directory.
- Run
pip install -r requirements.txt
to install all dependencies.
Running the web service
The Device Registry Service is written using Flask.
Before running it from the command line,
set the FLASK_APP
environment variable to the name of the app's main module, registry
.
- Windows:
set FLASK_APP=registry
- macOS and Linux:
export FLASK_APP=registry
To run the web service, run flask run
.
You should see output like this from the command line:
$ flask run
* Serving Flask app 'registry' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
When running, the web service can be accessed at http://127.0.0.1:5000/
(the address printed by the flask run
output).
If you load that address in a web browser, you should see docs for the REST API.
Choosing a database
Out of the box, the Device Registry Service uses a SQLite database. SQLite is not meant to be a high-scale production database, but it works just fine for this small example web service.
There are two ways to manage the app's database:
-
Testing: create a fresh, empty, in-memory SQLite database every time
flask run
is launched. -
Development: create a SQLite database file named
registry_data.sqlite
with a few prepopulated devices.
Set the FLASK_CONFIG
environment variable to chose which database to use:
- For the Testing database, set
FLASK_CONFIG
totesting
. - For the Development database, set
FLASK_CONFIG
todevelopment
.
If FLASK_CONFIG
is not set, then the app uses the Testing database by default.
If you want to use the Development database,
you must create it before running the Flask app.
Run flask init-db
to create the initial registry_data.sqlite
file in the project's root directory.
Then, set FLASK_CONFIG
to development
and run flask run
to run the app with this database.
Any changes will persist, even after the app is restarted.
Setting configuration options
The Device Registry Service stores all its configuration options in config.py
.
The following configuration options have default values,
but they may optionally be overridden using environment variables:
-
SECRET_KEY
: the secret key used for app security -
AUTH_USERNAME1
: the username for user 1 -
AUTH_PASSWORD1
: the password for user 1 -
AUTH_USERNAME2
: the username for user 2 -
AUTH_PASSWORD2
: the password for user 2 -
AUTH_TOKEN_EXPIRATION
: the expiration time in seconds for authentication tokens
Warning: Overriding these options is not recommended for most cases.
Reading the REST API docs
The Device Registry Service provides its REST API docs at the /docs/
resource.
The home page /
also redirects to the /docs/
page.
The /docs/json/
resource provides the docs as a JSON object.
Docs are automatically generated using flask-selfdoc.
Running the test cases
REST API integration tests are located in the tests
directory.
They are written using pytest.
The tests are not unit tests -
they send requests to a live version of the Device Registry Service.
If you try to run them without the following setup steps, they will fail.
The tests require a configuration file that specifies the web service's base URL and available users.
In the tests/integration
directory, create a file named inputs.json
with the following contents:
{
"base_url": "<base-url>",
"users" : [
{
"username": "<user1-username>",
"password": "<user1-password>"
},
{
"username": "<user2-username>",
"password": "<user2-password>"
}
]
}
You will need to substitute appropriate values for the "<...>"
values.
If you run the Device Registry Service with the default values from config.py
,
then tests/integration/inputs.json
should look like this:
{
"base_url": "http://127.0.0.1:5000",
"users" : [
{
"username": "pythonista",
"password": "I<3testing"
},
{
"username": "engineer",
"password": "Muh5devices"
}
]
}
Note: tests/integration/inputs.json
is not committed to the repository
because inputs and secrets should never be committed to a publicly-shared location.
(Nevertheless, this web service is a teaching example,
so values are pasted in the section above for convenience and clarity.)
Once the inputs file is ready, configure the app to use the Testing database and run flask run
.
Then, in another command line terminal, run python -m pytest tests
.
Note that the web app must be running before launching the tests.
Here's a condensed guide for running tests:
- Set the
FLASK_APP
environment variable toregistry
. - Set the
FLASK_CONFIG
environment variable totesting
. - Run
flask run
from the project root directory. - Create the
tests/integration/inputs.json
file. - Run
python -m pytest tests
from the project root directory.
Test Outline
Remove this section upon chapter completion.
- Basic request-response-validate test for '/status/'
- Set up project
- Write the test with hard-coding
- Improve with inputs file and base URL builder
- Authentication for '/devices/'
- Demonstrate GET with and without auth & explain
- Add credentials to inputs file
- Write positive test with auth
- Write negative test without auth
- Show auth with tokens
- Show how to reuse token
- Discuss if every endpoint should test every type of auth or no-auth
- CRUD tests with '/devices/'
- Include negative cases
- Testing multiple objects with '/devices/'
- Creating and deleting multiple objects
- Query parameters for large data
- Authorization tests with '/devices/'
- Multiple users
- Multiple methods
- Additional REST API factors
- File downloads
- HEAD and OPTIONS for GET
- Invalid methods