/
test_api_v2_0_assets.py
293 lines (256 loc) · 11.6 KB
/
test_api_v2_0_assets.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
from flask import url_for
import pytest
import pandas as pd
from flexmeasures.data.models.assets import Asset
from flexmeasures.data.services.users import find_user_by_email
from flexmeasures.api.tests.utils import get_auth_token, UserContext
from flexmeasures.api.v2_0.tests.utils import get_asset_post_data
@pytest.mark.parametrize("use_auth", [False, True])
def test_get_assets_badauth(client, use_auth):
"""
Attempt to get assets with wrong or missing auth.
"""
# the case without auth: authentication will fail
headers = {"content-type": "application/json"}
query = {}
if use_auth:
# in this case, we successfully authenticate, but fail authorization
headers["Authorization"] = get_auth_token(
client, "test_prosumer_user_2@seita.nl", "testtest"
)
test_prosumer_id = find_user_by_email("test_prosumer_user@seita.nl").id
query = {"owner_id": test_prosumer_id}
get_assets_response = client.get(
url_for("flexmeasures_api_v2_0.get_assets"), query_string=query, headers=headers
)
print("Server responded with:\n%s" % get_assets_response.json)
if use_auth:
assert get_assets_response.status_code == 403
else:
assert get_assets_response.status_code == 401
def test_get_asset_nonadmin_access(client):
"""Without being an admin, test correct responses when accessing one asset."""
with UserContext("test_prosumer_user@seita.nl") as prosumer1:
prosumer1_assets = prosumer1.assets
with UserContext("test_prosumer_user_2@seita.nl") as prosumer2:
prosumer2_assets = prosumer2.assets
headers = {
"content-type": "application/json",
"Authorization": get_auth_token(
client, "test_prosumer_user_2@seita.nl", "testtest"
),
}
# okay to look at own asset
asset_response = client.get(
url_for("flexmeasures_api_v2_0.get_asset", id=prosumer2_assets[0].id),
headers=headers,
follow_redirects=True,
)
assert asset_response.status_code == 200
# not okay to see assets owned by others
asset_response = client.get(
url_for("flexmeasures_api_v2_0.get_asset", id=prosumer1_assets[0].id),
headers=headers,
follow_redirects=True,
)
assert asset_response.status_code == 403
# proper 404 for non-existing asset
asset_response = client.get(
url_for("flexmeasures_api_v2_0.get_asset", id=8171766575),
headers=headers,
follow_redirects=True,
)
assert asset_response.status_code == 404
assert "not found" in asset_response.json["message"]
@pytest.mark.parametrize("use_owner_id, num_assets", [(False, 7), (True, 1)])
def test_get_assets(client, add_charging_station_assets, use_owner_id, num_assets):
"""
Get assets, either for all users (our user here is admin, so is allowed to see all 7 assets) or for
a unique one (prosumer user 2 has one asset ― "Test battery").
"""
auth_token = get_auth_token(client, "test_admin_user@seita.nl", "testtest")
test_prosumer2_id = find_user_by_email("test_prosumer_user_2@seita.nl").id
query = {}
if use_owner_id:
query["owner_id"] = test_prosumer2_id
get_assets_response = client.get(
url_for("flexmeasures_api_v2_0.get_assets"),
query_string=query,
headers={"content-type": "application/json", "Authorization": auth_token},
)
print("Server responded with:\n%s" % get_assets_response.json)
assert get_assets_response.status_code == 200
assert len(get_assets_response.json) == num_assets
battery = {}
for asset in get_assets_response.json:
if asset["name"] == "Test battery":
battery = asset
assert battery
assert pd.Timestamp(battery["soc_datetime"]) == pd.Timestamp(
"2015-01-01T00:00:00+01:00"
)
assert battery["owner_id"] == test_prosumer2_id
assert battery["capacity_in_mw"] == 2
def test_alter_an_asset_wrongauth(client):
# without admin and owner rights, no asset can be created ...
with UserContext("test_prosumer_user@seita.nl") as prosumer1:
prosumer1_asset = prosumer1.assets[0]
with UserContext("test_prosumer_user_2@seita.nl") as prosumer2:
auth_token = prosumer2.get_auth_token()
prosumer2_asset = prosumer2.assets[0]
asset_creation_response = client.post(
url_for("flexmeasures_api_v2_0.post_assets"),
headers={"content-type": "application/json", "Authorization": auth_token},
json={},
)
print(f"Response: {asset_creation_response.json}")
assert asset_creation_response.status_code == 403
# ... or edited ...
asset_edit_response = client.patch(
url_for("flexmeasures_api_v2_0.patch_asset", id=prosumer1_asset.id),
headers={"content-type": "application/json", "Authorization": auth_token},
json={},
)
assert asset_edit_response.status_code == 403
# ... or deleted ...
asset_delete_response = client.delete(
url_for("flexmeasures_api_v2_0.delete_asset", id=prosumer1_asset.id),
headers={"content-type": "application/json", "Authorization": auth_token},
json={},
)
assert asset_delete_response.status_code == 403
# ... which is impossible even if you're the owner
asset_delete_response = client.delete(
url_for("flexmeasures_api_v2_0.delete_asset", id=prosumer2_asset.id),
headers={"content-type": "application/json", "Authorization": auth_token},
json={},
)
assert asset_delete_response.status_code == 403
def test_post_an_asset_with_existing_name(client):
"""Catch DB error (Unique key violated) correctly"""
with UserContext("test_admin_user@seita.nl") as prosumer:
auth_token = prosumer.get_auth_token()
with UserContext("test_prosumer_user@seita.nl") as prosumer:
test_prosumer_id = prosumer.id
existing_asset = prosumer.assets[0]
post_data = get_asset_post_data()
post_data["name"] = existing_asset.name
post_data["owner_id"] = test_prosumer_id
asset_creation = client.post(
url_for("flexmeasures_api_v2_0.post_assets"),
json=post_data,
headers={"content-type": "application/json", "Authorization": auth_token},
)
assert asset_creation.status_code == 422
assert "already exists" in asset_creation.json["message"]["json"]["name"][0]
def test_post_an_asset_with_nonexisting_field(client):
"""Posting a field that is unexpected leads to a 422"""
with UserContext("test_admin_user@seita.nl") as prosumer:
auth_token = prosumer.get_auth_token()
post_data = get_asset_post_data()
post_data["nnname"] = "This field does not exist"
asset_creation = client.post(
url_for("flexmeasures_api_v2_0.post_assets"),
json=post_data,
headers={"content-type": "application/json", "Authorization": auth_token},
)
assert asset_creation.status_code == 422
assert asset_creation.json["message"]["json"]["nnname"][0] == "Unknown field."
def test_posting_multiple_assets(client):
"""We can only send one at a time"""
with UserContext("test_admin_user@seita.nl") as prosumer:
auth_token = prosumer.get_auth_token()
post_data1 = get_asset_post_data()
post_data2 = get_asset_post_data()
post_data2["name"] = "Test battery 3"
asset_creation = client.post(
url_for("flexmeasures_api_v2_0.post_assets"),
json=[post_data1, post_data2],
headers={"content-type": "application/json", "Authorization": auth_token},
)
print(f"Response: {asset_creation.json}")
assert asset_creation.status_code == 422
assert asset_creation.json["message"]["json"]["_schema"][0] == "Invalid input type."
def test_post_an_asset(client):
"""
Post one extra asset, as an admin user.
TODO: Soon we'll allow creating assets on an account-basis, i.e. for users
who have the user role "account-admin" or sthg similar. Then we'll
test that here.
"""
auth_token = get_auth_token(client, "test_admin_user@seita.nl", "testtest")
post_data = get_asset_post_data()
post_assets_response = client.post(
url_for("flexmeasures_api_v2_0.post_assets"),
json=post_data,
headers={"content-type": "application/json", "Authorization": auth_token},
)
print("Server responded with:\n%s" % post_assets_response.json)
assert post_assets_response.status_code == 201
assert post_assets_response.json["latitude"] == 30.1
asset: Asset = Asset.query.filter(Asset.name == "Test battery 2").one_or_none()
assert asset is not None
assert asset.capacity_in_mw == 3
def test_post_an_asset_with_invalid_data(client, db):
"""
Add an asset with some fields having invalid data and one field missing.
The right error messages should be in the response and the number of assets has not increased.
"""
with UserContext("test_admin_user@seita.nl") as prosumer:
num_assets_before = len(prosumer.assets)
auth_token = get_auth_token(client, "test_admin_user@seita.nl", "testtest")
post_data = get_asset_post_data()
post_data["latitude"] = 70.4
post_data["longitude"] = 300.9
post_data["capacity_in_mw"] = -100
post_data["min_soc_in_mwh"] = 10
post_data["max_soc_in_mwh"] = 5
del post_data["unit"]
post_asset_response = client.post(
url_for("flexmeasures_api_v2_0.post_assets"),
json=post_data,
headers={"content-type": "application/json", "Authorization": auth_token},
)
print("Server responded with:\n%s" % post_asset_response.json)
assert post_asset_response.status_code == 422
assert (
"Must be greater than or equal to 0"
in post_asset_response.json["message"]["json"]["capacity_in_mw"][0]
)
assert (
"greater than or equal to -180 and less than or equal to 180"
in post_asset_response.json["message"]["json"]["longitude"][0]
)
assert "required field" in post_asset_response.json["message"]["json"]["unit"][0]
assert (
"must be equal or higher than the minimum soc"
in post_asset_response.json["message"]["json"]["max_soc_in_mwh"]
)
assert Asset.query.filter_by(owner_id=prosumer.id).count() == num_assets_before
def test_edit_an_asset(client, db):
with UserContext("test_prosumer_user@seita.nl") as prosumer:
existing_asset = prosumer.assets[1]
post_data = dict(latitude=10, id=999) # id will be ignored
auth_token = get_auth_token(client, "test_admin_user@seita.nl", "testtest")
edit_asset_response = client.patch(
url_for("flexmeasures_api_v2_0.patch_asset", id=existing_asset.id),
json=post_data,
headers={"content-type": "application/json", "Authorization": auth_token},
)
assert edit_asset_response.status_code == 200
updated_asset = Asset.query.filter_by(id=existing_asset.id).one_or_none()
assert updated_asset.latitude == 10 # changed value
assert updated_asset.longitude == existing_asset.longitude
assert updated_asset.capacity_in_mw == existing_asset.capacity_in_mw
assert updated_asset.name == existing_asset.name
def test_delete_an_asset(client, db):
with UserContext("test_prosumer_user@seita.nl") as prosumer:
existing_asset_id = prosumer.assets[0].id
auth_token = get_auth_token(client, "test_admin_user@seita.nl", "testtest")
delete_asset_response = client.delete(
url_for("flexmeasures_api_v2_0.delete_asset", id=existing_asset_id),
headers={"content-type": "application/json", "Authorization": auth_token},
)
assert delete_asset_response.status_code == 204
deleted_asset = Asset.query.filter_by(id=existing_asset_id).one_or_none()
assert deleted_asset is None