c2cgeoportal-admin 2.5.0.100__py3-none-any.whl → 2.9rc44__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 (99) hide show
  1. c2cgeoportal_admin/__init__.py +44 -14
  2. c2cgeoportal_admin/lib/__init__.py +0 -0
  3. c2cgeoportal_admin/lib/lingva_extractor.py +77 -0
  4. c2cgeoportal_admin/lib/ogcserver_synchronizer.py +410 -0
  5. c2cgeoportal_admin/py.typed +0 -0
  6. c2cgeoportal_admin/routes.py +30 -11
  7. c2cgeoportal_admin/schemas/dimensions.py +17 -11
  8. c2cgeoportal_admin/schemas/functionalities.py +60 -22
  9. c2cgeoportal_admin/schemas/interfaces.py +27 -19
  10. c2cgeoportal_admin/schemas/metadata.py +122 -48
  11. c2cgeoportal_admin/schemas/restriction_areas.py +26 -20
  12. c2cgeoportal_admin/schemas/roles.py +13 -7
  13. c2cgeoportal_admin/schemas/treegroup.py +90 -20
  14. c2cgeoportal_admin/schemas/treeitem.py +3 -4
  15. c2cgeoportal_admin/static/layertree.css +26 -4
  16. c2cgeoportal_admin/static/navbar.css +59 -36
  17. c2cgeoportal_admin/static/theme.css +51 -11
  18. c2cgeoportal_admin/subscribers.py +3 -3
  19. c2cgeoportal_admin/templates/404.jinja2 +41 -2
  20. c2cgeoportal_admin/templates/edit.jinja2 +23 -0
  21. c2cgeoportal_admin/templates/home.jinja2 +23 -0
  22. c2cgeoportal_admin/templates/index.jinja2 +23 -0
  23. c2cgeoportal_admin/templates/layertree.jinja2 +55 -11
  24. c2cgeoportal_admin/templates/layout.jinja2 +23 -0
  25. c2cgeoportal_admin/templates/navigation_navbar.jinja2 +56 -0
  26. c2cgeoportal_admin/templates/ogcserver_synchronize.jinja2 +90 -0
  27. c2cgeoportal_admin/templates/widgets/child.pt +35 -3
  28. c2cgeoportal_admin/templates/widgets/children.pt +121 -92
  29. c2cgeoportal_admin/templates/widgets/dimension.pt +23 -0
  30. c2cgeoportal_admin/templates/widgets/dimensions.pt +23 -0
  31. c2cgeoportal_admin/templates/widgets/functionality_fields.pt +51 -0
  32. c2cgeoportal_admin/templates/widgets/layer_fields.pt +23 -0
  33. c2cgeoportal_admin/templates/widgets/layer_group_fields.pt +23 -0
  34. c2cgeoportal_admin/templates/widgets/layer_v1_fields.pt +23 -0
  35. c2cgeoportal_admin/templates/widgets/metadata.pt +30 -1
  36. c2cgeoportal_admin/templates/widgets/metadatas.pt +23 -0
  37. c2cgeoportal_admin/templates/widgets/ogcserver_fields.pt +23 -0
  38. c2cgeoportal_admin/templates/widgets/restriction_area_fields.pt +25 -9
  39. c2cgeoportal_admin/templates/widgets/role_fields.pt +52 -25
  40. c2cgeoportal_admin/templates/widgets/theme_fields.pt +23 -0
  41. c2cgeoportal_admin/templates/widgets/user_fields.pt +23 -0
  42. c2cgeoportal_admin/views/__init__.py +29 -0
  43. c2cgeoportal_admin/views/dimension_layers.py +14 -9
  44. c2cgeoportal_admin/views/functionalities.py +52 -18
  45. c2cgeoportal_admin/views/home.py +5 -5
  46. c2cgeoportal_admin/views/interfaces.py +29 -21
  47. c2cgeoportal_admin/views/layer_groups.py +36 -25
  48. c2cgeoportal_admin/views/layers.py +17 -13
  49. c2cgeoportal_admin/views/layers_cog.py +135 -0
  50. c2cgeoportal_admin/views/layers_vectortiles.py +62 -27
  51. c2cgeoportal_admin/views/layers_wms.py +61 -36
  52. c2cgeoportal_admin/views/layers_wmts.py +54 -32
  53. c2cgeoportal_admin/views/layertree.py +37 -28
  54. c2cgeoportal_admin/views/logged_views.py +83 -0
  55. c2cgeoportal_admin/views/logs.py +91 -0
  56. c2cgeoportal_admin/views/oauth2_clients.py +96 -0
  57. c2cgeoportal_admin/views/ogc_servers.py +192 -21
  58. c2cgeoportal_admin/views/restriction_areas.py +78 -25
  59. c2cgeoportal_admin/views/roles.py +88 -25
  60. c2cgeoportal_admin/views/themes.py +47 -35
  61. c2cgeoportal_admin/views/themes_ordering.py +44 -24
  62. c2cgeoportal_admin/views/treeitems.py +21 -17
  63. c2cgeoportal_admin/views/users.py +46 -26
  64. c2cgeoportal_admin/widgets.py +79 -28
  65. {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/METADATA +15 -13
  66. c2cgeoportal_admin-2.9rc44.dist-info/RECORD +97 -0
  67. {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/WHEEL +1 -1
  68. c2cgeoportal_admin-2.9rc44.dist-info/entry_points.txt +5 -0
  69. tests/__init__.py +36 -27
  70. tests/conftest.py +23 -24
  71. tests/test_edit_url.py +16 -19
  72. tests/test_functionalities.py +52 -14
  73. tests/test_home.py +0 -1
  74. tests/test_interface.py +35 -12
  75. tests/test_layer_groups.py +58 -32
  76. tests/test_layers_cog.py +243 -0
  77. tests/test_layers_vectortiles.py +46 -30
  78. tests/test_layers_wms.py +77 -82
  79. tests/test_layers_wmts.py +51 -30
  80. tests/test_layertree.py +107 -101
  81. tests/test_learn.py +1 -1
  82. tests/test_left_menu.py +0 -1
  83. tests/test_lingva_extractor_config.py +64 -0
  84. tests/test_logs.py +102 -0
  85. tests/test_main.py +4 -2
  86. tests/test_metadatas.py +79 -71
  87. tests/test_oauth2_clients.py +186 -0
  88. tests/test_ogc_servers.py +110 -28
  89. tests/test_restriction_areas.py +109 -20
  90. tests/test_role.py +142 -82
  91. tests/test_themes.py +75 -41
  92. tests/test_themes_ordering.py +1 -2
  93. tests/test_treegroup.py +2 -2
  94. tests/test_user.py +72 -70
  95. tests/themes_ordering.py +1 -2
  96. c2cgeoportal_admin/templates/navigation_vertical.jinja2 +0 -10
  97. c2cgeoportal_admin-2.5.0.100.dist-info/RECORD +0 -84
  98. c2cgeoportal_admin-2.5.0.100.dist-info/entry_points.txt +0 -3
  99. {c2cgeoportal_admin-2.5.0.100.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/top_level.txt +0 -0
tests/test_layertree.py CHANGED
@@ -3,10 +3,8 @@
3
3
  from unittest.mock import patch
4
4
 
5
5
  import pytest
6
- from selenium.common.exceptions import NoSuchElementException
7
6
 
8
- from . import AbstractViewsTests, skip_if_ci
9
- from .selenium.page import LayertreePage
7
+ from . import AbstractViewsTests
10
8
 
11
9
 
12
10
  @pytest.fixture(scope="function")
@@ -15,6 +13,7 @@ def layertree_test_data(dbsession, transact):
15
13
  del transact
16
14
 
17
15
  from c2cgeoportal_commons.models.main import (
16
+ Interface,
18
17
  LayerGroup,
19
18
  LayergroupTreeitem,
20
19
  LayerWMS,
@@ -23,26 +22,39 @@ def layertree_test_data(dbsession, transact):
23
22
  Theme,
24
23
  )
25
24
 
25
+ interface1 = Interface("interface1")
26
+ dbsession.add(interface1)
27
+ interface2 = Interface("interface2")
28
+ dbsession.add(interface2)
29
+
26
30
  layers_wms = []
27
31
  ogc_server = OGCServer(name="ogc_server")
28
32
  dbsession.add(ogc_server)
29
- for i in range(0, 10):
30
- layer_wms = LayerWMS(name="layer_wms_{}".format(i))
33
+ for i in range(10):
34
+ layer_wms = LayerWMS(name=f"layer_wms_{i}")
35
+ if i == 1:
36
+ layer_wms.interfaces = [interface1]
37
+ elif i > 1:
38
+ layer_wms.interfaces = [interface1, interface2]
31
39
  layer_wms.ogc_server = ogc_server
32
40
  layers_wms.append(layer_wms)
33
41
  dbsession.add(layer_wms)
34
42
 
35
43
  layers_wmts = []
36
- for i in range(0, 10):
37
- layer_wmts = LayerWMTS(name="layer_wmts_{}".format(i))
44
+ for i in range(10):
45
+ layer_wmts = LayerWMTS(name=f"layer_wmts_{i}")
46
+ if i == 1:
47
+ layer_wmts.interfaces = [interface1]
48
+ elif i > 1:
49
+ layer_wmts.interfaces = [interface1, interface2]
38
50
  layer_wmts.url = "http://localhost/wmts"
39
51
  layer_wmts.layer = layer_wmts.name
40
52
  layers_wmts.append(layer_wmts)
41
53
  dbsession.add(layer_wmts)
42
54
 
43
55
  groups = []
44
- for i in range(0, 10):
45
- group = LayerGroup(name="layer_group_{}".format(i))
56
+ for i in range(10):
57
+ group = LayerGroup(name=f"layer_group_{i}")
46
58
  groups.append(group)
47
59
  dbsession.add(group)
48
60
 
@@ -53,10 +65,14 @@ def layertree_test_data(dbsession, transact):
53
65
  dbsession.add(LayergroupTreeitem(group=groups[9], item=groups[8], ordering=3))
54
66
 
55
67
  themes = []
56
- for i in range(0, 5):
57
- theme = Theme(name="theme_{}".format(i))
68
+ for i in range(5):
69
+ theme = Theme(name=f"theme_{i}")
58
70
  themes.append(theme)
59
71
  dbsession.add(theme)
72
+ if i == 1:
73
+ theme.interfaces = [interface1]
74
+ elif i > 1:
75
+ theme.interfaces = [interface1, interface2]
60
76
 
61
77
  dbsession.add(LayergroupTreeitem(group=theme, item=groups[i], ordering=0))
62
78
  dbsession.add(LayergroupTreeitem(group=theme, item=groups[i + 5], ordering=1))
@@ -76,6 +92,7 @@ def layertree_test_data(dbsession, transact):
76
92
  "layers_wms": layers_wms,
77
93
  "layers_wmts": layers_wmts,
78
94
  "ogc_servers": [ogc_server],
95
+ "interfaces": [interface1, interface2],
79
96
  }
80
97
  )
81
98
 
@@ -86,7 +103,6 @@ def layertree_test_data(dbsession, transact):
86
103
  )
