Files
odoo-addons/addons/cetmix_tower_yaml/tests/test_server_log.py

128 lines
4.5 KiB
Python

# Copyright (C) 2025 Cetmix OÜ
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
"""
Tests for the cx.tower.server.log model YAML export/import.
Covers:
1. YAML export of a file-type log must include `file_id` and allow suffixes.
2. A full round-trip (export → delete → import) preserves the `file_id` relation.
3. Exporting a non-file log must include a falsy `file_id`.
4. Importing YAML with a bogus `file_id` reference raises ValidationError.
"""
import yaml
from odoo.tests import TransactionCase, tagged
@tagged("post_install", "-at_install")
class TestServerLog(TransactionCase):
"""YAML export/import tests for cx.tower.server.log."""
@classmethod
def setUpClass(cls):
super().setUpClass()
env = cls.env
cls.File = env["cx.tower.file"]
cls.Server = env["cx.tower.server"]
cls.ServerLog = env["cx.tower.server.log"]
# Create a file to reference from the log
cls.file = cls.File.create(
{
"name": "repos.yaml",
"reference": "reposyaml",
"source": "tower",
"file_type": "text",
"server_dir": "/tmp",
"code": "# Example\nHello, Tower!",
}
)
# Create a server (use password auth to satisfy constraints)
cls.server = cls.Server.create(
{
"name": "Srv-YAML-Test",
"reference": "srv_yaml_test",
"ip_v4_address": "127.0.0.1",
"ssh_username": "admin",
"ssh_port": 22,
"ssh_auth_mode": "p",
"ssh_password": "dummy",
"use_sudo": False,
}
)
# Create a file-type log linked to the file above
cls.log = cls.ServerLog.create(
{
"name": "Log from file",
"reference": "log_from_file",
"log_type": "file",
"file_id": cls.file.id,
"server_id": cls.server.id,
"use_sudo": False,
}
)
def test_yaml_export_contains_file_id(self):
"""Exported YAML must include a file_id starting with the file's reference."""
data = yaml.safe_load(self.log.yaml_code)
# Ensure file_id is present
self.assertIn("file_id", data, "`file_id` is missing from YAML export")
# Allow for auto-appended suffixes, so only check prefix
self.assertTrue(
data["file_id"].startswith(self.file.reference),
f"`file_id` value '{data['file_id']}' should start with "
f"'{self.file.reference}'",
)
def test_yaml_roundtrip_restores_file_id(self):
"""A full export→delete→import cycle must restore the file_id relation."""
yaml_dict = yaml.safe_load(self.log.yaml_code)
# Remove the original log
self.log.unlink()
# Recreate from YAML
vals = self.ServerLog._post_process_yaml_dict_values(yaml_dict)
restored = self.ServerLog.with_context(from_yaml=True).create(vals)
# Verify relation restored
self.assertEqual(
restored.file_id.id,
self.file.id,
"`file_id` was not restored after round-trip",
)
def test_yaml_export_without_file_id(self):
"""Logs of non-file type should not include file_id in YAML."""
cmd_log = self.ServerLog.create(
{
"name": "Log no file",
"reference": "log_no_file",
"log_type": "command",
"server_id": self.server.id,
"use_sudo": False,
}
)
data = yaml.safe_load(cmd_log.yaml_code)
# key is present, but must be falsy
self.assertIn("file_id", data, "`file_id` key is missing")
self.assertFalse(
data["file_id"],
"`file_id` for non-file log must be False/empty",
)
def test_yaml_import_with_missing_file_reference(self):
"""Missing file reference is accepted, but file_id stays empty."""
yaml_dict = yaml.safe_load(self.log.yaml_code)
yaml_dict["file_id"] = "does_not_exist"
vals = self.ServerLog._post_process_yaml_dict_values(yaml_dict)
new_log = self.ServerLog.with_context(from_yaml=True).create(vals)
# Log is created, but the relation is not resolved
self.assertFalse(
new_log.file_id,
"file_id should be empty when reference cannot be resolved",
)