diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 2984a71..66fd3cc 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -6,11 +6,15 @@ on: - main pull_request: +permissions: {} + jobs: app-checks: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 + with: + persist-credentials: false - name: Run framework tests run: | cd dashboard @@ -31,3 +35,19 @@ jobs: run: | cd dashboard make makemigrations-check + + charm-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 + - name: Lint + run: | + cd charm + uvx tox run -e lint + - name: Unit tests + run: | + cd charm + uvx tox run -e unit diff --git a/charm/requirements.txt b/charm/requirements.txt index d58a30c..575081c 100644 --- a/charm/requirements.txt +++ b/charm/requirements.txt @@ -1,2 +1,2 @@ -ops ~= 2.17 +ops ~= 3.7 paas-charm>=1.0,<2 diff --git a/charm/tests/unit/test_charm.py b/charm/tests/unit/test_charm.py new file mode 100644 index 0000000..3ca90cd --- /dev/null +++ b/charm/tests/unit/test_charm.py @@ -0,0 +1,58 @@ +# Copyright 2026 Canonical Ltd. +# See LICENSE file for licensing details. + +"""Unit tests for the dashboard charm's custom functionality.""" + +import pytest +from ops import testing + +import charm + +_LOADDATA_CMD = ["python3", "manage.py", "loaddata", "initial_data.yaml"] + + +class TestLoadSampleDataAction: + """Test the load-sample-data action.""" + + def test_load_sample_data_runs_loaddata(self): + """load-sample-data executes Django loaddata command.""" + ctx = testing.Context(charm.DashboardCharm) + container = testing.Container( + "django-app", + can_connect=True, + execs={testing.Exec(_LOADDATA_CMD)}, + ) + state = testing.State(containers={container}, leader=True) + + ctx.run(ctx.on.action("load-sample-data"), state) + + exec_record = ctx.exec_history["django-app"][0] + assert exec_record.command == _LOADDATA_CMD + assert exec_record.working_dir is not None + assert ctx.action_results == {"result": "loaded sample data"} + + def test_load_sample_data_fails_on_exec_error(self): + """load-sample-data action fails when the manage.py command fails.""" + ctx = testing.Context(charm.DashboardCharm) + container = testing.Container( + "django-app", + can_connect=True, + execs={testing.Exec(_LOADDATA_CMD, return_code=1)}, + ) + state = testing.State(containers={container}, leader=True) + + with pytest.raises(testing.ActionFailed) as exc_info: + ctx.run(ctx.on.action("load-sample-data"), state) + + assert "unable to load sample data" in exc_info.value.message + + def test_load_sample_data_fails_on_api_error(self): + """load-sample-data fails when manage.py isn't available.""" + ctx = testing.Context(charm.DashboardCharm) + container = testing.Container("django-app", can_connect=True) + state = testing.State(containers={container}, leader=True) + + with pytest.raises(testing.ActionFailed) as exc_info: + ctx.run(ctx.on.action("load-sample-data"), state) + + assert "unable to load sample data" in exc_info.value.message diff --git a/charm/tox.ini b/charm/tox.ini index b6b376d..fa220b6 100644 --- a/charm/tox.ini +++ b/charm/tox.ini @@ -4,12 +4,12 @@ [tox] no_package = True skip_missing_interpreters = True -env_list = format, lint, static +env_list = format, lint min_version = 4.0.0 [vars] src_path = {tox_root}/src -;tests_path = {tox_root}/tests +tests_path = {tox_root}/tests ;lib_path = {tox_root}/lib/charms/operator_name_with_underscores all_path = {[vars]src_path} @@ -32,10 +32,12 @@ commands = ruff check --fix {[vars]all_path} [testenv:lint] -description = Check code against coding style standards +description = Check code against coding style standards and run static type checks deps = ruff codespell + pyright + -r {tox_root}/requirements.txt commands = # if this charm owns a lib, uncomment "lib_path" variable # and uncomment the following line @@ -43,12 +45,14 @@ commands = codespell {tox_root} ruff check {[vars]all_path} ruff format --check --diff {[vars]all_path} + pyright {posargs} [testenv:unit] description = Run unit tests deps = pytest coverage[toml] + ops[testing] -r {tox_root}/requirements.txt commands = coverage run --source={[vars]src_path} \ @@ -60,14 +64,6 @@ commands = {[vars]tests_path}/unit coverage report -[testenv:static] -description = Run static type checks -deps = - pyright - -r {tox_root}/requirements.txt -commands = - pyright {posargs} - [testenv:integration] description = Run integration tests deps =