87
104
  @pytest.mark.usefixtures("layertree_test_data", "test_app")
88
105
  class TestLayerTreeView(AbstractViewsTests):
89
-
90
106
  _prefix = "/admin/layertree"
91
107
 
92
108
  def test_index(self, test_app):
@@ -95,13 +111,13 @@ class TestLayerTreeView(AbstractViewsTests):
95
111
  def check_edit_action(self, test_app, nodes, table, item_id):
96
112
  node = next(n for n in nodes if n["id"] == item_id)
97
113
  action = next(a for a in node["actions"] if a["name"] == "edit")
98
- assert "http://localhost/admin/{}/{}".format(table, item_id) == action["url"]
114
+ assert f"http://localhost/admin/{table}/{item_id}" == action["url"]
99
115
  test_app.get(action["url"], status=200)
100
116
 
101
117
  def check_unlink_action(self, test_app, nodes, group_id, item_id):
102
118
  node = next(n for n in nodes if n["id"] == item_id)
103
119
  action = next(a for a in node["actions"] if a["name"] == "unlink")
104
- assert "http://localhost/admin/layertree/unlink/{}/{}".format(group_id, item_id) == action["url"]
120
+ assert f"http://localhost/admin/layertree/unlink/{group_id}/{item_id}" == action["url"]
105
121
  test_app.delete(action["url"], status=200)
