Skip to content

Commit 2e926e3

Browse files
committed
test: add integration tests for organisations, users, locations and policies UI
1 parent c0b7a17 commit 2e926e3

8 files changed

Lines changed: 579 additions & 60 deletions

File tree

src/dataplatform/toolbox/policy.py

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
import streamlit as st
44
from dataplatform.toolbox.clients import get_admin_client
5-
5+
from dataplatform.toolbox.clients import get_data_client
66
import grpc
77

88
def policies_section():
99
"""Policy management section."""
1010

1111
admin_client = get_admin_client()
12+
data_client = get_data_client()
1213

1314
# Permission mappings
1415
PERMISSIONS = {
@@ -72,13 +73,41 @@ def policies_section():
7273

7374

7475
# Add Location Policies to Group
76+
77+
## Add a feature to display a dropdown with all locations(uuid and name)
7578
st.markdown(
7679
'<h2 style="color:#ffd053;font-size:32px;">Add Location Policies to Group</h2>',
7780
unsafe_allow_html=True,
7881
)
7982
with st.expander("Add policies to group"):
8083
add_policy_group = st.text_input("Policy Group Name", key="add_policy_group")
81-
add_location_id = st.text_input("Location UUID", key="add_policy_location")
84+
85+
data_client = get_data_client()
86+
locations = []
87+
88+
if data_client:
89+
try:
90+
resp = data_client.ListLocations({})
91+
locations = resp.get("locations", [])
92+
except Exception as e:
93+
st.error(f"❌ Failed to fetch locations: {e}")
94+
95+
if not locations:
96+
st.info("ℹ️ No locations found. Please create a location first.")
97+
98+
location_options = {
99+
f"{loc.get('location_name', 'N/A')}{loc.get('location_uuid', '')}": loc.get("location_uuid")
100+
for loc in locations
101+
}
102+
103+
selected_label = st.selectbox(
104+
"Location",
105+
options=list(location_options.keys()),
106+
key="add_policy_location"
107+
)
108+
109+
add_location_id = location_options.get(selected_label)
110+
82111
add_energy_source = st.selectbox("Energy Source", ["SOLAR", "WIND"], key="add_policy_energy")
83112
add_permission = st.selectbox("Permission", ["READ", "WRITE"], key="add_policy_permission")
84113

@@ -111,7 +140,32 @@ def policies_section():
111140
)
112141
with st.expander("Remove policies from group"):
113142
rem_policy_group = st.text_input("Policy Group Name", key="rem_policy_group")
114-
rem_location_id = st.text_input("Location UUID", key="rem_policy_location")
143+
144+
data_client = get_data_client()
145+
locations = []
146+
147+
if data_client:
148+
try:
149+
resp = data_client.ListLocations({})
150+
locations = resp.get("locations", [])
151+
except Exception as e:
152+
st.error(f"❌ Failed to fetch locations: {e}")
153+
154+
if not locations:
155+
st.info("ℹ️ No locations found. Please create a location first.")
156+
157+
location_options = {
158+
f"{loc.get('location_name', 'N/A')}{loc.get('location_uuid', '')}": loc.get("location_uuid")
159+
for loc in locations
160+
}
161+
162+
selected_label = st.selectbox(
163+
"Location",
164+
options=list(location_options.keys()),
165+
key="rem_policy_location"
166+
)
167+
168+
rem_location_id = location_options.get(selected_label)
115169
rem_energy_source = st.selectbox("Energy Source", ["SOLAR", "WIND"], key="rem_policy_energy")
116170
rem_permission = st.selectbox("Permission", ["READ", "WRITE"], key="rem_policy_permission")
117171

src/dataplatform/toolbox/user_organisation.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ def user_organisation_section():
4141
'<h2 style="color:#E63946;font-size:32px;">Remove User from Organisation</h2>',
4242
unsafe_allow_html=True,
4343
)
44-
rem_org = st.text_input("Organisation Name", key="rem_user_org")
45-
rem_user_oauth = st.text_input("User OAuth ID", key="rem_user_oauth")
46-
if st.button("Remove User from Organisation", key="remove_user_button"):
44+
rem_org = st.text_input("Organisation Name", key="remove_user_org")
45+
rem_user_oauth = st.text_input("User OAuth ID", key="remove_user_oauth")
46+
if st.button("Remove User from Organisation", key="remove_user_from_org_button"):
4747
if not admin_client:
4848
st.error("❌ Could not connect to Data Platform")
4949
elif not rem_org.strip() or not rem_user_oauth.strip():

tests/integration/conftest.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import time
22
from testcontainers.postgres import PostgresContainer
33
from testcontainers.core.container import DockerContainer
4-
import pytest
54
import pytest_asyncio
65
from importlib.metadata import version
76
import os
@@ -10,7 +9,7 @@
109
from grpclib.client import Channel
1110

