c2cgeoportal-admin 2.6.0__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.
- c2cgeoportal_admin/__init__.py +42 -12
- c2cgeoportal_admin/lib/lingva_extractor.py +77 -0
- c2cgeoportal_admin/lib/ogcserver_synchronizer.py +170 -57
- c2cgeoportal_admin/py.typed +0 -0
- c2cgeoportal_admin/routes.py +18 -6
- c2cgeoportal_admin/schemas/dimensions.py +16 -10
- c2cgeoportal_admin/schemas/functionalities.py +59 -21
- c2cgeoportal_admin/schemas/interfaces.py +26 -18
- c2cgeoportal_admin/schemas/metadata.py +101 -48
- c2cgeoportal_admin/schemas/restriction_areas.py +25 -19
- c2cgeoportal_admin/schemas/roles.py +12 -6
- c2cgeoportal_admin/schemas/treegroup.py +46 -21
- c2cgeoportal_admin/schemas/treeitem.py +3 -4
- c2cgeoportal_admin/static/layertree.css +3 -4
- c2cgeoportal_admin/static/navbar.css +36 -35
- c2cgeoportal_admin/static/theme.css +19 -9
- c2cgeoportal_admin/subscribers.py +3 -3
- c2cgeoportal_admin/templates/404.jinja2 +18 -2
- c2cgeoportal_admin/templates/layertree.jinja2 +31 -9
- c2cgeoportal_admin/templates/navigation_navbar.jinja2 +33 -0
- c2cgeoportal_admin/templates/ogcserver_synchronize.jinja2 +12 -0
- c2cgeoportal_admin/templates/widgets/functionality_fields.pt +51 -0
- c2cgeoportal_admin/templates/widgets/metadata.pt +7 -1
- c2cgeoportal_admin/views/__init__.py +29 -0
- c2cgeoportal_admin/views/dimension_layers.py +14 -9
- c2cgeoportal_admin/views/functionalities.py +52 -18
- c2cgeoportal_admin/views/home.py +5 -5
- c2cgeoportal_admin/views/interfaces.py +26 -20
- c2cgeoportal_admin/views/layer_groups.py +36 -25
- c2cgeoportal_admin/views/layers.py +17 -13
- c2cgeoportal_admin/views/layers_cog.py +135 -0
- c2cgeoportal_admin/views/layers_vectortiles.py +62 -27
- c2cgeoportal_admin/views/layers_wms.py +55 -34
- c2cgeoportal_admin/views/layers_wmts.py +54 -34
- c2cgeoportal_admin/views/layertree.py +38 -29
- c2cgeoportal_admin/views/logged_views.py +83 -0
- c2cgeoportal_admin/views/logs.py +91 -0
- c2cgeoportal_admin/views/oauth2_clients.py +30 -18
- c2cgeoportal_admin/views/ogc_servers.py +132 -36
- c2cgeoportal_admin/views/restriction_areas.py +39 -27
- c2cgeoportal_admin/views/roles.py +42 -28
- c2cgeoportal_admin/views/themes.py +47 -35
- c2cgeoportal_admin/views/themes_ordering.py +19 -14
- c2cgeoportal_admin/views/treeitems.py +21 -17
- c2cgeoportal_admin/views/users.py +46 -26
- c2cgeoportal_admin/widgets.py +17 -14
- {c2cgeoportal_admin-2.6.0.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/METADATA +12 -12
- c2cgeoportal_admin-2.9rc44.dist-info/RECORD +97 -0
- {c2cgeoportal_admin-2.6.0.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/WHEEL +1 -1
- c2cgeoportal_admin-2.9rc44.dist-info/entry_points.txt +5 -0
- tests/__init__.py +24 -20
- tests/conftest.py +22 -11
- tests/test_edit_url.py +11 -14
- tests/test_functionalities.py +52 -14
- tests/test_home.py +0 -1
- tests/test_interface.py +34 -11
- tests/test_layer_groups.py +57 -27
- tests/test_layers_cog.py +243 -0
- tests/test_layers_vectortiles.py +43 -25
- tests/test_layers_wms.py +67 -45
- tests/test_layers_wmts.py +47 -26
- tests/test_layertree.py +99 -16
- tests/test_left_menu.py +0 -1
- tests/test_lingva_extractor_config.py +64 -0
- tests/test_logs.py +102 -0
- tests/test_main.py +3 -1
- tests/test_metadatas.py +34 -21
- tests/test_oauth2_clients.py +40 -11
- tests/test_ogc_servers.py +84 -35
- tests/test_restriction_areas.py +38 -15
- tests/test_role.py +71 -43
- tests/test_themes.py +71 -37
- tests/test_themes_ordering.py +1 -2
- tests/test_treegroup.py +2 -2
- tests/test_user.py +56 -19
- tests/themes_ordering.py +1 -2
- c2cgeoportal_admin/templates/navigation_vertical.jinja2 +0 -33
- c2cgeoportal_admin-2.6.0.dist-info/RECORD +0 -89
- c2cgeoportal_admin-2.6.0.dist-info/entry_points.txt +0 -3
- {c2cgeoportal_admin-2.6.0.dist-info → c2cgeoportal_admin-2.9rc44.dist-info}/top_level.txt +0 -0
tests/test_layertree.py
CHANGED
@@ -13,6 +13,7 @@ def layertree_test_data(dbsession, transact):
|
|
13
13
|
del transact
|
14
14
|
|
15
15
|
from c2cgeoportal_commons.models.main import (
|
16
|
+
Interface,
|
16
17
|
LayerGroup,
|
17
18
|
LayergroupTreeitem,
|
18
19
|
LayerWMS,
|
@@ -21,26 +22,39 @@ def layertree_test_data(dbsession, transact):
|
|
21
22
|
Theme,
|
22
23
|
)
|
23
24
|
|
25
|
+
interface1 = Interface("interface1")
|
26
|
+
dbsession.add(interface1)
|
27
|
+
interface2 = Interface("interface2")
|
28
|
+
dbsession.add(interface2)
|
29
|
+
|
24
30
|
layers_wms = []
|
25
31
|
ogc_server = OGCServer(name="ogc_server")
|
26
32
|
dbsession.add(ogc_server)
|
27
|
-
for i in range(
|
28
|
-
layer_wms = LayerWMS(name="layer_wms_{}"
|
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]
|
29
39
|
layer_wms.ogc_server = ogc_server
|
30
40
|
layers_wms.append(layer_wms)
|
31
41
|
dbsession.add(layer_wms)
|
32
42
|
|
33
43
|
layers_wmts = []
|
34
|
-
for i in range(
|
35
|
-
layer_wmts = LayerWMTS(name="layer_wmts_{}"
|
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]
|
36
50
|
layer_wmts.url = "http://localhost/wmts"
|
37
51
|
layer_wmts.layer = layer_wmts.name
|
38
52
|
layers_wmts.append(layer_wmts)
|
39
53
|
dbsession.add(layer_wmts)
|
40
54
|
|
41
55
|
groups = []
|
42
|
-
for i in range(
|
43
|
-
group = LayerGroup(name="layer_group_{}"
|
56
|
+
for i in range(10):
|
57
|
+
group = LayerGroup(name=f"layer_group_{i}")
|
44
58
|
groups.append(group)
|
45
59
|
dbsession.add(group)
|
46
60
|
|
@@ -51,10 +65,14 @@ def layertree_test_data(dbsession, transact):
|
|
51
65
|
dbsession.add(LayergroupTreeitem(group=groups[9], item=groups[8], ordering=3))
|
52
66
|
|
53
67
|
themes = []
|
54
|
-
for i in range(
|
55
|
-
theme = Theme(name="theme_{}"
|
68
|
+
for i in range(5):
|
69
|
+
theme = Theme(name=f"theme_{i}")
|
56
70
|
themes.append(theme)
|
57
71
|
dbsession.add(theme)
|
72
|
+
if i == 1:
|
73
|
+
theme.interfaces = [interface1]
|
74
|
+
elif i > 1:
|
75
|
+
theme.interfaces = [interface1, interface2]
|
58
76
|
|
59
77
|
dbsession.add(LayergroupTreeitem(group=theme, item=groups[i], ordering=0))
|
60
78
|
dbsession.add(LayergroupTreeitem(group=theme, item=groups[i + 5], ordering=1))
|
@@ -74,6 +92,7 @@ def layertree_test_data(dbsession, transact):
|
|
74
92
|
"layers_wms": layers_wms,
|
75
93
|
"layers_wmts": layers_wmts,
|
76
94
|
"ogc_servers": [ogc_server],
|
95
|
+
"interfaces": [interface1, interface2],
|
77
96
|
}
|
78
97
|
)
|
79
98
|
|
@@ -84,7 +103,6 @@ def layertree_test_data(dbsession, transact):
|
|
84
103
|
)
|
85
104
|
@pytest.mark.usefixtures("layertree_test_data", "test_app")
|
86
105
|
class TestLayerTreeView(AbstractViewsTests):
|
87
|
-
|
88
106
|
_prefix = "/admin/layertree"
|
89
107
|
|
90
108
|
def test_index(self, test_app):
|
@@ -93,13 +111,13 @@ class TestLayerTreeView(AbstractViewsTests):
|
|
93
111
|
def check_edit_action(self, test_app, nodes, table, item_id):
|
94
112
|
node = next(n for n in nodes if n["id"] == item_id)
|
95
113
|
action = next(a for a in node["actions"] if a["name"] == "edit")
|
96
|
-
assert "http://localhost/admin/{}/{}"
|
114
|
+
assert f"http://localhost/admin/{table}/{item_id}" == action["url"]
|
97
115
|
test_app.get(action["url"], status=200)
|
98
116
|
|
99
117
|
def check_unlink_action(self, test_app, nodes, group_id, item_id):
|
100
118
|
node = next(n for n in nodes if n["id"] == item_id)
|
101
119
|
action = next(a for a in node["actions"] if a["name"] == "unlink")
|
102
|
-
assert "http://localhost/admin/layertree/unlink/{}/{}"
|
120
|
+
assert f"http://localhost/admin/layertree/unlink/{group_id}/{item_id}" == action["url"]
|
103
121
|
test_app.delete(action["url"], status=200)
|
104
122
|
|
105
123
|
def check_translation(self, nodes, item):
|
@@ -111,7 +129,7 @@ class TestLayerTreeView(AbstractViewsTests):
|
|
111
129
|
node = next(n for n in nodes if n["id"] == parent_id)
|
112
130
|
action = next(a for a in node["actions"] if a["name"] == action_name)
|
113
131
|
assert label == action["label"]
|
114
|
-
assert "http://localhost/admin/{}/new?parent_id={}"
|
132
|
+
assert f"http://localhost/admin/{route_table}/new?parent_id={parent_id}" == action["url"]
|
115
133
|
|
116
134
|
form = test_app.get(action["url"], status=200).form
|
117
135
|
assert form["parent_id"].value == str(parent_id)
|
@@ -150,12 +168,26 @@ class TestLayerTreeView(AbstractViewsTests):
|
|
150
168
|
{"name": "new_name_from_layer_group"},
|
151
169
|
)
|
152
170
|
|
153
|
-
def test_groups(self, test_app, layertree_test_data):
|
171
|
+
def test_groups(self, test_app, layertree_test_data, dbsession):
|
154
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
|
+
|
155
179
|
resp = self.get(test_app, "/children?group_id={0}&path=_{0}".format(theme.id), status=200)
|
156
180
|
nodes = resp.json
|
157
181
|
assert 2 == len(nodes)
|
158
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
|
+
|
159
191
|
group = layertree_test_data["groups"][0]
|
160
192
|
self.check_edit_action(test_app, nodes, "layer_groups", group.id)
|
161
193
|
self.check_unlink_action(test_app, nodes, theme.id, group.id)
|
@@ -186,15 +218,29 @@ class TestLayerTreeView(AbstractViewsTests):
|
|
186
218
|
},
|
187
219
|
)
|
188
220
|
|
189
|
-
def test_layers(self, test_app, layertree_test_data):
|
221
|
+
def test_layers(self, test_app, layertree_test_data, dbsession):
|
190
222
|
theme = layertree_test_data["themes"][0]
|
191
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
|
+
|
192
230
|
resp = self.get(
|
193
231
|
test_app, "/children?group_id={0}&path=_{1}_{0}".format(group.id, theme.id), status=200
|
194
232
|
)
|
195
233
|
nodes = resp.json
|
196
234
|
assert len(nodes) == 2
|
197
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
|
+
|
198
244
|
layer_wms = layertree_test_data["layers_wms"][0]
|
199
245
|
layer_wmts = layertree_test_data["layers_wmts"][0]
|
200
246
|
|
@@ -210,7 +256,7 @@ class TestLayerTreeView(AbstractViewsTests):
|
|
210
256
|
def test_unlink(self, test_app, layertree_test_data, dbsession):
|
211
257
|
group = layertree_test_data["groups"][0]
|
212
258
|
item = layertree_test_data["layers_wms"][0]
|
213
|
-
test_app.delete("/admin/layertree/unlink/{}/{
|
259
|
+
test_app.delete(f"/admin/layertree/unlink/{group.id}/{item.id}", status=200)
|
214
260
|
dbsession.expire_all()
|
215
261
|
assert item not in group.children
|
216
262
|
|
@@ -226,5 +272,42 @@ class TestLayerTreeView(AbstractViewsTests):
|
|
226
272
|
(layers_wms[1].id, LayerWMS),
|
227
273
|
(groups[1].id, LayerGroup),
|
228
274
|
):
|
229
|
-
test_app.delete("/admin/layertree/delete/{}"
|
275
|
+
test_app.delete(f"/admin/layertree/delete/{item_id}", status=200)
|
230
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_left_menu.py
CHANGED
@@ -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
@@ -5,7 +5,9 @@ from webtest import TestApp as WebTestApp
|
|
5
5
|
|
6
6
|
@pytest.mark.usefixtures("app_env")
|
7
7
|
def test_main(app_env):
|
8
|
-
"""
|
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)
|
tests/test_metadatas.py
CHANGED
@@ -59,7 +59,6 @@ def metadatas_test_data(dbsession, transact):
|
|
59
59
|
|
60
60
|
@pytest.mark.usefixtures("metadatas_test_data", "test_app")
|
61
61
|
class TestMetadatasView(AbstractViewsTests):
|
62
|
-
|
63
62
|
_prefix = "/admin/"
|
64
63
|
|
65
64
|
def __metadata_ui_types(self):
|
@@ -85,7 +84,9 @@ class TestMetadatasView(AbstractViewsTests):
|
|
85
84
|
return None
|
86
85
|
return metadata.value
|
87
86
|
|
88
|
-
def _check_metadatas(self, test_app, item, metadatas):
|
87
|
+
def _check_metadatas(self, test_app, item, metadatas, model):
|
88
|
+
from c2cgeoportal_admin.schemas.metadata import metadata_definitions
|
89
|
+
|
89
90
|
settings = test_app.app.registry.settings
|
90
91
|
self._check_sequence(
|
91
92
|
item,
|
@@ -96,9 +97,7 @@ class TestMetadatasView(AbstractViewsTests):
|
|
96
97
|
"name": "name",
|
97
98
|
"value": [
|
98
99
|
{"text": s_m["name"], "value": s_m["name"], "selected": s_m["name"] == m.name}
|
99
|
-
for s_m in sorted(
|
100
|
-
settings["admin_interface"]["available_metadata"], key=lambda m: m["name"]
|
101
|
-
)
|
100
|
+
for s_m in sorted(metadata_definitions(settings, model), key=lambda m: m["name"])
|
102
101
|
],
|
103
102
|
"label": "Name",
|
104
103
|
},
|
@@ -131,7 +130,7 @@ class TestMetadatasView(AbstractViewsTests):
|
|
131
130
|
resp = self._post_metadata(test_app, url, base_mapping, name, value, 200)
|
132
131
|
assert (
|
133
132
|
error_msg
|
134
|
-
== resp.html.select_one(".item-{
|
133
|
+
== resp.html.select_one(f".item-{self.__metadata_ui_type(test_app, name)} .help-block")
|
135
134
|
.getText()
|
136
135
|
.strip()
|
137
136
|
)
|
@@ -156,12 +155,16 @@ class TestMetadatasView(AbstractViewsTests):
|
|
156
155
|
)
|
157
156
|
|
158
157
|
def test_get_true_boolean_metadata(self, metadatas_test_data, test_app):
|
159
|
-
|
160
|
-
|
158
|
+
from c2cgeoportal_commons.models.main import LayerWMS
|
159
|
+
|
160
|
+
metadatas_test_data["layer_wms"].get_metadata("_boolean")[0].value = "true"
|
161
|
+
self._test_edit_treeitem("layers_wms", metadatas_test_data["layer_wms"], test_app, LayerWMS)
|
161
162
|
|
162
163
|
def test_get_false_boolean_metadata(self, metadatas_test_data, test_app):
|
163
|
-
|
164
|
-
|
164
|
+
from c2cgeoportal_commons.models.main import LayerWMS
|
165
|
+
|
166
|
+
metadatas_test_data["layer_wms"].get_metadata("_boolean")[0].value = "false"
|
167
|
+
self._test_edit_treeitem("layers_wms", metadatas_test_data["layer_wms"], test_app, LayerWMS)
|
165
168
|
|
166
169
|
def test_post_true_boolean_metadata(self, test_app, metadatas_test_data, dbsession):
|
167
170
|
from c2cgeoportal_commons.models.main import LayerWMS
|
@@ -175,7 +178,7 @@ class TestMetadatasView(AbstractViewsTests):
|
|
175
178
|
302,
|
176
179
|
)
|
177
180
|
layer = dbsession.query(LayerWMS).filter(LayerWMS.name == "new_name").one()
|
178
|
-
assert layer.
|
181
|
+
assert layer.get_metadata("_boolean")[0].value == "true"
|
179
182
|
|
180
183
|
def test_post_false_boolean_metadata(self, test_app, metadatas_test_data, dbsession):
|
181
184
|
from c2cgeoportal_commons.models.main import LayerWMS
|
@@ -189,7 +192,7 @@ class TestMetadatasView(AbstractViewsTests):
|
|
189
192
|
302,
|
190
193
|
)
|
191
194
|
layer = dbsession.query(LayerWMS).filter(LayerWMS.name == "new_name").one()
|
192
|
-
assert layer.
|
195
|
+
assert layer.get_metadata("_boolean")[0].value == "false"
|
193
196
|
|
194
197
|
def test_valid_float_metadata(self, test_app, metadatas_test_data):
|
195
198
|
self._post_metadata(
|
@@ -304,31 +307,41 @@ class TestMetadatasView(AbstractViewsTests):
|
|
304
307
|
302,
|
305
308
|
)
|
306
309
|
|
307
|
-
def _test_edit_treeitem(self, prefix, item, test_app):
|
308
|
-
resp = self.get(test_app, "{}/{
|
309
|
-
self._check_metadatas(test_app, resp.html.select_one(".item-metadatas"), item.metadatas)
|
310
|
+
def _test_edit_treeitem(self, prefix, item, test_app, model):
|
311
|
+
resp = self.get(test_app, f"{prefix}/{item.id}")
|
312
|
+
self._check_metadatas(test_app, resp.html.select_one(".item-metadatas"), item.metadatas, model)
|
310
313
|
resp.form.submit("submit", status=302)
|
311
314
|
|
312
315
|
def test_layer_wms_metadatas(self, metadatas_test_data, test_app):
|
313
|
-
|
316
|
+
from c2cgeoportal_commons.models.main import LayerWMS
|
317
|
+
|
318
|
+
self._test_edit_treeitem("layers_wms", metadatas_test_data["layer_wms"], test_app, LayerWMS)
|
314
319
|
|
315
320
|
def test_layer_wmts_metadatas(self, metadatas_test_data, test_app):
|
316
|
-
|
321
|
+
from c2cgeoportal_commons.models.main import LayerWMTS
|
322
|
+
|
323
|
+
self._test_edit_treeitem("layers_wmts", metadatas_test_data["layer_wmts"], test_app, LayerWMTS)
|
317
324
|
|
318
325
|
def test_theme_metadatas(self, metadatas_test_data, test_app):
|
319
|
-
|
326
|
+
from c2cgeoportal_commons.models.main import Theme
|
327
|
+
|
328
|
+
self._test_edit_treeitem("themes", metadatas_test_data["theme"], test_app, Theme)
|
320
329
|
|
321
330
|
def test_group_metadatas(self, metadatas_test_data, test_app):
|
322
|
-
|
331
|
+
from c2cgeoportal_commons.models.main import LayerGroup
|
332
|
+
|
333
|
+
self._test_edit_treeitem("layer_groups", metadatas_test_data["group"], test_app, LayerGroup)
|
323
334
|
|
324
335
|
def test_undefined_metadata(self, metadatas_test_data, test_app):
|
325
|
-
"""
|
336
|
+
"""
|
337
|
+
Undefined metadata must be kept intact across submissions.
|
338
|
+
"""
|
326
339
|
from c2cgeoportal_commons.models.main import Metadata
|
327
340
|
|
328
341
|
layer = metadatas_test_data["layer_wms"]
|
329
342
|
layer.metadatas = [Metadata("_undefined", "This is an undefined metadata")]
|
330
343
|
|
331
|
-
resp = self.get(test_app, "layers_wms/{
|
344
|
+
resp = self.get(test_app, f"layers_wms/{layer.id}")
|
332
345
|
resp.form.submit("submit", status=302)
|
333
346
|
|
334
347
|
metadata = layer.metadatas[0]
|
tests/test_oauth2_clients.py
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
import re
|
4
4
|
from uuid import uuid4
|
5
5
|
|
6
|
+
import pyramid.httpexceptions
|
6
7
|
import pytest
|
7
8
|
from pyramid.testing import DummyRequest
|
8
9
|
|
@@ -35,7 +36,6 @@ def oauth2_clients_test_data(dbsession, transact):
|
|
35
36
|
|
36
37
|
@pytest.mark.usefixtures("oauth2_clients_test_data", "test_app")
|
37
38
|
class TestOAuth2Client(TestTreeGroup):
|
38
|
-
|
39
39
|
_prefix = "/admin/oauth2_clients"
|
40
40
|
|
41
41
|
def test_index_rendering(self, test_app):
|
@@ -59,7 +59,8 @@ class TestOAuth2Client(TestTreeGroup):
|
|
59
59
|
self.check_search(test_app, client.client_id, total=1)
|
60
60
|
|
61
61
|
def test_submit_new(self, dbsession, test_app, oauth2_clients_test_data):
|
62
|
-
from c2cgeoportal_commons.models.
|
62
|
+
from c2cgeoportal_commons.models.main import LogAction
|
63
|
+
from c2cgeoportal_commons.models.static import Log, OAuth2Client
|
63
64
|
|
64
65
|
resp = test_app.post(
|
65
66
|
"/admin/oauth2_clients/new",
|
@@ -84,7 +85,18 @@ class TestOAuth2Client(TestTreeGroup):
|
|
84
85
|
assert oauth2_client.secret == "12345"
|
85
86
|
assert oauth2_client.redirect_uri == "http://127.0.0.1:7070/bis"
|
86
87
|
|
88
|
+
log = dbsession.query(Log).one()
|
89
|
+
assert log.date != None
|
90
|
+
assert log.action == LogAction.INSERT
|
91
|
+
assert log.element_type == "oauth2_client"
|
92
|
+
assert log.element_id == oauth2_client.id
|
93
|
+
assert log.element_name == oauth2_client.client_id
|
94
|
+
assert log.username == "test_user"
|
95
|
+
|
87
96
|
def test_edit_then_save(self, dbsession, test_app, oauth2_clients_test_data):
|
97
|
+
from c2cgeoportal_commons.models.main import LogAction
|
98
|
+
from c2cgeoportal_commons.models.static import Log
|
99
|
+
|
88
100
|
oauth2_client = oauth2_clients_test_data["oauth2_clients"][10]
|
89
101
|
|
90
102
|
dbsession.expire(oauth2_client)
|
@@ -112,12 +124,20 @@ class TestOAuth2Client(TestTreeGroup):
|
|
112
124
|
assert "New secret" == oauth2_client.secret
|
113
125
|
assert "New redirect URI" == oauth2_client.redirect_uri
|
114
126
|
|
127
|
+
log = dbsession.query(Log).one()
|
128
|
+
assert log.date != None
|
129
|
+
assert log.action == LogAction.UPDATE
|
130
|
+
assert log.element_type == "oauth2_client"
|
131
|
+
assert log.element_id == oauth2_client.id
|
132
|
+
assert log.element_name == oauth2_client.client_id
|
133
|
+
assert log.username == "test_user"
|
134
|
+
|
115
135
|
def test_duplicate(self, oauth2_clients_test_data, test_app, dbsession):
|
116
136
|
from c2cgeoportal_commons.models.static import OAuth2Client
|
117
137
|
|
118
138
|
oauth2_client_proto = oauth2_clients_test_data["oauth2_clients"][7]
|
119
139
|
|
120
|
-
resp = test_app.get("/admin/oauth2_clients/{}/duplicate"
|
140
|
+
resp = test_app.get(f"/admin/oauth2_clients/{oauth2_client_proto.id}/duplicate", status=200)
|
121
141
|
form = resp.form
|
122
142
|
|
123
143
|
assert "" == self.get_first_field_named(form, "id").value
|
@@ -134,24 +154,33 @@ class TestOAuth2Client(TestTreeGroup):
|
|
134
154
|
assert oauth2_client_proto.id != oauth2_client.id
|
135
155
|
|
136
156
|
def test_delete(self, test_app, dbsession):
|
137
|
-
from c2cgeoportal_commons.models.
|
157
|
+
from c2cgeoportal_commons.models.main import LogAction
|
158
|
+
from c2cgeoportal_commons.models.static import Log, OAuth2Client
|
159
|
+
|
160
|
+
oauth2_client = dbsession.query(OAuth2Client).first()
|
161
|
+
test_app.delete(f"/admin/oauth2_clients/{oauth2_client.id}", status=200)
|
162
|
+
assert dbsession.query(OAuth2Client).get(oauth2_client.id) is None
|
138
163
|
|
139
|
-
|
140
|
-
|
141
|
-
assert
|
164
|
+
log = dbsession.query(Log).one()
|
165
|
+
assert log.date != None
|
166
|
+
assert log.action == LogAction.DELETE
|
167
|
+
assert log.element_type == "oauth2_client"
|
168
|
+
assert log.element_id == oauth2_client.id
|
169
|
+
assert log.element_name == oauth2_client.client_id
|
170
|
+
assert log.username == "test_user"
|
142
171
|
|
143
172
|
def test_unicity_validator(self, oauth2_clients_test_data, test_app):
|
144
173
|
oauth2_client_proto = oauth2_clients_test_data["oauth2_clients"][7]
|
145
|
-
resp = test_app.get("/admin/oauth2_clients/{}/duplicate"
|
174
|
+
resp = test_app.get(f"/admin/oauth2_clients/{oauth2_client_proto.id}/duplicate", status=200)
|
146
175
|
|
147
176
|
resp = resp.form.submit("submit")
|
148
177
|
|
149
|
-
self._check_submission_problem(resp, "{} is already used."
|
178
|
+
self._check_submission_problem(resp, f"{oauth2_client_proto.client_id} is already used.")
|
150
179
|
|
151
180
|
@pytest.mark.usefixtures("raise_db_error_on_query")
|
152
181
|
def test_grid_dberror(self, dbsession):
|
153
182
|
from c2cgeoportal_admin.views.oauth2_clients import OAuth2ClientViews
|
154
183
|
|
155
184
|
request = DummyRequest(dbsession=dbsession, params={"offset": 0, "limit": 10})
|
156
|
-
|
157
|
-
|
185
|
+
with pytest.raises(pyramid.httpexceptions.HTTPInternalServerError):
|
186
|
+
OAuth2ClientViews(request).grid()
|