106
122
 
107
123
  def check_translation(self, nodes, item):
@@ -113,7 +129,7 @@ class TestLayerTreeView(AbstractViewsTests):
113
129
  node = next(n for n in nodes if n["id"] == parent_id)
114
130
  action = next(a for a in node["actions"] if a["name"] == action_name)
115
131
  assert label == action["label"]
116
- assert "http://localhost/admin/{}/new?parent_id={}".format(route_table, parent_id) == action["url"]
132
+ assert f"http://localhost/admin/{route_table}/new?parent_id={parent_id}" == action["url"]
117
133
 
118
134
  form = test_app.get(action["url"], status=200).form
119
135
  assert form["parent_id"].value == str(parent_id)
@@ -152,12 +168,26 @@ class TestLayerTreeView(AbstractViewsTests):
152
168
  {"name": "new_name_from_layer_group"},
153
169
  )
154
170
 
155
- def test_groups(self, test_app, layertree_test_data):
171
+ def test_groups(self, test_app, layertree_test_data, dbsession):
156
172
  theme = layertree_test_data["themes"][0]
173
+
174
+ # Invert children order (to test ordering)
175
+ theme.children_relation[0].ordering = 1
176
+ theme.children_relation[1].ordering = 0
177
+ dbsession.flush()
178
+
157
179
  resp = self.get(test_app, "/children?group_id={0}&path=_{0}".format(theme.id), status=200)
