odoo-addon-odoo-repository 16.0.1.3.0.13__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.
Files changed (77) hide show
  1. odoo/addons/odoo_repository/README.rst +81 -0
  2. odoo/addons/odoo_repository/__init__.py +2 -0
  3. odoo/addons/odoo_repository/__manifest__.py +58 -0
  4. odoo/addons/odoo_repository/controllers/__init__.py +1 -0
  5. odoo/addons/odoo_repository/controllers/main.py +32 -0
  6. odoo/addons/odoo_repository/data/ir_cron.xml +38 -0
  7. odoo/addons/odoo_repository/data/odoo.repository.csv +216 -0
  8. odoo/addons/odoo_repository/data/odoo_branch.xml +82 -0
  9. odoo/addons/odoo_repository/data/odoo_module.xml +16 -0
  10. odoo/addons/odoo_repository/data/odoo_repository.xml +71 -0
  11. odoo/addons/odoo_repository/data/odoo_repository_addons_path.xml +59 -0
  12. odoo/addons/odoo_repository/data/odoo_repository_org.xml +14 -0
  13. odoo/addons/odoo_repository/data/queue_job.xml +56 -0
  14. odoo/addons/odoo_repository/lib/__init__.py +0 -0
  15. odoo/addons/odoo_repository/lib/scanner.py +1302 -0
  16. odoo/addons/odoo_repository/migrations/16.0.1.1.0/post-migration.py +26 -0
  17. odoo/addons/odoo_repository/migrations/16.0.1.2.0/pre-migration.py +43 -0
  18. odoo/addons/odoo_repository/migrations/16.0.1.3.0/post-migration.py +19 -0
  19. odoo/addons/odoo_repository/models/__init__.py +18 -0
  20. odoo/addons/odoo_repository/models/authentication_token.py +12 -0
  21. odoo/addons/odoo_repository/models/odoo_author.py +16 -0
  22. odoo/addons/odoo_repository/models/odoo_branch.py +111 -0
  23. odoo/addons/odoo_repository/models/odoo_license.py +16 -0
  24. odoo/addons/odoo_repository/models/odoo_maintainer.py +31 -0
  25. odoo/addons/odoo_repository/models/odoo_module.py +24 -0
  26. odoo/addons/odoo_repository/models/odoo_module_branch.py +873 -0
  27. odoo/addons/odoo_repository/models/odoo_module_branch_version.py +123 -0
  28. odoo/addons/odoo_repository/models/odoo_module_category.py +15 -0
  29. odoo/addons/odoo_repository/models/odoo_module_dev_status.py +15 -0
  30. odoo/addons/odoo_repository/models/odoo_python_dependency.py +16 -0
  31. odoo/addons/odoo_repository/models/odoo_repository.py +664 -0
  32. odoo/addons/odoo_repository/models/odoo_repository_addons_path.py +40 -0
  33. odoo/addons/odoo_repository/models/odoo_repository_branch.py +98 -0
  34. odoo/addons/odoo_repository/models/odoo_repository_org.py +23 -0
  35. odoo/addons/odoo_repository/models/res_company.py +23 -0
  36. odoo/addons/odoo_repository/models/res_config_settings.py +23 -0
  37. odoo/addons/odoo_repository/models/ssh_key.py +12 -0
  38. odoo/addons/odoo_repository/readme/CONTRIBUTORS.rst +2 -0
  39. odoo/addons/odoo_repository/readme/DESCRIPTION.rst +1 -0
  40. odoo/addons/odoo_repository/security/ir.model.access.csv +27 -0
  41. odoo/addons/odoo_repository/security/res_groups.xml +25 -0
  42. odoo/addons/odoo_repository/static/description/README +4 -0
  43. odoo/addons/odoo_repository/static/description/icon.png +0 -0
  44. odoo/addons/odoo_repository/static/description/index.html +430 -0
  45. odoo/addons/odoo_repository/tests/__init__.py +6 -0
  46. odoo/addons/odoo_repository/tests/common.py +162 -0
  47. odoo/addons/odoo_repository/tests/test_base_scanner.py +214 -0
  48. odoo/addons/odoo_repository/tests/test_odoo_module_branch.py +97 -0
  49. odoo/addons/odoo_repository/tests/test_odoo_repository_scan.py +242 -0
  50. odoo/addons/odoo_repository/tests/test_repository_scanner.py +215 -0
  51. odoo/addons/odoo_repository/tests/test_sync_node.py +55 -0
  52. odoo/addons/odoo_repository/tests/test_utils.py +25 -0
  53. odoo/addons/odoo_repository/utils/__init__.py +0 -0
  54. odoo/addons/odoo_repository/utils/github.py +30 -0
  55. odoo/addons/odoo_repository/utils/module.py +25 -0
  56. odoo/addons/odoo_repository/utils/scanner.py +90 -0
  57. odoo/addons/odoo_repository/views/authentication_token.xml +63 -0
  58. odoo/addons/odoo_repository/views/menu.xml +38 -0
  59. odoo/addons/odoo_repository/views/odoo_author.xml +54 -0
  60. odoo/addons/odoo_repository/views/odoo_branch.xml +84 -0
  61. odoo/addons/odoo_repository/views/odoo_license.xml +40 -0
  62. odoo/addons/odoo_repository/views/odoo_maintainer.xml +69 -0
  63. odoo/addons/odoo_repository/views/odoo_module.xml +90 -0
  64. odoo/addons/odoo_repository/views/odoo_module_branch.xml +353 -0
  65. odoo/addons/odoo_repository/views/odoo_module_category.xml +40 -0
  66. odoo/addons/odoo_repository/views/odoo_module_dev_status.xml +40 -0
  67. odoo/addons/odoo_repository/views/odoo_python_dependency.xml +40 -0
  68. odoo/addons/odoo_repository/views/odoo_repository.xml +165 -0
  69. odoo/addons/odoo_repository/views/odoo_repository_addons_path.xml +49 -0
  70. odoo/addons/odoo_repository/views/odoo_repository_branch.xml +60 -0
  71. odoo/addons/odoo_repository/views/odoo_repository_org.xml +54 -0
  72. odoo/addons/odoo_repository/views/res_config_settings.xml +123 -0
  73. odoo/addons/odoo_repository/views/ssh_key.xml +63 -0
  74. odoo_addon_odoo_repository-16.0.1.3.0.13.dist-info/METADATA +100 -0
  75. odoo_addon_odoo_repository-16.0.1.3.0.13.dist-info/RECORD +77 -0
  76. odoo_addon_odoo_repository-16.0.1.3.0.13.dist-info/WHEEL +5 -0
  77. odoo_addon_odoo_repository-16.0.1.3.0.13.dist-info/top_level.txt +1 -0