1211
@pytest_asyncio.fixture(scope="session")
13-
async def client():
12+
async def dp_channel():
1413
"""
1514
Fixture to spin up a PostgreSQL container for the entire test session.
1615
This fixture uses `testcontainers` to start a fresh PostgreSQL container and provides
@@ -51,6 +50,16 @@ async def client():
5150
os.environ["DATA_PLATFORM_PORT"] = str(port)
5251

5352
channel = Channel(host=host, port=port)
54-
client = dp.DataPlatformAdministrationServiceStub(channel)
55-
yield client
56-
channel.close()
53+
# client = dp.DataPlatformAdministrationServiceStub(channel)
54+
# data_client = dp.DataPlatformDataServiceStub(channel)
55+
# yield client, data_client
56+
yield channel
57+
channel.close()
58+
59+
@pytest_asyncio.fixture(scope="session")
60+
async def admin_client(dp_channel):
61+
return dp.DataPlatformAdministrationServiceStub(dp_channel)
62+
63+
@pytest_asyncio.fixture(scope="session")
64+
async def data_client(dp_channel):
65+
return dp.DataPlatformDataServiceStub(dp_channel)
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import uuid
2+
import pytest
3+
import pytest
4+
from streamlit.testing.v1 import AppTest
5+
from dp_sdk.ocf import dp
6+
import pandas as pd
7+
8+
9+
@pytest.fixture
10+
def app():
11+
test_app = AppTest.from_file("src/dataplatform/toolbox/main.py")
12+
test_app.run()
13+
return test_app
14+
15+
16+
@pytest.mark.integration
17+
@pytest.mark.asyncio(loop_scope="session")
18+
async def test_list_locations_ui(app, data_client: dp.DataPlatformDataServiceStub):
19+
# -----------------
20+
# LIST LOCATIONS (UI)
21+
# -----------------
22+
23+
# Create some locations via gRPC
24+
location_names = []
25+
for i in range(3):
26+
location_name = "ui_location_" + str(uuid.uuid4()).replace("-", "_")
27+
location_names.append(location_name)
28+
await data_client.create_location(
29+
dp.CreateLocationRequest(
30+
location_name=location_name,
31+
energy_source=1,
32+
geometry_wkt="Point(0 0)",
33+
location_type=1,
34+
effective_capacity_watts=100,
35+
metadata={}
36+
)
37+
)
38+
app.run()
39+
# Expand filter options and click list button
40+
app.selectbox("list_loc_energy").set_value("All")
41+
app.selectbox("list_loc_type").set_value("All")
42+
app.text_input("list_loc_user").set_value("")
43+
app.run()
44+
app.button("list_locations_button").click()
45+
app.run()
46+
47+
dfs = [df.value for df in app.dataframe]
48+
# Combine all rendered dataframes
49+
all_tables = pd.concat(dfs)
50+
for location_name in location_names:
51+
assert location_name in all_tables["Name"].values
52+
53+
54+
@pytest.mark.integration
55+
@pytest.mark.asyncio(loop_scope="session")
56+
async def test_get_location_ui(app, data_client: dp.DataPlatformDataServiceStub):
57+
# -----------------
58+
# GET LOCATION (UI)
59+
# -----------------
60+
location_name = "ui_location_" + str(uuid.uuid4()).replace("-", "_")
61+
62+
# Create location via gRPC
63+
response = await data_client.create_location(
64+
dp.CreateLocationRequest(
65+
location_name=location_name,
66+
energy_source=1,
67+
geometry_wkt="Point(0 0)",
68+
location_type=1,
69+
effective_capacity_watts=100,
70+
metadata={}
71+
)
72+
)
73+
location_uuid = response.location_uuid
74+
75+
# Get location via UI
76+
app.text_input("get_loc_uuid").set_value(location_uuid)
77+
app.selectbox("get_loc_energy").set_value("SOLAR")
78+
app.checkbox("get_loc_geom").set_value(True)
79+
app.button("get_location_button").click()
80+
app.run()
81+
82+
# Assert location details are displayed
83+
assert any(location_uuid in s.value for s in app.success)
84+
85+
@pytest.mark.integration
86+
@pytest.mark.asyncio(loop_scope="session")
87+
async def test_create_location_ui(app, data_client: dp.DataPlatformDataServiceStub):
88+
# -----------------
89+
# CREATE LOCATION (UI)
90+
# -----------------
91+
location_name = "ui_location_" + str(uuid.uuid4()).replace("-", "_")
92+
93+
# Fill in form and create location via UI
94+
app.text_input("create_loc_name").set_value(location_name)
95+
app.selectbox("create_loc_energy").set_value("WIND")
96+
app.selectbox("create_loc_type").set_value("REGION")
97+
app.text_input("create_loc_geom").set_value("POINT(0 0)")
98+
app.number_input("create_loc_cap").set_value(100)
99+
app.text_area("create_loc_metadata").set_value("{}")
100+
app.button("create_location_button").click()
101+
app.run()
102+
103+
# Assert success message in UI
104+
assert any("created" in s.value.lower() for s in app.success)
105+
106+
# Verify location was actually created via gRPC
107+
response = await data_client.list_locations(
108+
dp.ListLocationsRequest()
109+
)
110+
assert any(loc.location_name == location_name for loc in response.locations)
111+
Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
1+
import uuid
12
from streamlit.testing.v1 import AppTest
23
import pytest
34
from dp_sdk.ocf import dp
45
from src.dataplatform.toolbox.main import dataplatform_toolbox_page
56

7+
8+
@pytest.fixture
9+
def app():
10+
test_app = AppTest.from_file("src/dataplatform/toolbox/main.py")
11+
test_app.run()
12+
return test_app
13+
14+
615
@pytest.mark.integration
716
@pytest.mark.asyncio(loop_scope="session")
8-
async def test_create_organisation_ui(client:dp.DataPlatformAdministrationServiceStub):
17+
async def test_create_organisation_ui(app, admin_client:dp.DataPlatformAdministrationServiceStub):
918
# -----------------
1019
# Create
1120
# -----------------
12-
app = AppTest.from_file("src/dataplatform/toolbox/main.py").run()
13-
# app = AppTest.from_function(dataplatform_toolbox_page).run()
21+
org_name = "org-test-" + str(uuid.uuid4())
1422
app.expander[0].expanded = True
1523
app.run()
1624

1725
# Fill inputs
18-
app.text_input("create_org_name").set_value("ui-test-org")
26+
app.text_input("create_org_name").set_value(org_name)
1927
app.text_area("create_org_metadata").set_value("{}")
2028

2129
# Click button
@@ -25,29 +33,50 @@ async def test_create_organisation_ui(client:dp.DataPlatformAdministrationServic
2533
# Assert success
2634
assert any("created" in s.value.lower() for s in app.success)
2735

28-
response = await client.get_organisation(dp.GetOrganisationRequest(org_name="ui-test-org"))
29-
assert response.org_name == "ui-test-org"
36+
response = await admin_client.get_organisation(dp.GetOrganisationRequest(org_name=org_name))
37+
assert response.org_name == org_name
3038

39+
@pytest.mark.integration
40+
@pytest.mark.asyncio(loop_scope="session")
41+
async def test_get_organisation_ui(app, admin_client:dp.DataPlatformAdministrationServiceStub):
3142
# -----------------
3243
# GET (UI)
3344
# -----------------
34-
app.text_input("get_org_name").set_value("ui-test-org")
45+
org_name = "org-test-" + str(uuid.uuid4())
46+
await admin_client.create_organisation(
47+
dp.CreateOrganisationRequest(
48+
org_name=org_name,
49+
metadata={}
50+
))
51+
app.text_input("get_org_name").set_value(org_name)
3552
app.button("get_org_button").click()
3653
app.run()
3754

38-
assert any("ui-test-org" in s.value for s in app.success)
55+
assert any(org_name in s.value for s in app.success)
56+
57+
@pytest.mark.integration
58+
@pytest.mark.asyncio(loop_scope="session")
59+
async def test_delete_organisation_ui(app, admin_client:dp.DataPlatformAdministrationServiceStub):
60+
# -----------------
61+
# DELETE (UI)
62+
# -----------------
63+
64+
org_name = "org-test-" + str(uuid.uuid4())
3965

40-
# # -----------------
41-
# # DELETE (UI)
42-
# # -----------------
43-
# app.text_input("delete_org_name").set_value("ui-test-org")
44-
# app.button("delete_org_button").click()
45-
# app.run()
66+
await admin_client.create_organisation(
67+
dp.CreateOrganisationRequest(
68+
org_name=org_name,
69+
metadata={}
70+
))
4671

47-
# assert any("deleted" in s.value.lower() for s in app.success)
72+
app.text_input("delete_org_name").set_value(org_name)
73+
app.checkbox("confirm_delete_org").set_value(True)
74+
app.button("delete_org_button").click()
75+
app.run()
76+
assert any("deleted" in s.value.lower() for s in app.success)
4877

49-
# # verify deletion via grpc
50-
# with pytest.raises(Exception):
51-
# await client.get_organisation(
52-
# dp.GetOrganisationRequest(org_name="ui-test-org")
53-
# )
78+
# verify deletion via grpc
79+
with pytest.raises(Exception):
80+
await admin_client.get_organisation(
81+
dp.GetOrganisationRequest(org_name=org_name)
82+
)

0 commit comments

Comments
 (0)