158
180
  nodes = resp.json
159
181
  assert 2 == len(nodes)
160
182
 
183
+ # check groups are sorted by ordering
184
+ expected = [
185
+ relation.treeitem.name
186
+ for relation in sorted(theme.children_relation, key=lambda relation: relation.ordering)
187
+ ]
188
+ group_names = [node["name"] for node in nodes]
189
+ assert expected == group_names
190
+
161
191
  group = layertree_test_data["groups"][0]
162
192
  self.check_edit_action(test_app, nodes, "layer_groups", group.id)
163
193
  self.check_unlink_action(test_app, nodes, theme.id, group.id)
@@ -188,15 +218,29 @@ class TestLayerTreeView(AbstractViewsTests):
188
218
  },
189
219
  )
190
220
 
191
- def test_layers(self, test_app, layertree_test_data):
221
+ def test_layers(self, test_app, layertree_test_data, dbsession):
192
222
  theme = layertree_test_data["themes"][0]
193
223
  group = layertree_test_data["groups"][0]
224
+
225
+ # Invert children order (to test ordering)
226
+ group.children_relation[0].ordering = 1
227
+ group.children_relation[1].ordering = 0
228
+ dbsession.flush()
229
+
194
230
  resp = self.get(
195
231
  test_app, "/children?group_id={0}&path=_{1}_{0}".format(group.id, theme.id), status=200
196
232
  )
197
233
  nodes = resp.json
198
234
  assert len(nodes) == 2
199
235
 
236
+ # check layers are sorted by ordering
237
+ expected = [
238
+ relation.treeitem.name
239
+ for relation in sorted(group.children_relation, key=lambda relation: relation.ordering)
240
+ ]
241
+ layer_names = [node["name"] for node in nodes]
242
+ assert expected == layer_names
243
+
200
244
  layer_wms = layertree_test_data["layers_wms"][0]
201
245
  layer_wmts = layertree_test_data["layers_wmts"][0]
202
246
 
@@ -212,96 +256,58 @@ class TestLayerTreeView(AbstractViewsTests):
212
256
  def test_unlink(self, test_app, layertree_test_data, dbsession):
213
257
  group = layertree_test_data["groups"][0]
214
258
  item = layertree_test_data["layers_wms"][0]
215
- test_app.delete("/admin/layertree/unlink/{}/{}".format(group.id, item.id), status=200)
259
+ test_app.delete(f"/admin/layertree/unlink/{group.id}/{item.id}", status=200)
216
260
  dbsession.expire_all()
217
261
  assert item not in group.children
218
262
 
263
+ def test_delete(self, test_app, layertree_test_data, dbsession):
264
+ from c2cgeoportal_commons.models.main import LayerGroup, LayerWMS, LayerWMTS
219
265
 
