|
5 | 5 | import base64 |
6 | 6 | from unittest import mock |
7 | 7 |
|
| 8 | +from lxml import etree |
| 9 | + |
8 | 10 | from odoo.exceptions import UserError |
9 | 11 | from odoo.tests import Form |
10 | 12 |
|
@@ -74,6 +76,33 @@ def test_swap_requires_destination_filename_strategy(self): |
74 | 76 | with self.assertRaisesRegex(UserError, "The filename strategy is empty"): |
75 | 77 | stfile._swap_backend(self.backend_b) |
76 | 78 |
|
| 79 | + def test_swap_rejects_different_backend_category(self): |
| 80 | + src_categ = self.env["storage.backend.category"].create({"name": "SRC"}) |
| 81 | + dst_categ = self.env["storage.backend.category"].create({"name": "DST"}) |
| 82 | + self.backend_a.categ_id = src_categ |
| 83 | + self.backend_b.categ_id = dst_categ |
| 84 | + stfile = self._create_storage_file(backend=self.backend_a) |
| 85 | + |
| 86 | + with self.assertRaisesRegex( |
| 87 | + UserError, "Destination backend category must match source backend category" |
| 88 | + ): |
| 89 | + stfile._swap_backend(self.backend_b) |
| 90 | + |
| 91 | + def test_swap_allows_different_backend_category_with_bypass(self): |
| 92 | + src_categ = self.env["storage.backend.category"].create({"name": "SRC"}) |
| 93 | + dst_categ = self.env["storage.backend.category"].create({"name": "DST"}) |
| 94 | + self.backend_a.categ_id = src_categ |
| 95 | + self.backend_b.categ_id = dst_categ |
| 96 | + stfile = self._create_storage_file(backend=self.backend_a, data=b"payload") |
| 97 | + |
| 98 | + result = stfile.with_context( |
| 99 | + swap_backend_bypass_category_check=True |
| 100 | + )._swap_backend(self.backend_b) |
| 101 | + |
| 102 | + self.assertEqual(stfile.backend_id, self.backend_b) |
| 103 | + self.assertEqual(base64.b64decode(stfile.data), b"payload") |
| 104 | + self.assertIn(stfile.name, result["moved"][0]) |
| 105 | + |
77 | 106 | def test_swap_failure_reports_in_failed(self): |
78 | 107 | """Upload failure is caught and reported in the failed list.""" |
79 | 108 | stfile = self._create_storage_file(data=b"payload") |
@@ -204,3 +233,57 @@ def test_write_backend_id_noop_if_same(self): |
204 | 233 | stfile.backend_id = self.backend_a |
205 | 234 | self.assertEqual(stfile.backend_id, self.backend_a) |
206 | 235 | self.assertEqual(stfile.relative_path, old_path) |
| 236 | + |
| 237 | + # -- category-based filtering ---------------------------------------- |
| 238 | + |
| 239 | + def test_wizard_form_same_category_shown_as_dest(self): |
| 240 | + """Wizard declares and applies a same-category destination domain.""" |
| 241 | + categ = self.env["storage.backend.category"].create({"name": "Group A"}) |
| 242 | + categ2 = self.env["storage.backend.category"].create({"name": "Group B"}) |
| 243 | + backend_a_cat = self.backend_a.copy( |
| 244 | + { |
| 245 | + "name": "Backend A (Group A)", |
| 246 | + "categ_id": categ.id, |
| 247 | + "directory_path": "a_cat", |
| 248 | + } |
| 249 | + ) |
| 250 | + backend_b_cat = self.backend_b.copy( |
| 251 | + { |
| 252 | + "name": "Backend B (Group A)", |
| 253 | + "categ_id": categ.id, |
| 254 | + "directory_path": "b_cat", |
| 255 | + } |
| 256 | + ) |
| 257 | + backend_other = self.backend_a.copy( |
| 258 | + { |
| 259 | + "name": "Backend (Group B)", |
| 260 | + "categ_id": categ2.id, |
| 261 | + "directory_path": "other", |
| 262 | + } |
| 263 | + ) |
| 264 | + stfile = self._create_storage_file(backend=backend_a_cat) |
| 265 | + |
| 266 | + # Assert the actual domain declared in the form view arch. |
| 267 | + view = self.env.ref("storage_file.storage_file_swap_backend_view_form") |
| 268 | + xml = etree.fromstring(view.arch_db.encode()) |
| 269 | + dest_field = xml.xpath("//field[@name='dest_backend_id']") |
| 270 | + self.assertEqual(len(dest_field), 1) |
| 271 | + domain = dest_field[0].get("domain") |
| 272 | + self.assertIn("('id', '!=', source_backend_id)", domain) |
| 273 | + self.assertIn("('categ_id', '=', source_backend_categ_id)", domain) |
| 274 | + self.assertNotIn("source_backend_id.categ_id", domain) |
| 275 | + |
| 276 | + with Form( |
| 277 | + self.env["storage.file.swap.backend"].with_context( |
| 278 | + active_model="storage.file", |
| 279 | + active_ids=stfile.ids, |
| 280 | + ) |
| 281 | + ) as wiz_form: |
| 282 | + self.assertEqual(wiz_form.source_backend_id, backend_a_cat) |
| 283 | + # domain: categ_id = Group A → Group B backend excluded |
| 284 | + same_categ_backends = self.env["storage.backend"].search( |
| 285 | + [("categ_id", "=", categ.id), ("id", "!=", backend_a_cat.id)] |
| 286 | + ) |
| 287 | + self.assertIn(backend_b_cat, same_categ_backends) |
| 288 | + self.assertNotIn(backend_other, same_categ_backends) |
| 289 | + wiz_form.dest_backend_id = backend_b_cat |
0 commit comments