test_assets_api.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. import json
  2. from flask import url_for
  3. import pytest
  4. from sqlalchemy import select, func
  5. from flexmeasures.data.models.audit_log import AssetAuditLog
  6. from flexmeasures.data.models.generic_assets import GenericAsset
  7. from flexmeasures.data.services.users import find_user_by_email
  8. from flexmeasures.api.tests.utils import get_auth_token, UserContext, AccountContext
  9. from flexmeasures.api.v3_0.tests.utils import get_asset_post_data
  10. from flexmeasures.utils.unit_utils import is_valid_unit
  11. @pytest.mark.parametrize(
  12. "requesting_user, status_code",
  13. [
  14. (None, 401), # the case without auth: authentication will fail
  15. (
  16. "test_dummy_user_3@seita.nl",
  17. 403,
  18. ), # fails authorization to get assets on another account
  19. ],
  20. indirect=["requesting_user"],
  21. )
  22. def test_get_assets_badauth(client, setup_api_test_data, requesting_user, status_code):
  23. """
  24. Attempt to get assets with wrong or missing auth.
  25. """
  26. test_prosumer = find_user_by_email("test_prosumer_user@seita.nl")
  27. query = {"account_id": test_prosumer.account.id}
  28. get_assets_response = client.get(url_for("AssetAPI:index"), query_string=query)
  29. print("Server responded with:\n%s" % get_assets_response.json)
  30. assert get_assets_response.status_code == status_code
  31. @pytest.mark.parametrize(
  32. "requesting_user", ["test_supplier_user_4@seita.nl"], indirect=True
  33. )
  34. def test_get_asset_nonaccount_access(client, setup_api_test_data, requesting_user):
  35. """Without being on the same account, test correct responses when accessing one asset."""
  36. with UserContext("test_prosumer_user@seita.nl") as prosumer1:
  37. prosumer1_assets = prosumer1.account.generic_assets
  38. with UserContext("test_supplier_user_4@seita.nl") as supplieruser4:
  39. supplieruser4_assets = supplieruser4.account.generic_assets
  40. # okay to look at assets in own account
  41. asset_response = client.get(
  42. url_for("AssetAPI:fetch_one", id=supplieruser4_assets[0].id),
  43. follow_redirects=True,
  44. )
  45. assert asset_response.status_code == 200
  46. # not okay to see assets owned by other accounts
  47. asset_response = client.get(
  48. url_for("AssetAPI:fetch_one", id=prosumer1_assets[0].id),
  49. follow_redirects=True,
  50. )
  51. assert asset_response.status_code == 403
  52. # proper 404 for non-existing asset
  53. asset_response = client.get(
  54. url_for("AssetAPI:fetch_one", id=8171766575),
  55. follow_redirects=True,
  56. )
  57. assert asset_response.status_code == 404
  58. assert "not found" in asset_response.json["message"]
  59. @pytest.mark.parametrize(
  60. "requesting_user, account_name, num_assets, use_pagination, sort_by, sort_dir, expected_name_of_first_asset",
  61. [
  62. ("test_admin_user@seita.nl", "Prosumer", 1, False, None, None, None),
  63. ("test_admin_user@seita.nl", "Supplier", 2, False, None, None, None),
  64. (
  65. "test_admin_user@seita.nl",
  66. "Supplier",
  67. 2,
  68. False,
  69. "name",
  70. "asc",
  71. "incineration line",
  72. ),
  73. (
  74. "test_admin_user@seita.nl",
  75. "Supplier",
  76. 2,
  77. False,
  78. "name",
  79. "desc",
  80. "Test wind turbine",
  81. ),
  82. ("test_consultant@seita.nl", "ConsultancyClient", 1, False, None, None, None),
  83. ("test_admin_user@seita.nl", "Prosumer", 1, True, None, None, None),
  84. ],
  85. indirect=["requesting_user"],
  86. )
  87. def test_get_assets(
  88. client,
  89. setup_api_test_data,
  90. setup_accounts,
  91. account_name,
  92. num_assets,
  93. use_pagination,
  94. sort_by,
  95. sort_dir,
  96. expected_name_of_first_asset,
  97. requesting_user,
  98. ):
  99. """
  100. Get assets per account.
  101. Our user here is admin, so is allowed to see all assets.
  102. Pagination is tested only in passing, we should test filtering and page > 1
  103. """
  104. query = {"account_id": setup_accounts[account_name].id}
  105. if use_pagination:
  106. query["page"] = 1
  107. if sort_by:
  108. query["sort_by"] = sort_by
  109. if sort_dir:
  110. query["sort_dir"] = sort_dir
  111. get_assets_response = client.get(
  112. url_for("AssetAPI:index"),
  113. query_string=query,
  114. )
  115. print("Server responded with:\n%s" % get_assets_response.json)
  116. assert get_assets_response.status_code == 200
  117. if use_pagination:
  118. assets = get_assets_response.json["data"]
  119. assert get_assets_response.json["num-records"] == num_assets
  120. assert get_assets_response.json["filtered-records"] == num_assets
  121. else:
  122. assets = get_assets_response.json
  123. if sort_by:
  124. assert assets[0]["name"] == expected_name_of_first_asset
  125. assert len(assets) == num_assets
  126. if account_name == "Supplier": # one deep dive
  127. turbine = {}
  128. for asset in assets:
  129. if asset["name"] == "Test wind turbine":
  130. turbine = asset
  131. assert turbine
  132. assert turbine["account_id"] == setup_accounts["Supplier"].id
  133. @pytest.mark.parametrize(
  134. "requesting_user, sort_by, sort_dir, expected_name_of_first_sensor",
  135. [
  136. ("test_admin_user@seita.nl", None, None, None),
  137. ("test_admin_user@seita.nl", "name", "asc", "empty temperature sensor"),
  138. ("test_admin_user@seita.nl", "name", "desc", "some temperature sensor"),
  139. ],
  140. indirect=["requesting_user"],
  141. )
  142. def test_fetch_asset_sensors(
  143. client,
  144. setup_api_test_data,
  145. requesting_user,
  146. sort_by,
  147. sort_dir,
  148. expected_name_of_first_sensor,
  149. ):
  150. """
  151. Retrieve all sensors associated with a specific asset.
  152. This test checks for these metadata fields and the number of sensors returned, as well as
  153. confirming that the response is a list of dictionaries, each containing a valid unit.
  154. """
  155. query = {}
  156. if sort_by:
  157. query["sort_by"] = sort_by
  158. if sort_dir:
  159. query["sort_dir"] = sort_dir
  160. asset_id = setup_api_test_data["some gas sensor"].generic_asset_id
  161. response = client.get(
  162. url_for("AssetAPI:asset_sensors", id=asset_id), query_string=query
  163. )
  164. print("Server responded with:\n%s" % response.json)
  165. assert response.status_code == 200
  166. assert response.json["status"] == 200
  167. assert isinstance(response.json["data"], list)
  168. assert isinstance(response.json["data"][0], dict)
  169. assert is_valid_unit(response.json["data"][0]["unit"])
  170. assert response.json["num-records"] == 3
  171. assert response.json["filtered-records"] == 3
  172. if sort_by:
  173. assert response.json["data"][0]["name"] == expected_name_of_first_sensor
  174. @pytest.mark.parametrize("requesting_user", ["test_admin_user@seita.nl"], indirect=True)
  175. def test_get_asset_with_children(client, add_asset_with_children, requesting_user):
  176. """
  177. Get asset `parent` with children `child_1` and `child_2`.
  178. We expect the response to be the serialized asset including its
  179. child assets in the field `child_assets`.
  180. """
  181. parent = add_asset_with_children["parent"]
  182. get_assets_response = client.get(
  183. url_for("AssetAPI:fetch_one", id=parent.id),
  184. )
  185. print("Server responded with:\n%s" % get_assets_response.json)
  186. assert get_assets_response.status_code == 200
  187. assert len(get_assets_response.json["child_assets"]) == 2
  188. @pytest.mark.parametrize("requesting_user", [None], indirect=True)
  189. def test_get_public_assets_noauth(
  190. client, setup_api_test_data, setup_accounts, requesting_user
  191. ):
  192. get_assets_response = client.get(url_for("AssetAPI:public"))
  193. print("Server responded with:\n%s" % get_assets_response.json)
  194. assert get_assets_response.status_code == 401
  195. @pytest.mark.parametrize("requesting_user", ["test_admin_user@seita.nl"], indirect=True)
  196. def test_get_public_assets(
  197. client, setup_api_test_data, setup_accounts, requesting_user
  198. ):
  199. auth_token = get_auth_token(client, "test_admin_user@seita.nl", "testtest")
  200. get_assets_response = client.get(
  201. url_for("AssetAPI:public"), headers={"Authorization": auth_token}
  202. )
  203. print("Server responded with:\n%s" % get_assets_response.json)
  204. assert get_assets_response.status_code == 200
  205. assert len(get_assets_response.json) == 1
  206. assert get_assets_response.json[0]["name"] == "troposphere"
  207. @pytest.mark.parametrize(
  208. "requesting_user", ["test_prosumer_user@seita.nl"], indirect=True
  209. )
  210. def test_alter_an_asset(
  211. client, setup_api_test_data, setup_accounts, requesting_user, db
  212. ):
  213. # without being an account-admin, no asset can be created ...
  214. with AccountContext("Test Prosumer Account") as prosumer:
  215. prosumer_asset = prosumer.generic_assets[0]
  216. asset_creation_response = client.post(
  217. url_for("AssetAPI:post"),
  218. json={},
  219. )
  220. print(f"Creation Response: {asset_creation_response.json}")
  221. assert asset_creation_response.status_code == 403
  222. # ... or deleted ...
  223. asset_delete_response = client.delete(
  224. url_for("AssetAPI:delete", id=prosumer_asset.id),
  225. json={},
  226. )
  227. print(f"Deletion Response: {asset_delete_response.json}")
  228. assert asset_delete_response.status_code == 403
  229. # ... but editing is allowed.
  230. latitude, name = prosumer_asset.latitude, prosumer_asset.name
  231. asset_edit_response = client.patch(
  232. url_for("AssetAPI:patch", id=prosumer_asset.id),
  233. json={
  234. "latitude": 11.1,
  235. "name": "other",
  236. },
  237. )
  238. print(f"Editing Response: {asset_edit_response.json}")
  239. assert asset_edit_response.status_code == 200
  240. # Resetting changes to keep other tests clean here
  241. asset_edit_response = client.patch(
  242. url_for("AssetAPI:patch", id=prosumer_asset.id),
  243. json={
  244. "latitude": latitude,
  245. "name": name,
  246. },
  247. )
  248. print(f"Editing Response: {asset_edit_response.json}")
  249. assert asset_edit_response.status_code == 200
  250. audit_log_event = f"Updated Field: name, From: {name}, To: other"
  251. assert db.session.execute(
  252. select(AssetAuditLog).filter_by(
  253. event=audit_log_event,
  254. active_user_id=requesting_user.id,
  255. active_user_name=requesting_user.username,
  256. affected_asset_id=prosumer_asset.id,
  257. )
  258. ).scalar_one_or_none()
  259. audit_log_event = f"Updated Field: latitude, From: {latitude}, To: 11.1"
  260. assert db.session.execute(
  261. select(AssetAuditLog).filter_by(
  262. event=audit_log_event,
  263. active_user_id=requesting_user.id,
  264. active_user_name=requesting_user.username,
  265. affected_asset_id=prosumer_asset.id,
  266. )
  267. ).scalar_one_or_none()
  268. @pytest.mark.parametrize(
  269. "bad_json_str, error_msg",
  270. [
  271. (None, "may not be null"),
  272. ("{", "Not a valid JSON"),
  273. ('{"hallo": world}', "Not a valid JSON"),
  274. ('{"sensors_to_show": [0, 1]}', "No sensor found"), # no sensor with ID 0
  275. ('{"sensors_to_show": [1, [0, 2]]}', "No sensor found"), # no sensor with ID 0
  276. (
  277. '{"sensors_to_show": [1, [2, [3, 4]]]}',
  278. "All elements in a list within 'sensors_to_show' must be integers.",
  279. ), # nesting level max 1
  280. (
  281. '{"sensors_to_show": [1, "2"]}',
  282. "Invalid item type in 'sensors_to_show'. Expected int, list, or dict.",
  283. ), # non-integer sensor ID
  284. ],
  285. )
  286. @pytest.mark.parametrize(
  287. "requesting_user", ["test_prosumer_user@seita.nl"], indirect=True
  288. )
  289. def test_alter_an_asset_with_bad_json_attributes(
  290. client,
  291. setup_api_test_data,
  292. setup_accounts,
  293. bad_json_str,
  294. error_msg,
  295. requesting_user,
  296. ):
  297. """Check whether updating an asset's attributes with a badly structured JSON fails."""
  298. with AccountContext("Test Prosumer Account") as prosumer:
  299. prosumer_asset = prosumer.generic_assets[0]
  300. asset_edit_response = client.patch(
  301. url_for("AssetAPI:patch", id=prosumer_asset.id),
  302. json={"attributes": bad_json_str},
  303. )
  304. print(f"Editing Response: {asset_edit_response.json}")
  305. assert asset_edit_response.status_code == 422
  306. assert error_msg in asset_edit_response.json["message"]["json"]["attributes"][0]
  307. @pytest.mark.parametrize(
  308. "requesting_user", ["test_prosumer_user@seita.nl"], indirect=True
  309. )
  310. def test_alter_an_asset_with_json_attributes(
  311. client, setup_api_test_data, setup_accounts, requesting_user
  312. ):
  313. """Check whether updating an asset's attributes with a properly structured JSON succeeds."""
  314. with AccountContext("Test Prosumer Account") as prosumer:
  315. prosumer_asset = prosumer.generic_assets[0]
  316. assert prosumer_asset.attributes[
  317. "sensors_to_show"
  318. ] # make sure we run this test on an asset with a non-empty sensors_to_show attribute
  319. asset_edit_response = client.patch(
  320. url_for("AssetAPI:patch", id=prosumer_asset.id),
  321. json={
  322. "attributes": json.dumps(prosumer_asset.attributes)
  323. }, # we're not changing values to keep other tests clean here
  324. )
  325. print(f"Editing Response: {asset_edit_response.json}")
  326. assert asset_edit_response.status_code == 200
  327. @pytest.mark.parametrize(
  328. "requesting_user", ["test_prosumer_user_2@seita.nl"], indirect=True
  329. )
  330. def test_post_an_asset_with_other_account(client, setup_api_test_data, requesting_user):
  331. """Catch auth error, when account-admin posts an asset for another account"""
  332. with AccountContext("Test Supplier Account") as supplier:
  333. supplier_id = supplier.id
  334. post_data = get_asset_post_data()
  335. post_data["account_id"] = supplier_id
  336. asset_creation_response = client.post(
  337. url_for("AssetAPI:post"),
  338. json=post_data,
  339. )
  340. print(f"Creation Response: {asset_creation_response.json}")
  341. assert asset_creation_response.status_code == 422
  342. assert (
  343. "not allowed to create assets for this account"
  344. in asset_creation_response.json["message"]["json"]["account_id"][0]
  345. )
  346. @pytest.mark.parametrize("requesting_user", ["test_admin_user@seita.nl"], indirect=True)
  347. def test_post_an_asset_with_nonexisting_field(
  348. client, setup_api_test_data, requesting_user
  349. ):
  350. """Posting a field that is unexpected leads to a 422"""
  351. post_data = get_asset_post_data()
  352. post_data["nnname"] = "This field does not exist"
  353. asset_creation = client.post(
  354. url_for("AssetAPI:post"),
  355. json=post_data,
  356. )
  357. assert asset_creation.status_code == 422
  358. assert asset_creation.json["message"]["json"]["nnname"][0] == "Unknown field."
  359. @pytest.mark.parametrize("requesting_user", ["test_admin_user@seita.nl"], indirect=True)
  360. def test_posting_multiple_assets(client, setup_api_test_data, requesting_user):
  361. """We can only send one at a time"""
  362. post_data1 = get_asset_post_data()
  363. post_data2 = get_asset_post_data()
  364. post_data2["name"] = "Test battery 3"
  365. asset_creation = client.post(
  366. url_for("AssetAPI:post"),
  367. json=[post_data1, post_data2],
  368. )
  369. print(f"Response: {asset_creation.json}")
  370. assert asset_creation.status_code == 422
  371. assert asset_creation.json["message"]["json"]["_schema"][0] == "Invalid input type."
  372. @pytest.mark.parametrize("requesting_user", ["test_admin_user@seita.nl"], indirect=True)
  373. def test_post_an_asset_with_invalid_data(
  374. client, setup_api_test_data, requesting_user, db
  375. ):
  376. """
  377. Add an asset with some fields having invalid data and one field missing.
  378. The right error messages should be in the response and the number of assets has not increased.
  379. """
  380. num_assets_before = len(requesting_user.account.generic_assets)
  381. post_data = get_asset_post_data()
  382. post_data["name"] = "Something new"
  383. post_data["longitude"] = 300.9
  384. del post_data["generic_asset_type_id"]
  385. post_asset_response = client.post(
  386. url_for("AssetAPI:post"),
  387. json=post_data,
  388. )
  389. print("Server responded with:\n%s" % post_asset_response.json)
  390. assert post_asset_response.status_code == 422
  391. assert (
  392. "exceeds the maximum longitude"
  393. in post_asset_response.json["message"]["json"]["longitude"][0]
  394. )
  395. assert (
  396. "required field"
  397. in post_asset_response.json["message"]["json"]["generic_asset_type_id"][0]
  398. )
  399. assert (
  400. db.session.scalar(
  401. select(func.count())
  402. .select_from(GenericAsset)
  403. .filter_by(account_id=requesting_user.account.id)
  404. )
  405. == num_assets_before
  406. )
  407. @pytest.mark.parametrize("requesting_user", ["test_admin_user@seita.nl"], indirect=True)
  408. def test_post_an_asset(client, setup_api_test_data, requesting_user, db):
  409. """
  410. Post one extra asset, as an admin user.
  411. TODO: Soon we'll allow creating assets on an account-basis, i.e. for users
  412. who have the user role "account-admin" or something similar. Then we'll
  413. test that here.
  414. """
  415. post_data = get_asset_post_data()
  416. post_assets_response = client.post(
  417. url_for("AssetAPI:post"),
  418. json=post_data,
  419. )
  420. print("Server responded with:\n%s" % post_assets_response.json)
  421. assert post_assets_response.status_code == 201
  422. assert post_assets_response.json["latitude"] == 30.1
  423. asset: GenericAsset = db.session.execute(
  424. select(GenericAsset).filter_by(name="Test battery 2")
  425. ).scalar_one_or_none()
  426. assert asset is not None
  427. assert asset.latitude == 30.1
  428. assert db.session.execute(
  429. select(AssetAuditLog).filter_by(
  430. affected_asset_id=asset.id,
  431. event=f"Created asset '{asset.name}': {asset.id}",
  432. active_user_id=requesting_user.id,
  433. active_user_name=requesting_user.username,
  434. )
  435. ).scalar_one_or_none()
  436. @pytest.mark.parametrize("requesting_user", ["test_admin_user@seita.nl"], indirect=True)
  437. def test_delete_an_asset(client, setup_api_test_data, requesting_user, db):
  438. existing_asset = setup_api_test_data["some gas sensor"].generic_asset
  439. existing_asset_id, existing_asset_name = existing_asset.id, existing_asset.name
  440. delete_asset_response = client.delete(
  441. url_for("AssetAPI:delete", id=existing_asset_id),
  442. )
  443. assert delete_asset_response.status_code == 204
  444. deleted_asset = db.session.execute(
  445. select(GenericAsset).filter_by(id=existing_asset_id)
  446. ).scalar_one_or_none()
  447. assert deleted_asset is None
  448. audit_log = db.session.execute(
  449. select(AssetAuditLog).filter_by(
  450. event=f"Deleted asset '{existing_asset_name}': {existing_asset_id}",
  451. active_user_id=requesting_user.id,
  452. active_user_name=requesting_user.username,
  453. )
  454. ).scalar_one_or_none()
  455. assert audit_log.affected_asset_id is None
  456. @pytest.mark.parametrize(
  457. "requesting_user",
  458. ["test_consultant@seita.nl"],
  459. indirect=True,
  460. )
  461. def test_consultant_can_read(
  462. client,
  463. setup_api_test_data,
  464. setup_accounts,
  465. requesting_user,
  466. ):
  467. """
  468. The Consultant Account reads the assets from the ConsultancyClient Account.
  469. """
  470. account_name = "ConsultancyClient"
  471. query = {"account_id": setup_accounts[account_name].id}
  472. get_assets_response = client.get(
  473. url_for("AssetAPI:index"),
  474. query_string=query,
  475. )
  476. print("Server responded with:\n%s" % get_assets_response.json)
  477. assert get_assets_response.status_code == 200
  478. assert len(get_assets_response.json) == 1
  479. assert get_assets_response.json[0]["name"] == "Test ConsultancyClient Asset"
  480. @pytest.mark.parametrize("requesting_user", ["test_consultant@seita.nl"], indirect=True)
  481. def test_consultant_can_not_patch(
  482. client, setup_api_test_data, setup_accounts, requesting_user, db
  483. ):
  484. """
  485. Try to edit an asset belonging to the ConsultancyClient account with the Consultant account.
  486. The Consultant account only has read access.
  487. """
  488. consultancy_client_asset = db.session.execute(
  489. select(GenericAsset).filter_by(name="Test ConsultancyClient Asset")
  490. ).scalar_one_or_none()
  491. print(consultancy_client_asset)
  492. asset_edit_response = client.patch(
  493. url_for("AssetAPI:patch", id=consultancy_client_asset.id),
  494. json={
  495. "latitude": 0,
  496. },
  497. )
  498. print(f"Editing Response: {asset_edit_response.json}")
  499. assert asset_edit_response.status_code == 403
  500. @pytest.mark.parametrize(
  501. "requesting_user",
  502. ["test_consultancy_user_without_consultant_access@seita.nl"],
  503. indirect=True,
  504. )
  505. def test_consultancy_user_without_consultant_role(
  506. client,
  507. setup_api_test_data,
  508. setup_accounts,
  509. requesting_user,
  510. ):
  511. """
  512. The Consultant Account user without customer manager role can not read.
  513. """
  514. account_name = "ConsultancyClient"
  515. query = {"account_id": setup_accounts[account_name].id}
  516. get_assets_response = client.get(
  517. url_for("AssetAPI:index"),
  518. query_string=query,
  519. )
  520. print("Server responded with:\n%s" % get_assets_response.json)
  521. assert get_assets_response.status_code == 403
  522. @pytest.mark.parametrize(
  523. "parent_name, child_name, fails",
  524. [
  525. ("parent", "child_4", False),
  526. (None, "child_1", False),
  527. (None, "child_1", True),
  528. ("parent", "child_1", True),
  529. ],
  530. )
  531. @pytest.mark.parametrize("requesting_user", ["test_admin_user@seita.nl"], indirect=True)
  532. def test_post_an_asset_with_existing_name(
  533. client, add_asset_with_children, parent_name, child_name, fails, requesting_user, db
  534. ):
  535. """Catch DB error (Unique key violated) correctly.
  536. Cases:
  537. 1) Create a child asset
  538. 2) Create an orphan asset with a name that already exists under a parent asset
  539. 3) Create an orphan asset with an existing name.
  540. 4) Create a child asset with a name that already exists among its siblings.
  541. """
  542. post_data = get_asset_post_data()
  543. def get_asset_with_name(asset_name):
  544. return db.session.execute(
  545. select(GenericAsset).filter_by(name=asset_name)
  546. ).scalar_one_or_none()
  547. parent = get_asset_with_name(parent_name)
  548. post_data["name"] = child_name
  549. post_data["account_id"] = requesting_user.account_id
  550. if parent:
  551. post_data["parent_asset_id"] = parent.parent_asset_id
  552. asset_creation_response = client.post(
  553. url_for("AssetAPI:post"),
  554. json=post_data,
  555. )
  556. if fails:
  557. assert asset_creation_response.status_code == 422
  558. assert (
  559. "already exists"
  560. in asset_creation_response.json["message"]["json"]["name"][0]
  561. )
  562. else:
  563. assert asset_creation_response.status_code == 201
  564. for key, val in post_data.items():
  565. assert asset_creation_response.json[key] == val
  566. # check that the asset exists
  567. assert (
  568. db.session.get(GenericAsset, asset_creation_response.json["id"]) is not None
  569. )
  570. @pytest.mark.parametrize(
  571. "requesting_user",
  572. ["test_consultant@seita.nl"],
  573. indirect=True,
  574. )
  575. def test_consultant_get_asset(
  576. client, setup_api_test_data, setup_accounts, requesting_user, db
  577. ):
  578. """
  579. The Consultant Account reads an asset from the ConsultancyClient Account.
  580. """
  581. asset = db.session.execute(
  582. select(GenericAsset).filter_by(name="Test ConsultancyClient Asset")
  583. ).scalar_one_or_none()
  584. get_asset_response = client.get(url_for("AssetAPI:get", id=asset.id))
  585. print("Server responded with:\n%s" % get_asset_response.json)
  586. assert get_asset_response.status_code == 200
  587. assert get_asset_response.json["name"] == "Test ConsultancyClient Asset"