220
- @skip_if_ci
221
- @pytest.mark.selenium
222
- @pytest.mark.usefixtures("selenium", "selenium_app", "layertree_test_data")
223
- class TestLayerTreeSelenium:
224
-
225
- _prefix = "/layertree"
226
-
227
- def test_unlink(self, dbsession, selenium, selenium_app, layertree_test_data):
228
- from c2cgeoportal_commons.models.main import LayergroupTreeitem
229
-
230
- themes = layertree_test_data["themes"]
231
- groups = layertree_test_data["groups"]
232
- layers_wms = layertree_test_data["layers_wms"]
233
- layers_wmts = layertree_test_data["layers_wmts"]
234
-
235
- selenium.get(selenium_app + self._prefix)
236
- page = LayertreePage(selenium)
237
- page.expand()
238
-
239
- for group_id, item_id, path in (
240
- (
241
- groups[0].id,
242
- layers_wmts[0].id,
243
- "_{}_{}_{}".format(themes[0].id, groups[0].id, layers_wmts[0].id),
244
- ),
245
- (
246
- groups[0].id,
247
- layers_wms[0].id,
248
- "_{}_{}_{}".format(themes[0].id, groups[0].id, layers_wms[0].id),
249
- ),
250
- (themes[0].id, groups[0].id, "_{}_{}".format(themes[0].id, groups[0].id)),
251
- ):
252
- action_el = page.find_item_action(path, "unlink", 10)
253
- expected_url = "{}/layertree/unlink/{}/{}".format(selenium_app, group_id, item_id)
254
- assert expected_url == action_el.get_attribute("data-url")
255
- page.click_and_confirm(action_el)
256
-
257
- dbsession.expire_all()
258
- link = (
259
- dbsession.query(LayergroupTreeitem)
260
- .filter(LayergroupTreeitem.treegroup_id == group_id)
261
- .filter(LayergroupTreeitem.treeitem_id == item_id)
262
- .one_or_none()
263
- )
264
- assert link is None
265
-
266
- selenium.refresh()
267
- page.wait_jquery_to_be_active()
268
- page.find_item("_{}_{}_{}".format(themes[0].id, groups[5].id, layers_wmts[5].id), 10)
269
- with pytest.raises(NoSuchElementException):
270
- page.find_item(path)
271
-
272
- @skip_if_ci
273
- @pytest.mark.selenium
274
- @pytest.mark.usefixtures("selenium", "selenium_app")
275
- def test_delete(self, dbsession, selenium, selenium_app, layertree_test_data):
276
- from c2cgeoportal_commons.models.main import LayerWMS, LayerWMTS, LayerGroup
277
-
278
- themes = layertree_test_data["themes"]
279
266
  groups = layertree_test_data["groups"]
280
267
  layers_wms = layertree_test_data["layers_wms"]
281
268
  layers_wmts = layertree_test_data["layers_wmts"]
282
269
 
