Database¶
About¶
We use CouchDB because it's a simple document-oriented NoSQL database. The database has two main purposes:
- Saving test report;
- Data synchronization with the web interface. Data synchronization uses the replication mechanism of CouchDB and PouchDB.
The CouchDB version must be equal to or greater than the 3.2 version.
CouchDB instance¶
This section explains how to launch and manage a CouchDB instance. After launching the database, it becomes available at the following address:
The internal settings of the database are contained in the couchDB.ini configuration file. It contains settings that define the behavior and operating parameters of the database.
Running CouchDB with Docker Compose¶
An example configuration for running CouchDB via Docker Compose is located in
the example/database/couchdb
folder.
A shortened version of the instructions is described below.
- Create a
docker
directory in the project's root directory. - Create a
couchdb.ini
file in thedocker
directory.
[chttpd]
enable_cors=true
[cors]
origins = *
methods = GET, PUT, POST, HEAD, DELETE
credentials = true
headers = accept, authorization, content-type, origin, referer, x-csrf-token
docker-compose.yaml
file in project's root directory.
version: "3.8"
services:
couchserver:
image: couchdb:3.4
ports:
- "5984:5984"
environment:
COUCHDB_USER: dev
COUCHDB_PASSWORD: dev
volumes:
- ./docker/dbdata:/opt/couchdb/data
- ./docker/couchdb.ini:/opt/couchdb/etc/local.ini
docker compose up
docker compose down
Running CouchDB with Docker¶
- Create
couchdb.ini
file.
[chttpd]
enable_cors=true
[cors]
origins = *
methods = GET, PUT, POST, HEAD, DELETE
credentials = true
headers = accept, authorization, content-type, origin, referer, x-csrf-token
docker run --rm --name couchdb -p 5984:5984 -e COUCHDB_USER=dev -e COUCHDB_PASSWORD=dev -v ./couchdb.ini:/opt/couchdb/etc/local.ini couchdb:3.4
Command for Windows:
docker run --rm --name couchdb -p 5984:5984 -e COUCHDB_USER=dev -e COUCHDB_PASSWORD=dev -v .\couchdb.ini:/opt/couchdb/etc/local.ini couchdb:3.4
The container will be deleted after use.
Running CouchDB with binary packages in Linux¶
- Use this instruction to install CouchDB
- The installer asks you if you want to install CouchDB as a standalone
application or in a clustered configuration.
Select
Standalone
and press Enter. - You are prompted to enter the Erlang Node Name.
You can ask it in Terminal with the command
hostname -f
- Set the Erlang Magic Cookie.
This is a unique identifier, for example,
test1234
- Configure the network interfaces on which CouchDB will be bound.
localhost
is fine. - Enter an admin password of your choice for CouchDB, press
Enter
, re-type the password and pressEnter
again to continue the installation. - After launching the database, it becomes available at the following address http://localhost:5984/_utils/. Open it.
- First of all, in the
User Management
section in theCreate Admins
tab, create a user with the logindev
and passworddev
- In the
Config
chooseCORS
and appointEnable CORS
withAll domains
To disable the CouchDB service:¶
Remove packages:
sudo apt remove --purge couchdb
sudo rm /usr/share/keyrings/couchdb-archive-keyring.gpg
sudo rm /etc/apt/sources.list.d/couchdb.list
sudo apt clean
systemctl stop couchdb.service
systemctl disable couchdb.service
systemctl daemon-reload
systemctl reset-failed
Running CouchDB with binary packages in Windows¶
- Use this instruction to install CouchDB
- Be sure to install CouchDB to a path with no spaces, such as
C:\CouchDB
. - Create a user with the login
dev
and passworddev
during the installation steps. Validate Credentials. - Generate Random Cookie.
- After launching the database, it becomes available at the following address http://localhost:5984/_utils/. Open it.
- In the
Config
chooseCORS
and appointEnable CORS
withAll domains
Database in pytest-hardpy¶
Description of databases¶
The pytest plugin has 2 databases: statestore and runstore.
- The statestore database contains the document current, which is a JSON object that stores the current state of the test run without artifacts. The plugin updates the document as testing progresses using the StateStore class.
- The runstore database contains the document current, which is a JSON object that stores the current state of the test run with artifacts - a report on the current test run.
- The plugin updates the document as testing progresses using the RunStore class.
A separate database is required to store the list of reports.
The report database is used as an example of storing reports on past testing runs.
It can be launched in the same instance as the statestore, runstore database, or in a different one.
The database is accessed through the CouchdbLoader class, which can be called at the end of each launch.
To read the current report, use the get_current_report()
function.
Sample code for saving a report at the end of testing:
# conftest.py
def save_report_to_couchdb():
report = get_current_report()
if report:
loader = CouchdbLoader(CouchdbConfig())
loader.load(report)
@pytest.fixture(scope="session", autouse=True)
def fill_actions_after_test(post_run_functions: list):
post_run_functions.append(save_report_to_couchdb)
yield
HardPy users have the flexibility to choose their preferred database. They can write custom classes to record reports at the end of testing.
Statestore scheme¶
The current document of the statestore database contains the following fields:
- _rev: current document revision;
- _id: unique document identifier;
- progress: test progress;
- stop_time: end time of testing in Unix seconds;
- start_time: testing start time in Unix seconds;
- status: test execution status;
- name: test suite name;
- dut: DUT information containing the serial number and additional information;
- test_stand: information about the test stand in the form of a dictionary;
- modules: module information;
- operator_msg: operator message.
The test_stand block containt the following fields:
- name - test stand name;
- drivers: information about drivers in the form of a dictionary;
- info: a dictionary containing additional information about the test stand;
- timezone: timezone as a string;
- location: test stand location;
- hw_id: test stand machine id (GUID) or MAC address.
The dut block contains the following fields:
- serial_number: DUT serial number;
- part_number: DUT part number;
- info: a dictionary containing additional information about the DUT, such as batch, board revision, etc.
The operator_msg block contains the following fields:
- msg: message for operator;
- title: the title of operator message dialog box;
- visible: should a message be displayed on the operator panel;
- id: operator message id;
- font_size: operator message font size.
The modules block contains the following fields:
- test_{module_name}: an object containing information about a specific module. Contains the following fields:
- status: module test execution status;
- name: module name, by default the same as module_id;
- start_time: module testing start time in Unix seconds;
- stop_time: end time of module testing in Unix seconds;
- cases: an object containing information about each test case within the module. Contains the following fields:
- test_{test_name}: an object containing information about the test. Contains the following fields:
- status: test execution status;
- name: test name, by default the same as case_id;
- start_time: test start time in Unix seconds;
- stop_time: test end time in Unix second;
- assertion_msg: error message if the test fails;
- msg: additional message;
- attempt: attempt counting to pass the case;
- dialog_box: information about dialog box;
- title_bar: title bar of the dialog box;
- dialog_text: text displayed in the dialog box;
- widget: information about the widget;
- info: widget additional information;
- type: type of the widget;
- image: information about image;
- address: image address;
- width: image width in percent;
- border: image border in pixels;
- base64: image in base64 code;
- visible: should a dialog box be displayed on the operator panel;
- id: dialog box id;
- font_size: dialog box font size.
Example of a current document:
{
"_rev": "44867-3888ae85c19c428cc46685845953b483",
"_id": "current",
"progress": 100,
"stop_time": 1695817266,
"start_time": 1695817263,
"status": "failed",
"name": "hardpy-stand",
"dut": {
"serial_number": "92c5a4bb-ecb0-42c5-89ac-e0caca0919fd",
"part_number": "part_1",
"info": {
"batch": "test_batch",
"board_rev": "rev_1"
}
},
"test_stand": {
"hw_id": "840982098ca2459a7b22cc608eff65d4",
"name": "test_stand_1",
"info": {
"geo": "Belgrade"
},
"timezone": "Europe/Belgrade",
"drivers": {
"driver_1": "driver info",
"driver_2": {
"state": "active",
"port": 8000
}
},
"location": "Belgrade_1"
},
"operator_msg": {
"msg": "Operator message",
"title": "Message",
"visible": true,
"id": "f45ac1e7-2ce8-4a6b-bb9d-8863e30bcc78"
},
"modules": {
"test_1_a": {
"status": "failed",
"name": "Module 1",
"start_time": 1695816884,
"stop_time": 1695817265,
"cases": {
"test_dut_info": {
"status": "passed",
"name": "DUT info ",
"start_time": 1695817263,
"stop_time": 1695817264,
"assertion_msg": null,
"msg": null,
"attempt": 1,
"dialog_box": {
"title_bar": "Example of text input",
"dialog_text": "Type some text and press the Confirm button",
"widget": {
"info": {
"text": "some text"
},
"type": "textinput"
},
"image": {
"address": "assets/test.png",
"width": 100,
"border": 0,
},
"visible": true,
"id": "b57ab1e7-8cf8-4a6a-bb9d-8863ea0bcc78"
}
},
"test_minute_parity": {
"status": "failed",
"name": "Test 1",
"start_time": 1695817264,
"stop_time": 1695817264,
"assertion_msg": "The test failed because minute 21 is odd! Try again!",
"attempt": 1,
"msg": [
"Current minute 21"
]
}
}
}
}
}
Runstore scheme¶
The runstore database is similar to statestore database, but there are differences:
- runstore contains the artifact field for test run, module, and case;
- runstore does not contain dialog_box and attempt fields.
The current document of runstore database contains the following fields:
- _rev: current document revision;
- _id: unique document identifier;
- stop_time: end time of testing in Unix seconds;
- start_time: testing start time in Unix seconds;
- status: test execution status;
- name: test suite name;
- dut: DUT information containing the serial number and additional information;
- test_stand: information about the test stand in the form of a dictionary;
- artifact: an object containing information about artifacts created during the test run;
- modules: module information.
The test_stand block containt the following fields:
- name - test stand name;
- drivers: information about drivers in the form of a dictionary;
- info: a dictionary containing additional information about the test stand;
- timezone: timezone as a string;
- location: test stand location;
- hw_id: test stand machine id (GUID) or MAC address.
The dut block contains the following fields:
- serial_number: DUT serial number;
- part_number: DUT part number;
- info: A dictionary containing additional information about the DUT, such as batch, board revision, etc.
The modules block contains the following fields:
- test_{module_name}: an object containing information about a specific module. Contains the following fields:
- status: module test execution status;
- name: module name, by default the same as module_id;
- start_time: module testing start time in Unix seconds;
- stop_time: end time of module testing in Unix seconds;
- artifact: an object containing information about artifacts created during the test module process;
- cases: an object containing information about each test within the module. Contains the following fields:
- test_{test_name}: an object containing information about the test. Contains the following fields:
- status: test execution status;
- name: test name, by default the same as case_id;
- start_time: test start time in Unix seconds;
- stop_time: test end time in Unix second;
- assertion_msg: error message if the test fails;
- msg: additional message;
- artifact: an object containing information about artifacts created during the test case process.
Example of a current document:
{
"_rev": "44867-3888ae85c19c428cc46685845953b483",
"_id": "current",
"stop_time": 1695817266,
"start_time": 1695817263,
"status": "failed",
"name": "hardpy-stand",
"dut": {
"serial_number": "92c5a4bb-ecb0-42c5-89ac-e0caca0919fd",
"part_number": "part_1",
"info": {
"batch": "test_batch",
"board_rev": "rev_1"
}
},
"test_stand": {
"hw_id": "840982098ca2459a7b22cc608eff65d4",
"name": "test_stand_1",
"info": {
"geo": "Belgrade"
},
"timezone": "Europe/Belgrade",
"drivers": {
"driver_1": "driver info",
"driver_2": {
"state": "active",
"port": 8000
}
},
"location": "Belgrade_1"
},
"artifact": {},
"modules": {
"test_1_a": {
"status": "failed",
"name": "Module 1",
"start_time": 1695816884,
"stop_time": 1695817265,
"artifact": {},
"cases": {
"test_dut_info": {
"status": "passed",
"name": "DUT info",
"start_time": 1695817263,
"stop_time": 1695817264,
"assertion_msg": null,
"msg": null,
"artifact": {}
},
"test_minute_parity": {
"status": "failed",
"name": "Test 1",
"start_time": 1695817264,
"stop_time": 1695817264,
"assertion_msg": "The test failed because minute 21 is odd! Try again!",
"msg": [
"Current minute 21"
],
"artifact": {
"data_str": "123DATA",
"data_int": 12345,
"data_dict": {
"test_key": "456DATA"
}
}
}
}
}
}
}