Skip to content

Commit 9eea926

Browse files
authored
Extend fleet and instance permission tests (#3627)
Add tests to ensure upcoming cross-project fleet sharing changes do not break existing contracts.
1 parent 6d61113 commit 9eea926

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

src/tests/_internal/server/routers/test_fleets.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,51 @@ async def test_returns_40x_if_not_authenticated(
192192
response = await client.post("/api/project/main/fleets/get")
193193
assert response.status_code in [401, 403]
194194

195+
@pytest.mark.asyncio
196+
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
197+
@pytest.mark.parametrize(
198+
"by_id", [pytest.param(False, id="by-name"), pytest.param(True, id="by-id")]
199+
)
200+
async def test_returns_403_on_nonexistent_fleet_in_foreign_project(
201+
self, test_db, session: AsyncSession, client: AsyncClient, by_id: bool
202+
):
203+
await create_project(session, name="test-project")
204+
user = await create_user(session, global_role=GlobalRole.USER) # not a project member
205+
if by_id:
206+
body = {"id": str(uuid4())}
207+
else:
208+
body = {"name": "nonexistent"}
209+
response = await client.post(
210+
"/api/project/test-project/fleets/get",
211+
headers=get_auth_headers(user.token),
212+
json=body,
213+
)
214+
assert response.status_code == 403
215+
216+
@pytest.mark.asyncio
217+
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
218+
@pytest.mark.parametrize(
219+
"by_id", [pytest.param(False, id="by-name"), pytest.param(True, id="by-id")]
220+
)
221+
async def test_returns_403_on_deleted_fleet_in_foreign_project(
222+
self, test_db, session: AsyncSession, client: AsyncClient, by_id: bool
223+
):
224+
project = await create_project(session, name="test-project")
225+
user = await create_user(session, global_role=GlobalRole.USER) # not a project member
226+
fleet = await create_fleet(
227+
session=session, project=project, deleted=True, name="deleted-fleet"
228+
)
229+
if by_id:
230+
body = {"id": str(fleet.id)}
231+
else:
232+
body = {"name": "deleted-fleet"}
233+
response = await client.post(
234+
"/api/project/test-project/fleets/get",
235+
headers=get_auth_headers(user.token),
236+
json=body,
237+
)
238+
assert response.status_code == 403
239+
195240
@pytest.mark.asyncio
196241
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
197242
@pytest.mark.parametrize("deleted", [False, True])
@@ -303,6 +348,29 @@ async def test_not_returns_by_name_if_fleet_does_not_exist(
303348
)
304349
assert response.status_code == 400
305350

351+
@pytest.mark.asyncio
352+
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
353+
@pytest.mark.parametrize(
354+
"by_id", [pytest.param(False, id="by-name"), pytest.param(True, id="by-id")]
355+
)
356+
async def test_returns_foreign_fleet_to_global_admin(
357+
self, test_db, session: AsyncSession, client: AsyncClient, by_id: bool
358+
):
359+
admin = await create_user(session, global_role=GlobalRole.ADMIN)
360+
project = await create_project(session, name="test-project")
361+
fleet = await create_fleet(session=session, project=project, name="test-fleet")
362+
if by_id:
363+
body = {"id": str(fleet.id)}
364+
else:
365+
body = {"name": "test-fleet"}
366+
response = await client.post(
367+
"/api/project/test-project/fleets/get",
368+
headers=get_auth_headers(admin.token),
369+
json=body,
370+
)
371+
assert response.status_code == 200
372+
assert response.json()["name"] == "test-fleet"
373+
306374

307375
class TestApplyFleetPlan:
308376
@pytest.mark.asyncio

src/tests/_internal/server/routers/test_instances.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,23 @@ async def test_returns_instance_by_id(
422422
assert resp_data["project_name"] == project.name
423423
assert resp_data["fleet_name"] == fleet.name
424424

425+
async def test_returns_instance_to_global_admin(
426+
self, session: AsyncSession, client: AsyncClient
427+
) -> None:
428+
admin = await create_user(session, global_role=GlobalRole.ADMIN, name="global-admin")
429+
project = await create_project(session)
430+
fleet = await create_fleet(session, project)
431+
instance = await create_instance(session=session, project=project, fleet=fleet)
432+
433+
resp = await client.post(
434+
f"/api/project/{project.name}/instances/get",
435+
headers=get_auth_headers(admin.token),
436+
json={"id": str(instance.id)},
437+
)
438+
assert resp.status_code == 200
439+
resp_data = resp.json()
440+
assert resp_data["id"] == str(instance.id)
441+
425442
async def test_returns_400_if_instance_not_found(
426443
self, session: AsyncSession, client: AsyncClient
427444
) -> None:
@@ -479,3 +496,16 @@ async def test_returns_403_if_not_project_member(
479496
json={"id": str(instance.id)},
480497
)
481498
assert resp.status_code == 403
499+
500+
async def test_returns_403_if_not_project_member_and_instance_not_exists(
501+
self, session: AsyncSession, client: AsyncClient
502+
) -> None:
503+
user = await create_user(session, name="non_member", global_role=GlobalRole.USER)
504+
project = await create_project(session)
505+
506+
resp = await client.post(
507+
f"/api/project/{project.name}/instances/get",
508+
headers=get_auth_headers(user.token),
509+
json={"id": str(uuid.uuid4())},
510+
)
511+
assert resp.status_code == 403

0 commit comments

Comments
 (0)