283
- selenium.get(selenium_app + self._prefix)
284
- page = LayertreePage(selenium)
285
- page.expand()
286
-
287
- for item_id, path, model in (
288
- (layers_wmts[1].id, "_{}_{}_{}".format(themes[1].id, groups[1].id, layers_wmts[1].id), LayerWMTS),
289
- (layers_wms[1].id, "_{}_{}_{}".format(themes[1].id, groups[1].id, layers_wms[1].id), LayerWMS),
290
- (groups[1].id, "_{}_{}".format(themes[1].id, groups[1].id), LayerGroup),
270
+ for item_id, model in (
271
+ (layers_wmts[1].id, LayerWMTS),
272
+ (layers_wms[1].id, LayerWMS),
273
+ (groups[1].id, LayerGroup),
291
274
  ):
292
- action_el = page.find_item_action(path, "delete", 10)
293
- expected_url = "{}/admin/layertree/delete/{}".format(selenium_app, item_id)
294
- assert expected_url == action_el.get_attribute("data-url")
295
- page.click_and_confirm(action_el)
296
- page.wait_jquery_to_be_active()
297
-
298
- deleted = dbsession.query(model).filter(model.id == item_id).one_or_none()
299
- assert deleted is None
300
-
301
- dbsession.expire_all()
302
- selenium.refresh()
303
- page.wait_jquery_to_be_active()
304
-
305
- page.find_item("_{}_{}_{}".format(themes[1].id, groups[6].id, layers_wmts[6].id), 10)
306
- with pytest.raises(NoSuchElementException):
307
- page.find_item(path)
275
+ test_app.delete(f"/admin/layertree/delete/{item_id}", status=200)
276
+ assert dbsession.query(model).get(item_id) is None
277
+
278
+ @pytest.mark.parametrize(
279
+ "params,expected",
280
+ [
281
+ ({}, ["theme_0", "theme_3", "theme_1", "theme_2", "theme_4"]),
282
+ ({"interface": "interface1"}, ["theme_3", "theme_1", "theme_2", "theme_4"]),
283
+ ({"interface": "interface2"}, ["theme_3", "theme_2", "theme_4"]),
284
+ ],
285
+ )
286
+ def test_themes_interfaces(self, test_app, layertree_test_data, params, expected):
287
+ resp = self.get(test_app, "/children", status=200, params=params)
288
+ nodes = resp.json
289
+ assert expected == [node["name"] for node in nodes if node["item_type"] == "theme"]
290
+
291
+ @pytest.mark.parametrize(
292
+ "interface,index,length",
293
+ [
294
+ (None, 0, 2),
295
+ (None, 1, 2),
296
+ (None, 2, 2),
297
+ ("interface1", 0, 0),
298
+ ("interface1", 1, 2),
299
+ ("interface1", 2, 2),
300
+ ("interface2", 0, 0),
301
+ ("interface2", 1, 0),
302
+ ("interface2", 2, 2),
303
+ ],
304
+ )
305
+ def test_layers_interface(self, test_app, layertree_test_data, interface, index, length):
306
+ theme = layertree_test_data["themes"][index]
307
+ group = layertree_test_data["groups"][index]
308
+ params = {"group_id": group.id, "path": f"_{theme.id}_{group.id}"}
309
+ if interface:
310
+ params["interface"] = interface
311
+ resp = self.get(test_app, "/children", status=200, params=params)
312
+ nodes = resp.json
313
+ assert length == len(nodes)
tests/test_learn.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # pylint: disable=no-self-use
2
2
 
3
- from pyramid.view import view_config
4
3
  import pytest
4
+ from pyramid.view import view_config
5
5
 
6
6
 
7
7
  @pytest.fixture(scope="class")
tests/test_left_menu.py CHANGED
@@ -24,7 +24,6 @@ def left_menu_test_data(dbsession, transact):
24
24
 
25
25
  @pytest.mark.usefixtures("test_app")
26
26
  class TestLeftMenu(AbstractViewsTests):
27
-
28
27
  _prefix = "/admin/roles"
29
28
 
30
29
  def test_index(self, test_app):
@@ -0,0 +1,64 @@
1
+ # Copyright (c) 2022-2024, Camptocamp SA
2
+ # All rights reserved.
3
+
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+
7
+ # 1. Redistributions of source code must retain the above copyright notice, this
8
+ # list of conditions and the following disclaimer.
9
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ # this list of conditions and the following disclaimer in the documentation
11
+ # and/or other materials provided with the distribution.
12
+
13
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20
+ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ # The views and conclusions contained in the software and documentation are those
25
+ # of the authors and should not be interpreted as representing official policies,
26
+ # either expressed or implied, of the FreeBSD Project.
27
+
28
+ # pylint: disable=missing-docstring
29
+
30
+ from unittest.mock import Mock, mock_open, patch
31
+
32
+ import pytest
33
+ import yaml
34
+ from c2c.template.config import config as configuration
35
+
36
+ from c2cgeoportal_admin.lib.lingva_extractor import GeomapfishConfigExtractor
37
+
38
+ GMF_CONFIG = """
39
+ vars:
40
+ admin_interface:
41
+ available_metadata:
42
+ - name: metadata1
43
+ description: description1
44
+ translate: true
45
+ available_functionalities:
46
+ - name: functionality1
47
+ description: description2
48
+ """
49
+
50
+
51
+ class TestGeomapfishConfigExtractor:
52
+ @patch(
53
+ "c2cgeoportal_admin.lib.lingva_extractor.open",
54
+ mock_open(read_data=GMF_CONFIG),
55
+ )
56
+ def test_extract_config(self):
57
+ extractor = GeomapfishConfigExtractor()
58
+
59
+ options = Mock()
60
+ options.keywords = []
61
+
62
+ messages = list(extractor("config.yaml", options))
63
+
64
+ assert {msg.msgid for msg in messages} == {"description1", "description2"}
tests/test_logs.py ADDED
@@ -0,0 +1,102 @@
1
+ # pylint: disable=no-self-use,unsubscriptable-object
2
+
3
+ import datetime
4
+ from datetime import timezone
5
+
6
+ import pytest
7
+
8
+ from . import AbstractViewsTests
9
+
10
+
11
+ @pytest.fixture(scope="function")
12
+ @pytest.mark.usefixtures("dbsession", "transact")
13
+ def logs_test_data(dbsession, transact):
14
+ del transact
15
+
16
+ from c2cgeoportal_commons.models.main import AbstractLog
17
+ from c2cgeoportal_commons.models.main import Log as MainLog
18
+ from c2cgeoportal_commons.models.main import LogAction
19
+ from c2cgeoportal_commons.models.static import Log as StaticLog
20
+
21
+ logs = []
22
+ for i in range(0, 5):
23
+ log = MainLog(
24
+ date=datetime.datetime.now(timezone.utc),
25
+ action=[LogAction.INSERT, LogAction.UPDATE, LogAction.DELETE][i % 3],
26
+ element_type="role",
27
+ element_id=i,
28
+ element_name=f"role_{i}",
29
+ element_url_table="roles",
30
+ username="testuser",
31
+ )
32
+ dbsession.add(log)
33
+ logs.append(log)
34
+
35
+ log = StaticLog(
36
+ date=datetime.datetime.now(timezone.utc),
37
+ action=[LogAction.INSERT, LogAction.UPDATE, LogAction.DELETE][i % 3],
38
+ element_type="user",
39
+ element_id=i,
40
+ element_name=f"user_{i}",
41
+ element_url_table="users",
42
+ username="testuser",
43
+ )
44
+ dbsession.add(log)
45
+ logs.append(log)
46
+
47
+ dbsession.flush()
48
+
49
+ yield {
50
+ "logs": logs,
51
+ }
52
+
53
+
54
+ @pytest.mark.usefixtures("logs_test_data", "test_app")
55
+ class TestLog(AbstractViewsTests):
56
+ _prefix = "/admin/logs"
57
+
58
+ def test_index_rendering(self, test_app):
59
+ resp = self.get(test_app)
60
+
61
+ self.check_left_menu(resp, "Logs")
62
+
63
+ expected = [
64
+ ("actions", "", "false"),
65
+ ("id", "id", "true"),
66
+ ("date", "Date"),
67
+ ("username", "Username"),
68
+ ("action", "Action"),
69
+ ("element_type", "Element type"),
70
+ ("element_id", "Element identifier"),
71
+ ("element_name", "Element name"),
72
+ ]
73
+ self.check_grid_headers(resp, expected, new=False)
74
+
75
+ def test_grid_default_sort_on_date_desc(self, test_app, logs_test_data):
76
+ json = self.check_search(test_app)
77
+ expected_ids = [
78
+ log.id
79
+ for log in sorted(
80
+ logs_test_data["logs"],
81
+ key=lambda log: log.date,
82
+ reverse=True,
83
+ )
84
+ ]
85
+ result_ids = [int(row["_id_"]) for row in json["rows"]]
86
+ assert result_ids == expected_ids
87
+
88
+ def test_grid_sort_on_element_type(self, test_app, logs_test_data):
89
+ json = self.check_search(test_app, sort="element_type")
90
+ expected_ids = [
91
+ log.id
92
+ for log in sorted(
93
+ logs_test_data["logs"],
94
+ key=lambda log: (log.element_type, -log.date.timestamp()),
95
+ )
96
+ ]
97
+ result_ids = [int(row["_id_"]) for row in json["rows"]]
98
+ assert result_ids == expected_ids
99
+
100
+ def test_grid_search(self, test_app):
101
+ self.check_search(test_app, "role", total=5)
102
+ self.check_search(test_app, "user_2", total=1)
tests/test_main.py CHANGED
@@ -1,11 +1,13 @@
1
- from pyramid import testing
2
1
  import pytest
2
+ from pyramid import testing
3
3
  from webtest import TestApp as WebTestApp
4
4
 
5
5
 
6
6
  @pytest.mark.usefixtures("app_env")
7
7
  def test_main(app_env):
8
- """Test dev environment"""
8
+ """
9
+ Test dev environment.
10
+ """
9
11
  config = testing.setUp(registry=app_env["registry"])
10
12
  app = config.make_wsgi_app()
11
13
  testapp = WebTestApp(app)