canonical / data-platform-libs

A collection of charm libraries curated by the Data Platform Team
https://charmhub.io/data-platform-libs
Apache License 2.0
10 stars 9 forks source link

Running ops.testing or ops.scenario tests fails when a charm uses the DatabaseRequires class #80

Open gnuoy opened 1 year ago

gnuoy commented 1 year ago

When using the relation_aliases option dynamic events are created. For some reason unlike other events these seem to persist across test invocations and cause event type duplication errors. I'm raising this against this interface as it's the only place I see this issue but it's possible the root cause of the bug is in ops.

Steps to reproduce

Run a ops.scenario (or ops.testing) pytest test which uses DatabaseRequires with the relations_aliases option. For example:

import pytest
from scenario import State, Context, Container, Relation
from ops.charm import CharmBase
from ops.model import UnknownStatus

from charms.data_platform_libs.v0.database_requires import (
    DatabaseRequires,
    DatabaseCreatedEvent,
)

class ApplicationCharm(CharmBase):
    def __init__(self, *args):
        super().__init__(*args)
        self.database = DatabaseRequires(
            self,
            relation_name="database",
            database_name="database",
            relations_aliases=["an_alias"],
        )
        self.framework.observe(
            self.database.on.database_created, self._on_database_created
        )

    def _on_database_created(self, event: DatabaseCreatedEvent) -> None:
        self.status.set(ActiveStatus("received database credentials"))

@pytest.mark.parametrize("leader", (True, False))
def test_charm_no_deps(leader):
    metadata = {
        "name": "myapp",
        "version": "3",
        "subordinate": False,
        "requires": {"database": {"interface": "mysql_client", "limit": 1}},
    }
    state = State(
        leader=leader,
        config={},
        containers=[],
        relations=[
            Relation(
                endpoint="database",
                interface="mysql_client",
                remote_app_name="mysql",
                local_unit_data={},
                remote_app_data={},
            )
        ],
    )
    ctxt = Context(charm_type=ApplicationCharm, meta=metadata)
    out = ctxt.run("config-changed", state)
    assert out.unit_status == UnknownStatus()

Expected behavior

2 passed in Xs

Actual behavior

...
E               RuntimeError: unable to define an event with event_kind that overlaps with an existing type <class 'charms.data_platform_libs.v0.database_requires.DatabaseEvents'> attribute: an_alias_database_created
...
1 failed, 1 passed in Xs

Versions

ops: 2.4.1
ops-scenario: 4.0.4
charms.data_platform_libs.v0.database_requires: 
    LIBID = "0241e088ffa9440fb4e3126349b2fb62"
    LIBAPI = 0
    LIBPATCH = 4
github-actions[bot] commented 1 year ago

https://warthogs.atlassian.net/browse/DPE-2287

carlcsaposs-canonical commented 1 year ago

Are you able to reproduce this issue with other custom events? (e.g. tls_certificates lib or cos_agent lib)

gnuoy commented 1 year ago

Are you able to reproduce this issue with other custom events? (e.g. tls_certificates lib or cos_agent lib)

tbh I'm testing with lots of interfaces which define their own events without issue but the difference seems to be that none of them do it dynamically. I'm really suspicious of the call to self.on.define_event and neither of those libs have a call to that. Either way, I gave it a try with tls_certificates and it worked fine https://paste.ubuntu.com/p/MXxM7SdC88/