Skip to content

Commit

Permalink
Use pytest for the unit tests (#105)
Browse files Browse the repository at this point in the history
* Use pytest for the unit tests

* Address PR comments

* Rework status check for some tests

* Add param ids for test_relations

* Simplify unit tests

* Fixes

* Use handle_exec in unit tests

* Remove left overs

* Fix

---------

Co-authored-by: arturo-seijas <[email protected]>
  • Loading branch information
nrobinaubertin and arturo-seijas authored Sep 22, 2023
1 parent 92c9b1e commit cae4ea1
Show file tree
Hide file tree
Showing 6 changed files with 733 additions and 748 deletions.
3 changes: 1 addition & 2 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import typing
from collections import defaultdict, namedtuple

import ops.lib
import ops
from charms.data_platform_libs.v0.data_interfaces import (
DatabaseCreatedEvent,
DatabaseEndpointsChangedEvent,
Expand All @@ -27,7 +27,6 @@
from database import DatabaseHandler

logger = logging.getLogger(__name__)
pgsql = ops.lib.use("pgsql", 1, "[email protected]")

S3Info = namedtuple("S3Info", ["enabled", "region", "bucket", "endpoint"])

Expand Down
2 changes: 0 additions & 2 deletions tests/unit/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
# Copyright 2023 Canonical Ltd.
# See LICENSE file for licensing details.

"""Ops testing settings."""
61 changes: 0 additions & 61 deletions tests/unit/_patched_charm.py

This file was deleted.

132 changes: 132 additions & 0 deletions tests/unit/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Copyright 2023 Canonical Ltd.
# See LICENSE file for licensing details.

"""helpers for the unit test."""

import contextlib
import typing
import unittest.mock

from ops.model import Container
from ops.testing import Harness

from charm import DiscourseCharm

DATABASE_NAME = "discourse"


def start_harness(
*,
with_postgres: bool = True,
with_redis: bool = True,
with_ingress: bool = False,
with_config: typing.Optional[typing.Dict[str, typing.Any]] = None,
):
"""Start a harness discourse charm.
This is also a workaround for the fact that Harness
doesn't reinitialize the charm as expected.
Ref: https://github.com/canonical/operator/issues/736
Args:
- with_postgres: should a postgres relation be added
- with_redis: should a redis relation be added
- with_ingress: should a ingress relation be added
- with_config: apply some configuration to the charm
Returns: a ready to use harness instance
"""
harness = Harness(DiscourseCharm)
harness.begin_with_initial_hooks()

# We catch all exec calls to the container by default
harness.handle_exec("discourse", [], result=0)

if with_postgres:
_add_postgres_relation(harness)

if with_redis:
_add_redis_relation(harness)
harness.framework.reemit()

if with_ingress:
_add_ingress_relation(harness)

if with_config is not None:
harness.update_config(with_config)
harness.container_pebble_ready("discourse")

return harness


@contextlib.contextmanager
def _patch_setup_completed():
"""Patch filesystem calls in the _is_setup_completed and _set_setup_completed functions."""
setup_completed = False

def exists(*_args, **_kwargs):
return setup_completed

def push(*_args, **_kwargs):
nonlocal setup_completed
setup_completed = True

with unittest.mock.patch.multiple(Container, exists=exists, push=push):
yield


def _add_postgres_relation(harness):
"""Add postgresql relation and relation data to the charm.
Args:
- A harness instance
Returns: the same harness instance with an added relation
"""

relation_data = {
"database": DATABASE_NAME,
"endpoints": "dbhost:5432,dbhost-2:5432",
"password": "somepasswd", # nosec
"username": "someuser",
}

# get a relation ID for the test outside of __init__
harness.db_relation_id = ( # pylint: disable=attribute-defined-outside-init
harness.add_relation("database", "postgresql")
)
harness.add_relation_unit(harness.db_relation_id, "postgresql/0")
harness.update_relation_data(
harness.db_relation_id,
"postgresql",
relation_data,
)


def _add_redis_relation(harness):
"""Add redis relation and relation data to the charm.
Args:
- A harness instance
Returns: the same harness instance with an added relation
"""
redis_relation_id = harness.add_relation("redis", "redis")
harness.add_relation_unit(redis_relation_id, "redis/0")
# We need to bypass protected access to inject the relation data
# pylint: disable=protected-access
harness.charm._stored.redis_relation = {
redis_relation_id: {"hostname": "redis-host", "port": 1010}
}


def _add_ingress_relation(harness):
"""Add ingress relation and relation data to the charm.
Args:
- A harness instance
Returns: the same harness instance with an added relation
"""
nginx_route_relation_id = harness.add_relation("nginx-route", "ingress")
harness.add_relation_unit(nginx_route_relation_id, "ingress/0")
Loading

0 comments on commit cae4ea1

Please sign in to comment.