Skip to content

Commit

Permalink
324 - Use the official thehive v5.3.0 for integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Kamforka committed May 2, 2024
1 parent 53b202f commit abf8309
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 21 deletions.
31 changes: 25 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ If you are a first time contributor to github projects please make yourself comf
Navigate to the cloned repository's directory and install the package with development extras using pip:

```
pip install -e '.[dev]'
pip install -e .[dev]
```

This command installs the package in editable mode (`-e`) and includes additional development dependencies.
Expand Down Expand Up @@ -304,14 +304,33 @@ With pre-commit hooks in place, your changes will be automatically validated for

## Testing

> IMPORTANT NOTE: Since TheHive 5.3 the licensing constraints has been partially lifted therefore a public integrator image is available for running tests both locally and in github.
`thehive4py` primarily relies on integration tests, which are designed to execute against a live TheHive 5.x instance. These tests ensure that the library functions correctly in an environment closely resembling real-world usage.

However, due to licensing constraints with TheHive 5.x, the integration tests are currently not available for public or local use.
### Test requirements

Since the test suite relies on the existence of a live TheHive docker container a local docker engine installation is a must.
If you are unfamiliar with docker please check out the [official documentation][get-docker].

### Test setup

The test suite relies on the official [thehive-image] to create a container locally with the predefined name `thehive4py-integration-tester` which will act as a unique id.
The container will expose TheHive on a random port to make sure it causes no conflicts for any other containers which expose ports.
The suite can identify this random port by querying the container info based on the predefined name.
Once TheHive is responsive the suite will initialize the instance with a setup required by the tests (e.g.: test users, organisations, etc.).
Please note that due to this initial setup the very first test run will idle for some time to make sure everything is up and running. Any other subsequent runs' statup time should be significantly faster.

### Testing locally
To execute the whole test suite locally one can use the `scripts/ci.py` utility script like:

To ensure code quality and prevent broken code from being merged, a private image is available for the integration-test workflow. This means that any issues should be detected and addressed during the PR phase.
./scripts/ci.py --test

The project is actively working on a solution to enable developers to run integration tests locally, providing a more accessible and comprehensive testing experience.
Note however that the above will execute the entire test suite which can take several minutes to complete.
In case one wants to execute only a portion of the test suite then the easiest workaround is to use `pytest` and pass the path to the specific test module. For example to only execute tests for the alert endpoints one can do:

While local testing is in development, relying on the automated PR checks ensures the reliability and quality of the `thehive4py` library.
pytest -v tests/test_alert_endpoint.py

