odooflow-cli 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odooflow/__init__.py +1 -0
- odooflow/cli.py +105 -0
- odooflow/commands/__init__.py +0 -0
- odooflow/commands/clone_module.py +160 -0
- odooflow/commands/config.py +63 -0
- odooflow/commands/init_module_env.py +37 -0
- odooflow/commands/keygen.py +56 -0
- odooflow/commands/push.py +132 -0
- odooflow/commands/remote.py +133 -0
- odooflow/commands/sync_env.py +38 -0
- odooflow/config_manager.py +56 -0
- odooflow/utils/env.py +40 -0
- odooflow/utils/ssh.py +137 -0
- odooflow_cli-0.1.0.dist-info/METADATA +174 -0
- odooflow_cli-0.1.0.dist-info/RECORD +28 -0
- odooflow_cli-0.1.0.dist-info/WHEEL +5 -0
- odooflow_cli-0.1.0.dist-info/entry_points.txt +2 -0
- odooflow_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- odooflow_cli-0.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/test_commands_config.py +104 -0
- tests/test_commands_init_module_env.py +74 -0
- tests/test_commands_keygen.py +23 -0
- tests/test_commands_remote.py +32 -0
- tests/test_commands_sync_env.py +70 -0
- tests/test_config_manager.py +123 -0
- tests/test_utils_env.py +106 -0
- tests/test_utils_ssh.py +23 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pytest
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from unittest.mock import patch
|
|
5
|
+
from typer.testing import CliRunner
|
|
6
|
+
|
|
7
|
+
from odooflow.cli import app
|
|
8
|
+
from odooflow import config_manager
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture
|
|
12
|
+
def runner():
|
|
13
|
+
"""Create a CLI runner for testing."""
|
|
14
|
+
return CliRunner()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def temp_config_file(tmp_path):
|
|
19
|
+
"""Create a temporary config file for testing."""
|
|
20
|
+
config_path = tmp_path / ".odooflowrc"
|
|
21
|
+
return config_path
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture
|
|
25
|
+
def mock_home(tmp_path, temp_config_file):
|
|
26
|
+
"""Mock home directory."""
|
|
27
|
+
with patch("odooflow.config_manager.Path.home", return_value=tmp_path):
|
|
28
|
+
yield tmp_path
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestConfigCommand:
|
|
32
|
+
"""Test cases for config command."""
|
|
33
|
+
|
|
34
|
+
def test_config_show_default(self, runner, mock_home):
|
|
35
|
+
"""Test showing default configuration."""
|
|
36
|
+
result = runner.invoke(app, ["config", "--show"])
|
|
37
|
+
assert result.exit_code == 0
|
|
38
|
+
assert "env_file" in result.stdout
|
|
39
|
+
|
|
40
|
+
def test_config_show_custom(self, runner, mock_home, temp_config_file):
|
|
41
|
+
"""Test showing custom configuration."""
|
|
42
|
+
test_config = {
|
|
43
|
+
"env_file": ".custom.env.json",
|
|
44
|
+
"manifest_file": "__openerp__.py"
|
|
45
|
+
}
|
|
46
|
+
temp_config_file.write_text(json.dumps(test_config))
|
|
47
|
+
|
|
48
|
+
result = runner.invoke(app, ["config", "--show"])
|
|
49
|
+
assert result.exit_code == 0
|
|
50
|
+
assert ".custom.env.json" in result.stdout
|
|
51
|
+
|
|
52
|
+
def test_config_set_env_file(self, runner, mock_home):
|
|
53
|
+
"""Test setting env_file in config."""
|
|
54
|
+
result = runner.invoke(app, ["config", "--env-file", ".test.env.json"])
|
|
55
|
+
assert result.exit_code == 0
|
|
56
|
+
assert "Configuration updated" in result.stdout
|
|
57
|
+
|
|
58
|
+
config = config_manager.load_config()
|
|
59
|
+
assert config["env_file"] == ".test.env.json"
|
|
60
|
+
|
|
61
|
+
def test_config_set_manifest_file(self, runner, mock_home):
|
|
62
|
+
"""Test setting manifest_file in config."""
|
|
63
|
+
result = runner.invoke(app, ["config", "--manifest-file", "__openerp__.py"])
|
|
64
|
+
assert result.exit_code == 0
|
|
65
|
+
assert "Configuration updated" in result.stdout
|
|
66
|
+
|
|
67
|
+
config = config_manager.load_config()
|
|
68
|
+
assert config["manifest_file"] == "__openerp__.py"
|
|
69
|
+
|
|
70
|
+
def test_config_set_access_token(self, runner, mock_home):
|
|
71
|
+
"""Test setting access token in config."""
|
|
72
|
+
result = runner.invoke(app, ["config", "--access-token", "test_token_123"])
|
|
73
|
+
assert result.exit_code == 0
|
|
74
|
+
assert "Configuration updated" in result.stdout
|
|
75
|
+
|
|
76
|
+
config = config_manager.load_config()
|
|
77
|
+
assert config["access_token"] == "test_token_123"
|
|
78
|
+
|
|
79
|
+
def test_config_add_core_module(self, runner, mock_home):
|
|
80
|
+
"""Test adding core modules to config."""
|
|
81
|
+
result = runner.invoke(app, ["config", "--add-core-module", "sale,purchase"])
|
|
82
|
+
assert result.exit_code == 0
|
|
83
|
+
assert "Added core module" in result.stdout
|
|
84
|
+
|
|
85
|
+
config = config_manager.load_config()
|
|
86
|
+
assert "sale" in config["core_modules"]
|
|
87
|
+
assert "purchase" in config["core_modules"]
|
|
88
|
+
|
|
89
|
+
def test_config_set_sync_keys(self, runner, mock_home):
|
|
90
|
+
"""Test setting sync keys in config."""
|
|
91
|
+
result = runner.invoke(app, ["config", "--sync-keys", "name,version,author"])
|
|
92
|
+
assert result.exit_code == 0
|
|
93
|
+
assert "Updated sync keys" in result.stdout
|
|
94
|
+
|
|
95
|
+
config = config_manager.load_config()
|
|
96
|
+
assert "name" in config["sync_keys"]
|
|
97
|
+
assert "version" in config["sync_keys"]
|
|
98
|
+
assert "author" in config["sync_keys"]
|
|
99
|
+
|
|
100
|
+
def test_config_no_changes(self, runner, mock_home):
|
|
101
|
+
"""Test config command with no changes."""
|
|
102
|
+
result = runner.invoke(app, ["config"])
|
|
103
|
+
assert result.exit_code == 0
|
|
104
|
+
assert "No changes provided" in result.stdout
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pytest
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from unittest.mock import patch
|
|
5
|
+
from typer.testing import CliRunner
|
|
6
|
+
|
|
7
|
+
from odooflow.cli import app
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def runner():
|
|
12
|
+
"""Create a CLI runner for testing."""
|
|
13
|
+
return CliRunner()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def temp_dir(tmp_path):
|
|
18
|
+
"""Create a temporary directory with manifest file."""
|
|
19
|
+
manifest_content = """{
|
|
20
|
+
'name': 'test_module',
|
|
21
|
+
'version': '16.0',
|
|
22
|
+
'author': 'Default Author',
|
|
23
|
+
'license': 'LGPL-3',
|
|
24
|
+
'website': 'https://example.com',
|
|
25
|
+
'depends': ['base', 'web']
|
|
26
|
+
}"""
|
|
27
|
+
manifest_path = tmp_path / "__manifest__.py"
|
|
28
|
+
manifest_path.write_text(manifest_content)
|
|
29
|
+
return tmp_path
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TestInitModuleEnv:
|
|
33
|
+
"""Test cases for init command."""
|
|
34
|
+
|
|
35
|
+
def test_init_with_all_params(self, runner, temp_dir):
|
|
36
|
+
"""Test init command with all parameters."""
|
|
37
|
+
with patch("pathlib.Path.cwd", return_value=temp_dir):
|
|
38
|
+
result = runner.invoke(app, [
|
|
39
|
+
"init",
|
|
40
|
+
"--author", "Test Author",
|
|
41
|
+
"--odoo-version", "17.0",
|
|
42
|
+
"--license", "MIT",
|
|
43
|
+
"--website", "https://test.com"
|
|
44
|
+
])
|
|
45
|
+
assert result.exit_code == 0
|
|
46
|
+
|
|
47
|
+
env_path = temp_dir / ".odooflow.env.json"
|
|
48
|
+
assert env_path.exists()
|
|
49
|
+
|
|
50
|
+
env = json.loads(env_path.read_text())
|
|
51
|
+
assert env["author"] == "Test Author"
|
|
52
|
+
assert env["version"] == "17.0"
|
|
53
|
+
assert env["license"] == "MIT"
|
|
54
|
+
assert env["website"] == "https://test.com"
|
|
55
|
+
|
|
56
|
+
def test_init_without_manifest(self, runner, tmp_path):
|
|
57
|
+
"""Test init command without manifest file."""
|
|
58
|
+
with patch("pathlib.Path.cwd", return_value=tmp_path):
|
|
59
|
+
result = runner.invoke(app, ["init"])
|
|
60
|
+
assert result.exit_code == 1
|
|
61
|
+
|
|
62
|
+
def test_init_partial_params(self, runner, temp_dir):
|
|
63
|
+
"""Test init command with partial parameters."""
|
|
64
|
+
with patch("pathlib.Path.cwd", return_value=temp_dir):
|
|
65
|
+
result = runner.invoke(app, [
|
|
66
|
+
"init",
|
|
67
|
+
"--author", "Test Author"
|
|
68
|
+
])
|
|
69
|
+
assert result.exit_code == 0
|
|
70
|
+
|
|
71
|
+
env_path = temp_dir / ".odooflow.env.json"
|
|
72
|
+
env = json.loads(env_path.read_text())
|
|
73
|
+
assert env["author"] == "Test Author"
|
|
74
|
+
assert env["version"] == "16.0" # Default from manifest
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from unittest.mock import patch, MagicMock
|
|
4
|
+
from typer.testing import CliRunner
|
|
5
|
+
from odooflow.cli import app
|
|
6
|
+
|
|
7
|
+
@pytest.fixture
|
|
8
|
+
def runner():
|
|
9
|
+
return CliRunner()
|
|
10
|
+
|
|
11
|
+
class TestKeygenCommand:
|
|
12
|
+
def test_keygen_basic(self, runner, tmp_path):
|
|
13
|
+
with patch("pathlib.Path.home", return_value=tmp_path):
|
|
14
|
+
with patch("subprocess.run") as mock_run:
|
|
15
|
+
result = runner.invoke(app, ["ssh-keygen"])
|
|
16
|
+
assert result.exit_code == 0
|
|
17
|
+
mock_run.assert_called_once()
|
|
18
|
+
|
|
19
|
+
def test_keygen_with_custom_name(self, runner, tmp_path):
|
|
20
|
+
with patch("pathlib.Path.home", return_value=tmp_path):
|
|
21
|
+
with patch("subprocess.run"):
|
|
22
|
+
result = runner.invoke(app, ["ssh-keygen", "--key-name", "custom_key"])
|
|
23
|
+
assert result.exit_code == 0
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pytest
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from unittest.mock import patch
|
|
5
|
+
from typer.testing import CliRunner
|
|
6
|
+
from odooflow.cli import app
|
|
7
|
+
|
|
8
|
+
@pytest.fixture
|
|
9
|
+
def runner():
|
|
10
|
+
return CliRunner()
|
|
11
|
+
|
|
12
|
+
@pytest.fixture
|
|
13
|
+
def temp_dir_with_env(tmp_path):
|
|
14
|
+
env_path = tmp_path / ".odooflow.env.json"
|
|
15
|
+
env_path.write_text('{"name": "test", "remotes": {}}')
|
|
16
|
+
return tmp_path
|
|
17
|
+
|
|
18
|
+
class TestRemoteCommand:
|
|
19
|
+
def test_remote_add_repo(self, runner, temp_dir_with_env):
|
|
20
|
+
with patch("pathlib.Path.cwd", return_value=temp_dir_with_env):
|
|
21
|
+
with patch("odooflow.commands.remote.get_current_branch", return_value="main"):
|
|
22
|
+
result = runner.invoke(app, ["remote", "--add-repo", "https://gitlab.com/test/repo.git"])
|
|
23
|
+
assert result.exit_code == 0
|
|
24
|
+
env = json.loads((temp_dir_with_env / ".odooflow.env.json").read_text())
|
|
25
|
+
assert env["remotes"]["repo"]["url"] == "https://gitlab.com/test/repo.git"
|
|
26
|
+
|
|
27
|
+
def test_remote_add_server(self, runner, temp_dir_with_env):
|
|
28
|
+
with patch("pathlib.Path.cwd", return_value=temp_dir_with_env):
|
|
29
|
+
result = runner.invoke(app, ["remote", "--server-json", '{"host": "127.0.0.1", "port": 22, "user": "test", "directory": "/opt/odoo"}'])
|
|
30
|
+
assert result.exit_code == 0
|
|
31
|
+
env = json.loads((temp_dir_with_env / ".odooflow.env.json").read_text())
|
|
32
|
+
assert env["remotes"]["server"]["host"] == "127.0.0.1"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pytest
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from unittest.mock import patch
|
|
5
|
+
from typer.testing import CliRunner
|
|
6
|
+
|
|
7
|
+
from odooflow.cli import app
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def runner():
|
|
12
|
+
"""Create a CLI runner for testing."""
|
|
13
|
+
return CliRunner()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def temp_dir_with_files(tmp_path):
|
|
18
|
+
"""Create a temporary directory with manifest and env files."""
|
|
19
|
+
manifest_content = """{
|
|
20
|
+
'name': 'test_module',
|
|
21
|
+
'version': '16.0',
|
|
22
|
+
'author': 'Test Author',
|
|
23
|
+
'license': 'LGPL-3',
|
|
24
|
+
'depends': ['base', 'web']
|
|
25
|
+
}"""
|
|
26
|
+
manifest_path = tmp_path / "__manifest__.py"
|
|
27
|
+
manifest_path.write_text(manifest_content)
|
|
28
|
+
|
|
29
|
+
env_content = """{
|
|
30
|
+
"name": "old_name",
|
|
31
|
+
"version": "15.0"
|
|
32
|
+
}"""
|
|
33
|
+
env_path = tmp_path / ".odooflow.env.json"
|
|
34
|
+
env_path.write_text(env_content)
|
|
35
|
+
|
|
36
|
+
return tmp_path
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TestSyncEnv:
|
|
40
|
+
"""Test cases for sync-env command."""
|
|
41
|
+
|
|
42
|
+
def test_sync_env_default_keys(self, runner, temp_dir_with_files):
|
|
43
|
+
"""Test sync-env with default keys."""
|
|
44
|
+
with patch("pathlib.Path.cwd", return_value=temp_dir_with_files):
|
|
45
|
+
result = runner.invoke(app, ["sync-env"])
|
|
46
|
+
assert result.exit_code == 0
|
|
47
|
+
|
|
48
|
+
env_path = temp_dir_with_files / ".odooflow.env.json"
|
|
49
|
+
env = json.loads(env_path.read_text())
|
|
50
|
+
assert env["name"] == "test_module"
|
|
51
|
+
assert env["version"] == "16.0"
|
|
52
|
+
assert env["author"] == "Test Author"
|
|
53
|
+
|
|
54
|
+
def test_sync_env_custom_keys(self, runner, temp_dir_with_files):
|
|
55
|
+
"""Test sync-env with custom keys."""
|
|
56
|
+
with patch("pathlib.Path.cwd", return_value=temp_dir_with_files):
|
|
57
|
+
result = runner.invoke(app, ["sync-env", "--keys", "name,license"])
|
|
58
|
+
assert result.exit_code == 0
|
|
59
|
+
|
|
60
|
+
env_path = temp_dir_with_files / ".odooflow.env.json"
|
|
61
|
+
env = json.loads(env_path.read_text())
|
|
62
|
+
assert env["name"] == "test_module"
|
|
63
|
+
assert env["license"] == "LGPL-3"
|
|
64
|
+
assert env["version"] == "15.0" # Should not be updated
|
|
65
|
+
|
|
66
|
+
def test_sync_env_no_manifest(self, runner, tmp_path):
|
|
67
|
+
"""Test sync-env without manifest file."""
|
|
68
|
+
with patch("pathlib.Path.cwd", return_value=tmp_path):
|
|
69
|
+
result = runner.invoke(app, ["sync-env"])
|
|
70
|
+
assert result.exit_code == 1
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pytest
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from unittest.mock import patch, MagicMock
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from odooflow.config_manager import (
|
|
8
|
+
DEFAULT_CONFIG,
|
|
9
|
+
get_global_config_path,
|
|
10
|
+
load_config,
|
|
11
|
+
save_config,
|
|
12
|
+
get_access_token,
|
|
13
|
+
get_core_modules_from_config,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def temp_config_file(tmp_path):
|
|
19
|
+
"""Create a temporary config file for testing."""
|
|
20
|
+
config_path = tmp_path / ".odooflowrc"
|
|
21
|
+
return config_path
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture
|
|
25
|
+
def mock_home(tmp_path):
|
|
26
|
+
"""Mock home directory."""
|
|
27
|
+
with patch("odooflow.config_manager.Path.home", return_value=tmp_path):
|
|
28
|
+
yield tmp_path
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestConfigManager:
|
|
32
|
+
"""Test cases for config_manager module."""
|
|
33
|
+
|
|
34
|
+
def test_default_config_structure(self):
|
|
35
|
+
"""Test that DEFAULT_CONFIG has all required keys."""
|
|
36
|
+
assert "env_file" in DEFAULT_CONFIG
|
|
37
|
+
assert "manifest_file" in DEFAULT_CONFIG
|
|
38
|
+
assert "core_modules" in DEFAULT_CONFIG
|
|
39
|
+
assert "sync_keys" in DEFAULT_CONFIG
|
|
40
|
+
assert "gitlab_url" in DEFAULT_CONFIG
|
|
41
|
+
|
|
42
|
+
def test_get_global_config_path(self, mock_home):
|
|
43
|
+
"""Test getting global config path."""
|
|
44
|
+
path = get_global_config_path()
|
|
45
|
+
assert path == mock_home / ".odooflowrc"
|
|
46
|
+
|
|
47
|
+
def test_load_config_no_file(self, mock_home):
|
|
48
|
+
"""Test loading config when no config file exists."""
|
|
49
|
+
config = load_config()
|
|
50
|
+
assert config == DEFAULT_CONFIG
|
|
51
|
+
|
|
52
|
+
def test_load_config_with_file(self, mock_home, temp_config_file):
|
|
53
|
+
"""Test loading config from existing file."""
|
|
54
|
+
test_config = {
|
|
55
|
+
"env_file": ".custom.env.json",
|
|
56
|
+
"manifest_file": "__openerp__.py",
|
|
57
|
+
"core_modules": ["base", "web"],
|
|
58
|
+
"sync_keys": ["name", "version"],
|
|
59
|
+
"gitlab_url": "https://custom.gitlab.com"
|
|
60
|
+
}
|
|
61
|
+
temp_config_file.write_text(json.dumps(test_config))
|
|
62
|
+
|
|
63
|
+
config = load_config()
|
|
64
|
+
assert config == test_config
|
|
65
|
+
|
|
66
|
+
def test_load_config_invalid_json(self, mock_home, temp_config_file):
|
|
67
|
+
"""Test loading config with invalid JSON."""
|
|
68
|
+
temp_config_file.write_text("invalid json")
|
|
69
|
+
config = load_config()
|
|
70
|
+
assert config == DEFAULT_CONFIG
|
|
71
|
+
|
|
72
|
+
def test_save_config(self, mock_home, temp_config_file):
|
|
73
|
+
"""Test saving config to file."""
|
|
74
|
+
test_config = {"env_file": ".test.env.json"}
|
|
75
|
+
save_config(test_config)
|
|
76
|
+
|
|
77
|
+
assert temp_config_file.exists()
|
|
78
|
+
loaded = json.loads(temp_config_file.read_text())
|
|
79
|
+
assert loaded == test_config
|
|
80
|
+
|
|
81
|
+
def test_get_access_token_from_env(self, mock_home):
|
|
82
|
+
"""Test getting access token from environment variable."""
|
|
83
|
+
with patch.dict("os.environ", {"ODOOFLOW_ACCESS_TOKEN": "test_token"}):
|
|
84
|
+
token = get_access_token()
|
|
85
|
+
assert token == "test_token"
|
|
86
|
+
|
|
87
|
+
def test_get_access_token_from_config(self, mock_home, temp_config_file):
|
|
88
|
+
"""Test getting access token from config file."""
|
|
89
|
+
test_config = {"access_token": "config_token"}
|
|
90
|
+
temp_config_file.write_text(json.dumps(test_config))
|
|
91
|
+
|
|
92
|
+
with patch.dict("os.environ", {}, clear=True):
|
|
93
|
+
token = get_access_token()
|
|
94
|
+
assert token == "config_token"
|
|
95
|
+
|
|
96
|
+
def test_get_access_token_not_found(self, mock_home):
|
|
97
|
+
"""Test getting access token when not found."""
|
|
98
|
+
with patch.dict("os.environ", {}, clear=True):
|
|
99
|
+
with pytest.raises(typer.Exit):
|
|
100
|
+
get_access_token()
|
|
101
|
+
|
|
102
|
+
def test_get_core_modules_from_config_default(self, mock_home):
|
|
103
|
+
"""Test getting core modules with default config."""
|
|
104
|
+
modules = get_core_modules_from_config()
|
|
105
|
+
assert modules == set(DEFAULT_CONFIG["core_modules"])
|
|
106
|
+
|
|
107
|
+
def test_get_core_modules_from_config_custom(self, mock_home, temp_config_file):
|
|
108
|
+
"""Test getting core modules with custom config."""
|
|
109
|
+
test_config = {
|
|
110
|
+
"core_modules": ["base", "web", "custom"]
|
|
111
|
+
}
|
|
112
|
+
temp_config_file.write_text(json.dumps(test_config))
|
|
113
|
+
|
|
114
|
+
modules = get_core_modules_from_config()
|
|
115
|
+
assert modules == {"base", "web", "custom"}
|
|
116
|
+
|
|
117
|
+
def test_get_core_modules_from_config_empty(self, mock_home, temp_config_file):
|
|
118
|
+
"""Test getting core modules when config has empty list."""
|
|
119
|
+
test_config = {"core_modules": []}
|
|
120
|
+
temp_config_file.write_text(json.dumps(test_config))
|
|
121
|
+
|
|
122
|
+
modules = get_core_modules_from_config()
|
|
123
|
+
assert modules == set()
|
tests/test_utils_env.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import pytest
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from unittest.mock import patch
|
|
5
|
+
|
|
6
|
+
from odooflow.utils.env import (
|
|
7
|
+
read_manifest,
|
|
8
|
+
update_manifest,
|
|
9
|
+
write_env_file,
|
|
10
|
+
read_env_file,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def temp_manifest_file(tmp_path):
|
|
16
|
+
"""Create a temporary manifest file for testing."""
|
|
17
|
+
manifest_path = tmp_path / "__manifest__.py"
|
|
18
|
+
return manifest_path
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def temp_env_file(tmp_path):
|
|
23
|
+
"""Create a temporary env file for testing."""
|
|
24
|
+
env_path = tmp_path / ".odooflow.env.json"
|
|
25
|
+
return env_path
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TestEnvUtils:
|
|
29
|
+
"""Test cases for env utility functions."""
|
|
30
|
+
|
|
31
|
+
def test_read_manifest_valid(self, temp_manifest_file):
|
|
32
|
+
"""Test reading a valid manifest file."""
|
|
33
|
+
manifest_content = "{'name': 'test_module', 'version': '16.0'}"
|
|
34
|
+
temp_manifest_file.write_text(manifest_content)
|
|
35
|
+
|
|
36
|
+
manifest = read_manifest(temp_manifest_file)
|
|
37
|
+
assert manifest == {"name": "test_module", "version": "16.0"}
|
|
38
|
+
|
|
39
|
+
def test_read_manifest_invalid_syntax(self, temp_manifest_file):
|
|
40
|
+
"""Test reading manifest with invalid syntax."""
|
|
41
|
+
temp_manifest_file.write_text("invalid python")
|
|
42
|
+
|
|
43
|
+
manifest = read_manifest(temp_manifest_file)
|
|
44
|
+
assert manifest == {}
|
|
45
|
+
|
|
46
|
+
def test_read_manifest_empty_file(self, temp_manifest_file):
|
|
47
|
+
"""Test reading an empty manifest file."""
|
|
48
|
+
temp_manifest_file.write_text("")
|
|
49
|
+
|
|
50
|
+
manifest = read_manifest(temp_manifest_file)
|
|
51
|
+
assert manifest == {}
|
|
52
|
+
|
|
53
|
+
def test_read_manifest_security(self, temp_manifest_file):
|
|
54
|
+
"""Test that eval is not used (security check)."""
|
|
55
|
+
# This should not execute arbitrary code
|
|
56
|
+
malicious_content = "__import__('os').system('echo malicious')"
|
|
57
|
+
temp_manifest_file.write_text(malicious_content)
|
|
58
|
+
|
|
59
|
+
manifest = read_manifest(temp_manifest_file)
|
|
60
|
+
# Should return empty dict due to ast.literal_eval failing
|
|
61
|
+
assert manifest == {}
|
|
62
|
+
|
|
63
|
+
def test_write_env_file(self, temp_env_file):
|
|
64
|
+
"""Test writing env file."""
|
|
65
|
+
test_values = {
|
|
66
|
+
"name": "test_module",
|
|
67
|
+
"author": "Test Author",
|
|
68
|
+
"version": "16.0"
|
|
69
|
+
}
|
|
70
|
+
write_env_file(temp_env_file, test_values)
|
|
71
|
+
|
|
72
|
+
assert temp_env_file.exists()
|
|
73
|
+
loaded = json.loads(temp_env_file.read_text())
|
|
74
|
+
assert loaded == test_values
|
|
75
|
+
|
|
76
|
+
def test_read_env_file_valid(self, temp_env_file):
|
|
77
|
+
"""Test reading a valid env file."""
|
|
78
|
+
test_values = {"name": "test_module", "version": "16.0"}
|
|
79
|
+
temp_env_file.write_text(json.dumps(test_values))
|
|
80
|
+
|
|
81
|
+
env = read_env_file(temp_env_file)
|
|
82
|
+
assert env == test_values
|
|
83
|
+
|
|
84
|
+
def test_read_env_file_invalid_json(self, temp_env_file):
|
|
85
|
+
"""Test reading env file with invalid JSON."""
|
|
86
|
+
temp_env_file.write_text("invalid json")
|
|
87
|
+
|
|
88
|
+
env = read_env_file(temp_env_file)
|
|
89
|
+
assert env == {}
|
|
90
|
+
|
|
91
|
+
def test_read_env_file_not_exists(self, temp_env_file):
|
|
92
|
+
"""Test reading env file that doesn't exist."""
|
|
93
|
+
env = read_env_file(temp_env_file)
|
|
94
|
+
assert env == {}
|
|
95
|
+
|
|
96
|
+
def test_update_manifest(self, temp_manifest_file):
|
|
97
|
+
"""Test updating manifest file."""
|
|
98
|
+
initial_content = "{'name': 'test_module', 'version': '16.0'}"
|
|
99
|
+
temp_manifest_file.write_text(initial_content)
|
|
100
|
+
|
|
101
|
+
updates = {"author": "Test Author", "license": "LGPL-3"}
|
|
102
|
+
update_manifest(temp_manifest_file, updates)
|
|
103
|
+
|
|
104
|
+
updated_content = temp_manifest_file.read_text()
|
|
105
|
+
assert "Test Author" in updated_content
|
|
106
|
+
assert "LGPL-3" in updated_content
|
tests/test_utils_ssh.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from unittest.mock import patch, MagicMock
|
|
4
|
+
from odooflow.utils.ssh import resolve_remote_path, compress_directory
|
|
5
|
+
|
|
6
|
+
class TestSSHUtils:
|
|
7
|
+
def test_resolve_remote_path_absolute(self):
|
|
8
|
+
sftp = MagicMock()
|
|
9
|
+
sftp.normalize.return_value = "/home/user"
|
|
10
|
+
result = resolve_remote_path(sftp, "/opt/odoo")
|
|
11
|
+
assert result == "/opt/odoo"
|
|
12
|
+
|
|
13
|
+
def test_resolve_remote_path_relative(self):
|
|
14
|
+
sftp = MagicMock()
|
|
15
|
+
sftp.normalize.return_value = "/home/user"
|
|
16
|
+
result = resolve_remote_path(sftp, "odoo")
|
|
17
|
+
assert result == "/home/user/odoo"
|
|
18
|
+
|
|
19
|
+
def test_resolve_remote_path_tilde(self):
|
|
20
|
+
sftp = MagicMock()
|
|
21
|
+
sftp.normalize.return_value = "/home/user"
|
|
22
|
+
result = resolve_remote_path(sftp, "~/odoo")
|
|
23
|
+
assert result == "/home/user/odoo"
|