@@ -0,0 +1,214 @@
1
+ # Copyright 2024 Camptocamp SA
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
3
+
4
+ import tempfile
5
+
6
+ from odoo.addons.odoo_repository.lib.scanner import BaseScanner
7
+
8
+ from .common import Common
9
+
10
+
11
+ class TestBaseScanner(Common):
12
+ def _init_scanner(self, **params):
13
+ kwargs = {
14
+ "org": self.fork_org,
15
+ "name": self.repo_name,
16
+ "clone_url": self.repo_upstream_path,
17
+ "branches": [
18
+ self.branch1_name,
19
+ self.branch2_name,
20
+ self.branch3_name,
21
+ ],
22
+ "repositories_path": self.repositories_path,
23
+ }
24
+ if params:
25
+ kwargs.update(params)
26
+ return BaseScanner(**kwargs)
27
+
28
+ def test_init(self):
29
+ scanner = self._init_scanner()
30
+ self.assertTrue(scanner.repositories_path.exists())
31
+ self.assertEqual(scanner.path.parts[-1], self.repo_name)
32
+ self.assertEqual(scanner.path.parts[-2], self.fork_org)
33
+ self.assertEqual(scanner.full_name, f"{self.fork_org}/{self.repo_name}")
34
+
35
+ def test_clone_url_github_token(self):
36
+ # Without token
37
+ base_clone_url = "https://github.com/OCA/test"
38
+ scanner = self._init_scanner(repo_type="github", clone_url=base_clone_url)
39
+ self.assertEqual(scanner.clone_url, base_clone_url)
40
+ # With a token
41
+ token = "test"
42
+ scanner = self._init_scanner(
43
+ repo_type="github", clone_url=base_clone_url, token=token
44
+ )
45
+ token_clone_url = f"https://oauth2:{token}@github.com/OCA/test"
46
+ self.assertEqual(scanner.clone_url, token_clone_url)
47
+
48
+ def test_sync(self):
49
+ scanner = self._init_scanner(repositories_path=tempfile.mkdtemp())
50
+ # Clone
51
+ self.assertFalse(scanner.path.exists())
52
+ self.assertFalse(scanner.is_cloned)
53
+ scanner.sync()
54
+ self.assertTrue(scanner.is_cloned)
55
+ # Fetch once cloned
56
+ scanner.sync()
57
+
58
+ def test_branch_exists(self):
59
+ scanner = self._init_scanner()
60
+ scanner.sync()
61
+ with scanner.repo() as repo:
62
+ self.assertTrue(scanner._branch_exists(repo, self.branch1_name))
63
+ self.assertTrue(scanner._branch_exists(repo, self.branch2_name))
64
+ self.assertTrue(scanner._branch_exists(repo, self.branch3_name))
65
+
66
+ def test_checkout_branch(self):
67
+ scanner = self._init_scanner()
68
+ scanner.sync()
69
+ with scanner.repo() as repo:
70
+ branch = self.branch2_name
71
+ branch_sha = repo.refs[f"origin/{branch}"].object.hexsha
72
+ self.assertNotEqual(repo.head.object.hexsha, branch_sha)
73
+ scanner._checkout_branch(repo, branch)
74
+ self.assertEqual(repo.head.object.hexsha, branch_sha)
75
+
76
+ def test_get_last_fetched_commit(self):
77
+ scanner = self._init_scanner()
78
+ scanner.sync()
79
+ with scanner.repo() as repo:
80
+ branch1 = self.branch1_name
81
+ branch2 = self.branch2_name
82
+ branch3 = self.branch3_name
83
+ branch1_sha = repo.refs[f"origin/{branch1}"].object.hexsha
84
+ branch2_sha = repo.refs[f"origin/{branch2}"].object.hexsha
85
+ branch3_sha = repo.refs[f"origin/{branch3}"].object.hexsha
86
+ self.assertEqual(
87
+ scanner._get_last_fetched_commit(repo, branch1), branch1_sha
88
+ )
89
+ self.assertEqual(
90
+ scanner._get_last_fetched_commit(repo, branch2), branch2_sha
91
+ )
92
+ self.assertEqual(
93
+ scanner._get_last_fetched_commit(repo, branch3), branch3_sha
94
+ )
95
+
96
+ def test_get_module_paths(self):
97
+ scanner = self._init_scanner()
98
+ scanner.sync()
99
+ with scanner.repo() as repo:
100
+ branch = self.branch1_name
101
+ module_paths = scanner._get_module_paths(repo, ".", branch)
102
+ self.assertEqual(len(module_paths), 1)
103
+ self.assertEqual(module_paths[0], self.addon)
104
+
105
+ def test_get_module_paths_updated(self):
106
+ scanner = self._init_scanner()
107
+ scanner.sync()
108
+ branch = self.branch1_name
109
+ with scanner.repo() as repo:
110
+ initial_commit = scanner._get_last_fetched_commit(repo, branch)
111
+ # Case where from_commit and to_commit are the same: no change detected
112
+ module_paths = scanner._get_module_paths_updated(
113
+ repo,
114
+ relative_path=".",
115
+ from_commit=initial_commit,
116
+ to_commit=initial_commit,
117
+ branch=branch,
118
+ )
119
+ self.assertFalse(module_paths)
120
+ # Update the upstream repository with a new commit
121
+ self._update_module_version_on_branch(branch, "1.0.1")
122
+ # Module is now detected has updated
123
+ scanner.sync() # Fetch new commit from upstream repo
124
+ with scanner.repo() as repo:
125
+ last_commit = scanner._get_last_fetched_commit(repo, branch)
126
+ module_paths = scanner._get_module_paths_updated(
127
+ repo,
128
+ relative_path=".",
129
+ from_commit=initial_commit,
130
+ to_commit=last_commit,
131
+ branch=branch,
132
+ )
133
+ self.assertEqual(len(module_paths), 1)
134
+ self.assertEqual(module_paths.pop(), self.addon)
135
+
136
+ def test_filter_file_path(self):
137
+ scanner = self._init_scanner()
138
+ self.assertFalse(scanner._filter_file_path("fr.po"))
139
+ self.assertTrue(scanner._filter_file_path("test.py"))
140
+
141
+ def test_get_last_commit_of_git_tree(self):
142
+ scanner = self._init_scanner()
143
+ scanner.sync()
144
+ with scanner.repo() as repo:
145
+ branch = self.branch1_name
146
+ remote_branch = f"origin/{branch}"
147
+ module = self.addon
148
+ module_tree = repo.tree(remote_branch) / module
149
+ all_commits = [c.hexsha for c in repo.iter_commits(remote_branch)]
150
+ commit = scanner._get_last_commit_of_git_tree(remote_branch, module_tree)
151
+ self.assertIn(commit, all_commits)
152
+
153
+ def test_get_commits_of_git_tree(self):
154
+ scanner = self._init_scanner()
155
+ scanner.sync()
156
+ with scanner.repo() as repo:
157
+ branch = self.branch1_name
158
+ remote_branch = f"origin/{branch}"
159
+ module = self.addon
160
+ module_tree = repo.tree(remote_branch) / module
161
+ all_commits = [c.hexsha for c in repo.iter_commits(remote_branch)]
162
+ commits = scanner._get_commits_of_git_tree(
163
+ from_=None, to_=remote_branch, tree=module_tree
164
+ )
165
+ for commit in commits:
166
+ self.assertIn(commit, all_commits)
167
+
168
+ def test_odoo_module(self):
169
+ scanner = self._init_scanner()
170
+ scanner.sync()
171
+ with scanner.repo() as repo:
172
+ branch = self.branch1_name
173
+ remote_branch = f"origin/{branch}"
174
+ module = self.addon
175
+ module_tree = repo.tree(remote_branch) / module
176
+ self.assertTrue(scanner._odoo_module(module_tree))
177
+
178
+ def test_manifest_exists(self):
179
+ scanner = self._init_scanner()
180
+ scanner.sync()
181
+ with scanner.repo() as repo:
182
+ branch = self.branch1_name
183
+ remote_branch = f"origin/{branch}"
184
+ # Check module tree: OK
185
+ module = self.addon
186
+ module_tree = repo.tree(remote_branch) / module
187
+ self.assertTrue(scanner._manifest_exists(module_tree))
188
+ # Check repository root tree: KO
189
+ self.assertFalse(scanner._manifest_exists(repo.tree(remote_branch)))
190
+
191
+ def test_get_subtree(self):
192
+ scanner = self._init_scanner()
193
+ scanner.sync()
194
+ with scanner.repo() as repo:
195
+ branch = self.branch1_name
196
+ remote_branch = f"origin/{branch}"
197
+ module = self.addon
198
+ # Module/folder exists: OK
199
+ self.assertTrue(scanner._get_subtree(repo.tree(remote_branch), module))
200
+ # Module/folder doesn't exist: KO
201
+ self.assertFalse(scanner._get_subtree(repo.tree(remote_branch), "none"))
202
+
203
+ def test_workaround_fs_errors(self):
204
+ scanner = self._init_scanner(
205
+ repositories_path=tempfile.mkdtemp(),
206
+ workaround_fs_errors=True,
207
+ )
208
+ # Clone
209
+ self.assertFalse(scanner.path.exists())
210
+ self.assertFalse(scanner.is_cloned)
211
+ scanner.sync()
212
+ self.assertTrue(scanner.is_cloned)
213
+ # Fetch once cloned
214
+ scanner.sync()
@@ -0,0 +1,97 @@
1
+ # Copyright 2024 Camptocamp SA
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
3
+
4
+ from odoo.exceptions import ValidationError
5
+
6
+ from .common import Common
7
+
8
+
9
+ class TestOdooModuleBranch(Common):
10
+ def test_constraint_generic_depends_on_specific(self):
11
+ generic_mod = self._create_odoo_module("generic_mod")
12
+ generic_mod_branch = self._create_odoo_module_branch(
13
+ generic_mod, self.branch, specific=False
14
+ )
15
+ specific_mod = self._create_odoo_module("specific_mod")
16
+ specific_mod_branch = self._create_odoo_module_branch(
17
+ specific_mod, self.branch, specific=True
18
+ )
19
+ with self.assertRaises(ValidationError):
20
+ generic_mod_branch.dependency_ids = specific_mod_branch
21
+
22
+ def test_dependency_level(self):
23
+ # base module in the dependencies tree
24
+ mod_base = self._create_odoo_module("base")
25
+ mod_base_branch = self._create_odoo_module_branch(
26
+ mod_base, self.branch, is_standard=True
27
+ )
28
+ self.assertEqual(mod_base_branch.global_dependency_level, 1)
29
+ self.assertEqual(mod_base_branch.non_std_dependency_level, 0)
30
+ # add a standard module depending on the base one
31
+ mod_std = self._create_odoo_module("std")
32
+ mod_std_branch = self._create_odoo_module_branch(
33
+ mod_std,
34
+ self.branch,
35
+ is_standard=True,
36
+ dependency_ids=[(4, mod_base_branch.id)],
37
+ )
38
+ self.assertEqual(mod_std_branch.global_dependency_level, 2)
39
+ self.assertEqual(mod_std_branch.non_std_dependency_level, 0)
40
+ # add a non-standard module depending on the std one
41
+ mod_non_std = self._create_odoo_module("non_std")
42
+ mod_non_std_branch = self._create_odoo_module_branch(
43
+ mod_non_std,
44
+ self.branch,
45
+ is_standard=False,
46
+ dependency_ids=[(4, mod_std_branch.id)],
47
+ )
48
+ self.assertEqual(mod_non_std_branch.global_dependency_level, 3)
49
+ self.assertEqual(mod_non_std_branch.non_std_dependency_level, 1)
50
+ # add another one depending on base module
51
+ mod_non_std2 = self._create_odoo_module("non_std2")
52
+ mod_non_std2_branch = self._create_odoo_module_branch(
53
+ mod_non_std2,
54
+ self.branch,
55
+ is_standard=False,
56
+ dependency_ids=[(4, mod_base_branch.id)],
57
+ )
58
+ self.assertEqual(mod_non_std2_branch.global_dependency_level, 2)
59
+ self.assertEqual(mod_non_std2_branch.non_std_dependency_level, 1)
60
+ # add another one depending on non-std module
61
+ mod_non_std3 = self._create_odoo_module("non_std3")
62
+ mod_non_std3_branch = self._create_odoo_module_branch(
63
+ mod_non_std3,
64
+ self.branch,
65
+ is_standard=False,
66
+ dependency_ids=[(4, mod_non_std_branch.id)],
67
+ )
68
+ self.assertEqual(mod_non_std3_branch.global_dependency_level, 4)
69
+ self.assertEqual(mod_non_std3_branch.non_std_dependency_level, 2)
70
+
71
+ def test_find(self):
72
+ mb_model = self.env["odoo.module.branch"]
73
+ mod = self._create_odoo_module("my_module")
74
+ repo = self.odoo_repository
75
+ repo2 = self.odoo_repository.copy({"name": "Repo2"})
76
+ # Find orphaned module
77
+ mod_orphaned = mb_model._create_orphaned_module_branch(self.branch, mod)
78
+ self.assertEqual(mb_model._find(self.branch, mod, repo), mod_orphaned)
79
+ # Find generic module
80
+ repo_branch = self._create_odoo_repository_branch(repo, self.branch)
81
+ mod_generic = self._create_odoo_module_branch(
82
+ mod,
83
+ self.branch,
84
+ specific=False,
85
+ repository_branch_id=repo_branch.id,
86
+ )
87
+ self.assertEqual(mb_model._find(self.branch, mod, repo), mod_generic)
88
+ # Find module in current repository
89
+ repo2_branch = self._create_odoo_repository_branch(repo2, self.branch)
90
+ mod_in_repo2 = self._create_odoo_module_branch(
91
+ mod,
92
+ self.branch,
93
+ repository_branch_id=repo2_branch.id,
94
+ )
95
+ self.assertEqual(mb_model._find(self.branch, mod, repo2), mod_in_repo2)
96
+ # While we have 3 modules (hosted in different repos or orphaned)
97
+ self.assertEqual(len(mod.module_branch_ids), 3)
@@ -0,0 +1,242 @@
1
+ # Copyright 2024 Camptocamp SA
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
3
+
4
+ from odoo import fields
5
+
6
+ from .common import Common
7
+
8
+
9
+ class TestOdooRepositoryScan(Common):
10
+ def test_check_config(self):
11
+ self.odoo_repository._check_config()
12
+
13
+ def test_action_scan_basic(self):
14
+ """Test the creation of a module when scanning a (generic) repository."""
15
+ self.assertFalse(self.odoo_repository.specific)
16
+ module = self.env["odoo.module"].search([("name", "=", self.module_name)])
17
+ self.assertFalse(module)
18
+ self._run_odoo_repository_action_scan(self.branch.id)
19
+ # Check module technical name
20
+ module = self.env["odoo.module"].search([("name", "=", self.module_name)])
21
+ self.assertTrue(module)
22
+ # Check module branch
23
+ module_branch = self.env["odoo.module.branch"].search(
24
+ [("module_id", "=", module.id), ("branch_id", "=", self.branch.id)]
25
+ )
26
+ self.assertEqual(module_branch.module_name, self.module_name)
27
+ self.assertTrue(module_branch.last_scanned_commit)
28
+ self.assertEqual(module_branch.repository_id, self.odoo_repository)
29
+ self.assertEqual(module_branch.org_id, self.org)
30
+ self.assertEqual(module_branch.title, "Test")
31
+ self.assertEqual(module_branch.category_id.name, "Test Module")
32
+ self.assertItemsEqual(
33
+ module_branch.author_ids.mapped("name"),
34
+ ["Odoo Community Association (OCA)", "Camptocamp"],
35
+ )
36
+ self.assertFalse(module_branch.specific)
37
+ self.assertEqual(module_branch.dependency_ids.module_name, "base")
38
+ self.assertFalse(module_branch.dependency_ids.specific)
39
+ self.assertEqual(module_branch.license_id.name, "AGPL-3")
40
+ self.assertEqual(module_branch.version, "1.0.0")
41
+ self.assertEqual(module_branch.version_ids.manifest_value, "1.0.0")
42
+ self.assertEqual(module_branch.version_ids.name, f"{self.branch.name}.1.0.0")
43
+ self.assertEqual(
44
+ module_branch.version_ids.commit, module_branch.last_scanned_commit
45
+ )
46
+ self.assertFalse(module_branch.version_ids.has_migration_script)
47
+ self.assertTrue(module_branch.sloc_python)
48
+ self.assertEqual(module_branch.addons_path, ".")
49
+ # Check repository branch
50
+ repo_branch = module_branch.repository_branch_id
51
+ self.assertEqual(repo_branch.branch_id, self.branch)
52
+ self.assertEqual(
53
+ repo_branch.last_scanned_commit, module_branch.last_scanned_commit
54
+ )
55
+
56
+ def test_action_scan_repo_specific(self):
57
+ """Test the creation of a module when scanning a specific repository."""
58
+ self.odoo_repository.specific = True
59
+ self.odoo_repository.write(
60
+ {
61
+ "specific": True,
62
+ "branch_ids": [
63
+ fields.Command.create({"branch_id": self.branch.id}),
64
+ ],
65
+ }
66
+ )
67
+ self.assertTrue(self.odoo_repository.specific)
68
+ self._run_odoo_repository_action_scan(self.branch.id)
69
+ # Check module data
70
+ module = self.env["odoo.module"].search([("name", "=", self.module_name)])
71
+ module_branch = self.env["odoo.module.branch"].search(
72
+ [("module_id", "=", module.id), ("branch_id", "=", self.branch.id)]
73
+ )
74
+ self.assertTrue(module_branch.specific)
75
+ self.assertFalse(module_branch.dependency_ids.specific)
76
+
77
+ def test_action_scan_repo_module_exists(self):
78
+ """Test the update of existing module when scanning a repository.
79
+
80
+ If a module has already been scanned in the current repository, a second
81
+ scan will trigger an update of its data.
82
+ """
83
+ # First scan, like in `test_action_scan_first_time`
84
+ self._run_odoo_repository_action_scan(self.branch.id)
85
+ module = self.env["odoo.module"].search([("name", "=", self.module_name)])
86
+ module_branch = self.env["odoo.module.branch"].search(
87
+ [("module_id", "=", module.id), ("branch_id", "=", self.branch.id)]
88
+ )
89
+ # Change some data in the module before triggering the second scan
90
+ module_branch.write({"title": False})
91
+ # Launch a second scan (force it to make it happen)
92
+ self._run_odoo_repository_action_scan(self.branch.id, force=True)
93
+ self.assertEqual(module_branch.title, "Test")
94
+
95
+ def test_action_scan_orphaned_module_exists(self):
96
+ """Test the link of an orphaned module when scanning a repository.
97
+
98
+ An orphaned module is a record without repository assigned while we know
99
+ its name and branch. Such record is automatically created when generating
100
+ dependencies of a given module that have not been scanned yet, or by
101
+ importing installed modules in a project (see `odoo_project` Odoo module).
102
+
103
+ If such a module matches a module scanned in a repository, it is updated
104
+ accordingly to belong to this repository.
105
+ """
106
+ # Create an orphaned module.
107
+ # To ease its creation, we run a scan to get the record created, and
108
+ # we update it to make it orphaned.
109
+ self._run_odoo_repository_action_scan(self.branch.id)
110
+ module = self.env["odoo.module"].search([("name", "=", self.module_name)])
111
+ module_branch = self.env["odoo.module.branch"].search(
112
+ [("module_id", "=", module.id), ("branch_id", "=", self.branch.id)]
113
+ )
114
+ module_branch.write(
115
+ {
116
+ "repository_branch_id": False,
117
+ "last_scanned_commit": False,
118
+ "dependency_ids": False,
119
+ }
120
+ )
121
+ # Launch a scan
122
+ self._run_odoo_repository_action_scan(self.branch.id, force=True)
123
+ self.assertEqual(module_branch.repository_id, self.odoo_repository)
124
+
125
+ def _create_wrong_repo_branch(self, repo_sequence=100):
126
+ wrong_repo = self.env["odoo.repository"].create(
127
+ {
128
+ "org_id": self.odoo_repository.org_id.id,
129
+ "name": "wrong_repo",
130
+ "repo_url": "https://example.net/OCA-test/wrong_repo",
131
+ "clone_url": "https://example.net/OCA-test/wrong_repo",
132
+ "repo_type": "github",
133
+ "sequence": repo_sequence,
134
+ }
135
+ )
136
+ wrong_repo_branch = self.env["odoo.repository.branch"].create(
137
+ {
138
+ "repository_id": wrong_repo.id,
139
+ "branch_id": self.branch.id,
140
+ }
141
+ )
142
+ return wrong_repo_branch
143
+
144
+ def _create_unmerged_module_branch(self):
145
+ # To ease the creation of such module, we run a scan to get the record
146
+ # created, and we update it to make it unmerged/pending.
147
+ self._run_odoo_repository_action_scan(self.branch.id)
148
+ module = self.env["odoo.module"].search([("name", "=", self.module_name)])
149
+ module_branch = self.env["odoo.module.branch"].search(
150
+ [("module_id", "=", module.id), ("branch_id", "=", self.branch.id)]
151
+ )
152
+ wrong_repo_branch = self._create_wrong_repo_branch(
153
+ # Lower priority than self.odoo_repository
154
+ repo_sequence=200
155
+ )
156
+ wrong_repo = wrong_repo_branch.repository_id
157
+ module_branch.write(
158
+ {
159
+ "repository_branch_id": wrong_repo_branch,
160
+ "last_scanned_commit": False,
161
+ "dependency_ids": False,
162
+ "pr_url": f"{wrong_repo.repo_url}/pull/1",
163
+ "specific": False,
164
+ }
165
+ )
166
+ return module_branch
167
+
168
+ def test_action_scan_repo_generic_unmerged_module_exists(self):
169
+ """Test link of an unmerged module when scanning a generic repository.
170
+
171
+ An unmerged module is like an orphaned module but with a PR attached.
172
+ However such PR indicates from which repository a module is coming from,
173
+ but this information could also be wrong (wrong PR detected on the wrong
174
+ repository).
175
+
176
+ Note: unmerged modules can only be generic, as PR detection is restricted
177
+ only to generic modules.
178
+
179
+ When scanning a generic repository, if an unmerged module is
180
+ detected it should be linked to this scanned repository.
181
+ """
182
+ self.odoo_repository.specific = False
183
+ # Create an unmerged module
184
+ module_branch = self._create_unmerged_module_branch()
185
+ self.assertFalse(module_branch.specific)
186
+ self.assertNotEqual(module_branch.repository_id, self.odoo_repository)
187
+ # Launch a scan
188
+ self._run_odoo_repository_action_scan(self.branch.id, force=True)
189
+ self.assertFalse(module_branch.specific)
190
+ self.assertEqual(module_branch.repository_id, self.odoo_repository)
191
+
192
+ def test_action_scan_repo_specific_unmerged_module_exists(self):
193
+ """Test non-link of an unmerged module when scanning a specific repository.
194
+
195
+ When scanning a specific repository, detection of unmerged modules is
196
+ not done as such modules are linked to generic repositories only.
197
+ """
198
+ self.odoo_repository.specific = True
199
+ # Create an unmerged module
200
+ module_branch = self._create_unmerged_module_branch()
201
+ self.assertFalse(module_branch.specific)
202
+ self.assertNotEqual(module_branch.repository_id, self.odoo_repository)
203
+ # Launch a scan
204
+ self._run_odoo_repository_action_scan(self.branch.id, force=True)
205
+ # Unmerged module hasn't been attached to the scanned repository
206
+ self.assertNotEqual(module_branch.repository_id, self.odoo_repository)
207
+
208
+ def test_action_scan_uninstallable_module(self):
209
+ """Test scan of an 'installable: False' module.
210
+
211
+ Such module should not be created with its dependencies (Odoo, Python...)
212
+ or versions history to not pollute the DB. Such data could be
213
+ outdated as the module is flagged as not installable. They will be updated
214
+ once the module is migrated/installable.
215
+ """
216
+ self._update_module_installable_on_branch(self.branch.name, installable=False)
217
+ module = self.env["odoo.module"].search([("name", "=", self.module_name)])
218
+ self.assertFalse(module)
219
+ self._run_odoo_repository_action_scan(self.branch.id)
220
+ module = self.env["odoo.module"].search([("name", "=", self.module_name)])
221
+ self.assertTrue(module)
222
+ # Check module branch
223
+ module_branch = self.env["odoo.module.branch"].search(
224
+ [("module_id", "=", module.id), ("branch_id", "=", self.branch.id)]
225
+ )
226
+ self.assertEqual(module_branch.module_name, self.module_name)
227
+ self.assertTrue(module_branch.last_scanned_commit)
228
+ self.assertEqual(module_branch.repository_id, self.odoo_repository)
229
+ self.assertEqual(module_branch.org_id, self.org)
230
+ self.assertEqual(module_branch.title, "Test")
231
+ self.assertEqual(module_branch.category_id.name, "Test Module")
232
+ self.assertItemsEqual(
233
+ module_branch.author_ids.mapped("name"),
234
+ ["Odoo Community Association (OCA)", "Camptocamp"],
235
+ )
236
+ self.assertFalse(module_branch.specific)
237
+ # No dependencies
238
+ self.assertFalse(module_branch.dependency_ids)
239
+ self.assertFalse(module_branch.external_dependencies)
240
+ self.assertFalse(module_branch.python_dependency_ids)
241
+ # No version scanned
242
+ self.assertFalse(module_branch.version_ids)