From 0ef8f75621537332fcbd7d4b38d4ad2c3fb82b77 Mon Sep 17 00:00:00 2001 From: Neha Oudin Date: Mon, 25 Nov 2024 17:39:59 +0100 Subject: [PATCH] test: more backup and basic tests --- single_kernel_mongo/core/vm_workload.py | 2 +- single_kernel_mongo/managers/backups.py | 4 +- tests/unit/test_backup_manager.py | 136 ++++++++++++++++++++++++ tests/unit/test_mongodb_paths.py | 1 + tests/unit/test_mongodb_workload.py | 25 ++++- 5 files changed, 164 insertions(+), 4 deletions(-) diff --git a/single_kernel_mongo/core/vm_workload.py b/single_kernel_mongo/core/vm_workload.py index 21e88580..9dd8a6a2 100644 --- a/single_kernel_mongo/core/vm_workload.py +++ b/single_kernel_mongo/core/vm_workload.py @@ -111,7 +111,7 @@ def delete(self, path: Path) -> None: path.unlink() @override - def copy_to_unit(self, src: Path, destination: Path) -> None: + def copy_to_unit(self, src: Path, destination: Path) -> None: # pragma: nocover copyfile(src, destination) @override diff --git a/single_kernel_mongo/managers/backups.py b/single_kernel_mongo/managers/backups.py index c5f008a1..2735e4b7 100644 --- a/single_kernel_mongo/managers/backups.py +++ b/single_kernel_mongo/managers/backups.py @@ -239,7 +239,7 @@ def get_status(self) -> StatusBase | None: logger.error(f"Failed to get pbm status: {e}") return BlockedStatus("PBM error") - def resync_config_options(self): + def resync_config_options(self): # pragma: nocover """Attempts to resync config options and sets status in case of failure.""" self.workload.start() @@ -382,7 +382,7 @@ def can_restore(self, backup_id: str, remapping_pattern: str) -> None: case BlockedStatus(): raise InvalidPBMStatusError(pbm_status.message) case _: - return + pass if not backup_id: raise InvalidArgumentForActionError("Missing backup-id to restore.") diff --git a/tests/unit/test_backup_manager.py b/tests/unit/test_backup_manager.py index f90c04c9..7a359186 100644 --- a/tests/unit/test_backup_manager.py +++ b/tests/unit/test_backup_manager.py @@ -11,6 +11,8 @@ from single_kernel_mongo.events.backups import INVALID_S3_INTEGRATION_STATUS from single_kernel_mongo.exceptions import ( BackupError, + InvalidArgumentForActionError, + InvalidPBMStatusError, ListBackupError, ResyncError, WorkloadExecError, @@ -265,3 +267,137 @@ def test_restore_backup_success(harness: Harness[MongoTestCharm], mocker) -> Non ["deadbeef", "--replset-remapping", "test-mongodb=test-mongodb"], environment=backup_manager.environment, ) + + +def test_get_backup_error_status(harness: Harness[MongoTestCharm], mocker) -> None: + backup_manager = harness.charm.operator.backup_manager + harness.set_leader(True) + harness.charm.operator.state.app_peer_data.role = MongoDBRoles.REPLICATION.value + mocker.patch("single_kernel_mongo.core.vm_workload.VMWorkload.active", return_value=True) + relation_id = harness.add_relation( + ExternalRequirerRelations.S3_CREDENTIALS.value, "s3-integrator" + ) + harness.add_relation_unit(relation_id, "s3-integrator/0") + mock = mocker.patch( + "single_kernel_mongo.managers.backups.BackupManager.pbm_status", + new_callable=mocker.PropertyMock, + ) + with open("tests/unit/data/list_backups.json") as fd: + pbm_status = fd.read() + + mock.return_value = pbm_status + + error = backup_manager.get_backup_error_status("2024-11-25T-15:30:05Z") + assert error == "not found" + + +@pytest.mark.parametrize( + ("pbm_status", "pattern"), + ( + (MaintenanceStatus(""), "Please wait for current.*"), + (WaitingStatus(""), "Sync-ing configurations needs more time.*"), + (BlockedStatus("error"), "error"), + ), +) +def test_can_restore_fail_status( + harness: Harness[MongoTestCharm], mocker, pbm_status, pattern +) -> None: + backup_manager = harness.charm.operator.backup_manager + harness.set_leader(True) + harness.charm.operator.state.app_peer_data.role = MongoDBRoles.REPLICATION.value + mocker.patch("single_kernel_mongo.core.vm_workload.VMWorkload.active", return_value=True) + relation_id = harness.add_relation( + ExternalRequirerRelations.S3_CREDENTIALS.value, "s3-integrator" + ) + harness.add_relation_unit(relation_id, "s3-integrator/0") + mock = mocker.patch( + "single_kernel_mongo.managers.backups.BackupManager.get_status", + ) + + mock.return_value = pbm_status + with pytest.raises(InvalidPBMStatusError) as e: + backup_manager.can_restore("backup", "remapping_pattern") + assert e.match(pattern) + + +@pytest.mark.parametrize( + ("backup_id", "remap_pattern", "pattern"), + (("", "", "Missing backup-id.*"), ("2024", "", ".*'remap-pattern'.*")), +) +def test_can_restore_fail_params( + harness: Harness[MongoTestCharm], mocker, backup_id, remap_pattern, pattern +) -> None: + backup_manager = harness.charm.operator.backup_manager + harness.set_leader(True) + harness.charm.operator.state.app_peer_data.role = MongoDBRoles.REPLICATION.value + mocker.patch("single_kernel_mongo.core.vm_workload.VMWorkload.active", return_value=True) + relation_id = harness.add_relation( + ExternalRequirerRelations.S3_CREDENTIALS.value, "s3-integrator" + ) + harness.add_relation_unit(relation_id, "s3-integrator/0") + mocker.patch( + "single_kernel_mongo.managers.backups.BackupManager.get_status", return_value=ActiveStatus() + ) + mocker.patch( + "single_kernel_mongo.managers.backups.BackupManager._needs_provided_remap_arguments", + return_value=True, + ) + with pytest.raises(InvalidArgumentForActionError) as e: + backup_manager.can_restore(backup_id, remap_pattern) + + assert e.match(pattern) + + +@pytest.mark.parametrize( + ("pbm_status", "pattern"), + ( + (MaintenanceStatus(""), "Can only create one backup.*"), + (WaitingStatus(""), "Sync-ing configurations needs more time.*"), + (BlockedStatus("error"), "error"), + ), +) +def test_can_backup_fail(harness: Harness[MongoTestCharm], mocker, pbm_status, pattern) -> None: + backup_manager = harness.charm.operator.backup_manager + harness.set_leader(True) + harness.charm.operator.state.app_peer_data.role = MongoDBRoles.REPLICATION.value + mocker.patch("single_kernel_mongo.core.vm_workload.VMWorkload.active", return_value=True) + relation_id = harness.add_relation( + ExternalRequirerRelations.S3_CREDENTIALS.value, "s3-integrator" + ) + harness.add_relation_unit(relation_id, "s3-integrator/0") + mock = mocker.patch( + "single_kernel_mongo.managers.backups.BackupManager.get_status", + ) + + mock.return_value = pbm_status + with pytest.raises(InvalidPBMStatusError) as e: + backup_manager.can_backup() + assert e.match(pattern) + + +@pytest.mark.parametrize( + ("pbm_status", "pattern"), + ( + (WaitingStatus(""), "Sync-ing configurations needs more time.*"), + (BlockedStatus("error"), "error"), + ), +) +def test_can_list_backup_fail( + harness: Harness[MongoTestCharm], mocker, pbm_status, pattern +) -> None: + backup_manager = harness.charm.operator.backup_manager + harness.set_leader(True) + harness.charm.operator.state.app_peer_data.role = MongoDBRoles.REPLICATION.value + mocker.patch("single_kernel_mongo.core.vm_workload.VMWorkload.active", return_value=True) + relation_id = harness.add_relation( + ExternalRequirerRelations.S3_CREDENTIALS.value, "s3-integrator" + ) + harness.add_relation_unit(relation_id, "s3-integrator/0") + mock = mocker.patch( + "single_kernel_mongo.managers.backups.BackupManager.get_status", + ) + + mock.return_value = pbm_status + with pytest.raises(InvalidPBMStatusError) as e: + backup_manager.can_list_backup() + assert e.match(pattern) diff --git a/tests/unit/test_mongodb_paths.py b/tests/unit/test_mongodb_paths.py index 1a9adae4..313ee2dd 100644 --- a/tests/unit/test_mongodb_paths.py +++ b/tests/unit/test_mongodb_paths.py @@ -23,5 +23,6 @@ def test_mongo_paths(role: Role): assert paths.int_pem_file.parent == Path(role.paths["CONF"]) assert paths.int_ca_file.parent == Path(role.paths["CONF"]) assert paths.socket_path.parent == Path(role.paths["VAR"]) + assert paths.common_path == Path(role.paths["VAR"]).parent assert all(path.parent == Path(role.paths["CONF"]) for path in paths.tls_files) diff --git a/tests/unit/test_mongodb_workload.py b/tests/unit/test_mongodb_workload.py index 2697f16c..6becc8f3 100644 --- a/tests/unit/test_mongodb_workload.py +++ b/tests/unit/test_mongodb_workload.py @@ -195,11 +195,34 @@ def mock_snap(*args, **kwargs): assert workload.install() -def test_read_file(): +def test_read_file_fail(): workload = VMMongoDBWorkload(container=None) assert workload.read(Path("/nonexistent")) == [] +def test_read_file_succeed(tmp_path): + tmp_file = tmp_path / "test_file.txt" + tmp_file.write_text("need\ndead\ncafe\n") + + workload = VMMongoDBWorkload(container=None) + assert workload.read(tmp_file) == ["need", "dead", "cafe"] + + +def test_delete_fail(): + workload = VMMongoDBWorkload(container=None) + assert workload.delete(Path("/nonexistent")) is None + + +def test_delete_success(tmp_path): + tmp_file = tmp_path / "test_file.txt" + tmp_file.write_text("need\ndead\ncafe\n") + + workload = VMMongoDBWorkload(container=None) + workload.delete(tmp_file) + + assert not tmp_file.is_file() + + @pytest.mark.parametrize("command", [("start"), ("stop"), ("restart")]) def test_command_success(monkeypatch, command): def mock_snap(*args, **kwargs):