diff --git a/.github/workflows/functional_test.yml b/.github/workflows/functional_test.yml new file mode 100644 index 0000000000..eaa50adf30 --- /dev/null +++ b/.github/workflows/functional_test.yml @@ -0,0 +1,31 @@ +name: "Functional tests" +# Runs automated test suites that ensure functionality is preserved. Any failures should prevent code from shipping. +on: + pull_request: + branches: [main, release] + +permissions: + contents: read + +jobs: + test: + name: test with ${{ matrix.env }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + env: ["3.10", "3.11"] #, "3.12", "3.13", "3.14" + os: [ubuntu-latest, macos-latest] + steps: + - uses: actions/checkout@v5 + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true + + - name: Run Tests + run: | + uv run --python ${{ matrix.env }} pytest \ + tests/test_classes \ + --color=yes diff --git a/augur/application/config.py b/augur/application/config.py index 41a7290200..f46f6dc278 100644 --- a/augur/application/config.py +++ b/augur/application/config.py @@ -546,6 +546,8 @@ class JsonConfig(ConfigStore): def __init__(self, json_data, logger: logging.Logger): super().__init__(logger) + if not self.writable: + json_data = copy.deepcopy(json_data) self.json_data = json_data @property diff --git a/pyproject.toml b/pyproject.toml index 8193867b81..064b5e7bdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -187,6 +187,19 @@ legacy_tox_ini = """ addopts = -ra -s """ +[tool.pytest.ini_options] +addopts = "-ra -s" +testpaths = [ + "tests/test_classes", + # "tests/test_routes", # runs, but needs a fixture for connecting to the web interface of Augur + # "tests/test_metrics", + # "tests/test_tasks", + # "tests/test_application", + # "tests/test_workers", + # "tests/test_workers/worker_persistence/", + # "tests/test_routes/runner.py" +] + [tool.mypy] files = ['augur/application/db/*.py'] ignore_missing_imports = true diff --git a/tests/test_classes/test_config_stores.py b/tests/test_classes/test_config_stores.py index b55275cfb7..003f19431d 100644 --- a/tests/test_classes/test_config_stores.py +++ b/tests/test_classes/test_config_stores.py @@ -25,12 +25,31 @@ def test_jsonconfig_empty_true_false(mock_logger): assert JsonConfig({"A": {}}, mock_logger).empty is False +def test_jsonconfig_write_protection(mock_logger): + # JsonConfig should be not writeable by default, so we should be unable to change + # its values, even by abusing references + + data = {"Alpha": {"a": 1, "b": "str"}, "Beta": {}} + cfg = JsonConfig(data, mock_logger) + + # mutation via input + data["Alpha"]["a"] = 2 + + config_test = cfg.retrieve_dict() + assert config_test != data # the data in the config should not change + + # mutation via output + config_test["Alpha"]["a"] = 3 + + config_test = cfg.retrieve_dict() + assert config_test != data # the data in the config should not change + def test_jsonconfig_retrieve_has_get(mock_logger): data = {"Alpha": {"a": 1, "b": "str"}, "Beta": {}} cfg = JsonConfig(data, mock_logger) # retrieve full dict - assert cfg.retrieve_dict() is data + assert cfg.retrieve_dict() == data # has/get section assert cfg.has_section("Alpha") is True