[query-api-docs]: https://docs.strangebee.com/thehive/api-docs/#operation/Query%20API
[get-docker]: https://docs.docker.com/get-docker/
[query-api-docs]: https://docs.strangebee.com/thehive/api-docs/#operation/Query%20API
[thehive-image]: https://hub.docker.com/r/strangebee/thehive
10 changes: 5 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from tests.utils import TestConfig, reinit_hive_container, spawn_hive_container
from tests.utils import TestConfig, reset_hive_instance, spawn_hive_container
from thehive4py.client import TheHiveApi
from thehive4py.helpers import now_to_ts
from thehive4py.types.alert import InputAlert, OutputAlert
Expand All @@ -23,8 +23,8 @@
@pytest.fixture(scope="session")
def test_config():
return TestConfig(
image_name="kamforka/thehive4py-integrator:thehive-5.2.11",
container_name="thehive4py-integration-tests",
image_name="strangebee/thehive:5.3.0",
container_name="thehive4py-integration-tester",
user="[email protected]",
password="secret",
admin_org="admin",
Expand All @@ -34,8 +34,8 @@ def test_config():


@pytest.fixture(scope="function", autouse=True)
def init_hive_container(test_config: TestConfig):
reinit_hive_container(test_config=test_config)
def auto_reset_hive_instance(thehive: TheHiveApi, test_config: TestConfig):
reset_hive_instance(hive_url=thehive.session.hive_url, test_config=test_config)


@pytest.fixture(scope="session")
Expand Down
2 changes: 2 additions & 0 deletions tests/test_case_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ def test_share_and_unshare(self, thehive: TheHiveApi, test_case: OutputCase):
thehive.case.unshare(case_id=test_case["_id"], organisation_ids=[organisation])
assert len(thehive.case.list_shares(case_id=test_case["_id"])) == 1

@pytest.mark.skip(reason="integrator container only supports a single org ")
def test_share_and_remove_share(self, thehive: TheHiveApi, test_case: OutputCase):
organisation = "share-org"
share: InputShare = {"organisation": organisation}
Expand All @@ -220,6 +221,7 @@ def test_update_share(self, thehive: TheHiveApi, test_case: OutputCase):
updated_share = thehive.case.share(case_id=test_case["_id"], shares=[share])[0]
assert updated_share["profileName"] == update_profile

@pytest.mark.skip(reason="integrator container only supports a single org ")
def test_share_and_set_share(self, thehive: TheHiveApi, test_case: OutputCase):
organisation = "share-org"
share: InputShare = {"organisation": organisation}
Expand Down
3 changes: 2 additions & 1 deletion tests/test_custom_field_endpoint.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import pytest

from thehive4py.client import TheHiveApi
from thehive4py.errors import TheHiveError
from thehive4py.types.custom_field import InputUpdateCustomField, OutputCustomField


class TestCustomeFieldEndpoint:
class TestCustomFieldEndpoint:
def test_create_and_list(self, thehive_admin: TheHiveApi):
created_custom_field = thehive_admin.custom_field.create(
custom_field={
Expand Down
1 change: 1 addition & 0 deletions tests/test_user_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def test_delete(self, thehive: TheHiveApi, test_user: OutputUser):
with pytest.raises(TheHiveError):
thehive.user.get(user_id=user_id)

@pytest.mark.skip(reason="integrator container only supports a single org ")
def test_set_organisations(
self, test_config: TestConfig, thehive: TheHiveApi, test_user: OutputUser
):
Expand Down
60 changes: 51 additions & 9 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import requests

from thehive4py.client import TheHiveApi
from thehive4py.helpers import now_to_ts
from thehive4py.query.filters import Eq


Expand All @@ -27,7 +28,7 @@ class TestConfig:

def _is_container_responsive(container_url: str) -> bool:
COOLDOWN = 1.0
TIMEOUT = 60.0
TIMEOUT = 120.0

now = time.time()
end = now + TIMEOUT
Expand Down Expand Up @@ -85,7 +86,7 @@ def _destroy_container(container_name: str):
)


def _reinit_hive_org(hive_url: str, test_config: TestConfig, organisation: str) -> None:
def _reset_hive_org(hive_url: str, test_config: TestConfig, organisation: str) -> None:
client = TheHiveApi(
url=hive_url,
username=test_config.user,
Expand All @@ -101,7 +102,7 @@ def _reinit_hive_org(hive_url: str, test_config: TestConfig, organisation: str)
executor.map(client.case.delete, [case["_id"] for case in cases])


def _reinit_hive_admin_org(hive_url: str, test_config: TestConfig) -> None:
def _reset_hive_admin_org(hive_url: str, test_config: TestConfig) -> None:
client = TheHiveApi(
url=hive_url,
username=test_config.user,
Expand Down Expand Up @@ -129,6 +130,45 @@ def _reinit_hive_admin_org(hive_url: str, test_config: TestConfig) -> None:
)


def init_hive_instance(url: str, test_config: TestConfig):
hive = TheHiveApi(
url=url,
username=test_config.user,
password=test_config.password,
organisation="admin",
)

current_user = hive.user.get_current()

current_license = hive.session.make_request("GET", "/api/v1/license/current")
if current_license["fallback"]["expiresAt"] < now_to_ts():
_destroy_container(container_name=test_config.container_name)
spawn_hive_container(test_config=test_config)

if not len(hive.organisation.find(filters=Eq("name", test_config.main_org))):
hive.organisation.create(
organisation={
"name": test_config.main_org,
"description": "main organisation for testing",
}
)

hive.user.set_organisations(
user_id=current_user["_id"],
organisations=[
{
"organisation": test_config.main_org,
"profile": "org-admin",
"default": True,
},
{
"organisation": "admin",
"profile": "admin",
},
],
)


def spawn_hive_container(test_config: TestConfig) -> str:
if not _is_container_exist(container_name=test_config.container_name):
_run_container(
Expand All @@ -141,15 +181,17 @@ def spawn_hive_container(test_config: TestConfig) -> str:
_destroy_container(container_name=test_config.container_name)
raise RuntimeError("Unable to startup test container for TheHive")

init_hive_instance(url=url, test_config=test_config)

return url


def reinit_hive_container(test_config: TestConfig) -> None:
hive_url = spawn_hive_container(test_config=test_config)
def reset_hive_instance(hive_url: str, test_config: TestConfig) -> None:
# TODO: add back share config reinitialization once the license allows it
with ThreadPoolExecutor() as executor:
for organisation in [
for org in [
test_config.main_org,
test_config.share_org,
# test_config.share_org,
]:
executor.submit(_reinit_hive_org, hive_url, test_config, organisation)
executor.submit(_reinit_hive_admin_org, hive_url, test_config)
executor.submit(_reset_hive_org, hive_url, test_config, org)
executor.submit(_reset_hive_admin_org, hive_url, test_config)

0 comments on commit abf8309

Please sign in to comment.