Skip to content

Commit be12d8f

Browse files
committed
wip
1 parent a806dac commit be12d8f

3 files changed

Lines changed: 670 additions & 645 deletions

File tree

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Callable, Coroutine
4+
from typing import Any
5+
6+
import pytest
7+
8+
from ai.backend.client.exceptions import BackendAPIError
9+
from ai.backend.client.v2.registry import BackendAIClientRegistry
10+
from ai.backend.common.dto.manager.vfolder import (
11+
GetQuotaQuery,
12+
GetQuotaResponse,
13+
GetUsageQuery,
14+
GetUsageResponse,
15+
GetUsedBytesQuery,
16+
GetUsedBytesResponse,
17+
UpdateQuotaReq,
18+
UpdateQuotaResponse,
19+
)
20+
from ai.backend.common.types import QuotaScopeID, QuotaScopeType
21+
22+
VFolderFixtureData = dict[str, Any]
23+
VFolderFactory = Callable[..., Coroutine[Any, Any, VFolderFixtureData]]
24+
25+
26+
class TestStorageQuotaScope:
27+
"""Storage quota CRUD and access control via the quota scope API.
28+
All tests are xfail because they require a live storage-proxy connection
29+
that is not available in the CI environment."""
30+
31+
@pytest.mark.xfail(strict=False, reason="Requires live storage-proxy")
32+
async def test_get_quota(
33+
self,
34+
admin_registry: BackendAIClientRegistry,
35+
target_vfolder: VFolderFixtureData,
36+
) -> None:
37+
"""Scenario: Admin queries the quota for an existing USER vfolder on the
38+
'local' storage host. Verifies the response is a GetQuotaResponse with
39+
a data dict containing quota limit information."""
40+
result = await admin_registry.vfolder.get_quota(
41+
GetQuotaQuery(
42+
folder_host="local",
43+
id=target_vfolder["id"],
44+
),
45+
)
46+
assert isinstance(result, GetQuotaResponse)
47+
assert isinstance(result.data, dict)
48+
49+
@pytest.mark.xfail(strict=False, reason="Requires live storage-proxy")
50+
async def test_update_quota(
51+
self,
52+
admin_registry: BackendAIClientRegistry,
53+
target_vfolder: VFolderFixtureData,
54+
) -> None:
55+
"""Scenario: Admin updates the quota for an existing vfolder to 100 MiB.
56+
Verifies the response is an UpdateQuotaResponse, confirming the
57+
storage-proxy accepted the new quota limit."""
58+
result = await admin_registry.vfolder.update_quota(
59+
UpdateQuotaReq(
60+
folder_host="local",
61+
id=target_vfolder["id"],
62+
input={"size_bytes": 1024 * 1024 * 100},
63+
),
64+
)
65+
assert isinstance(result, UpdateQuotaResponse)
66+
67+
@pytest.mark.xfail(strict=False, reason="Requires live storage-proxy")
68+
async def test_get_usage(
69+
self,
70+
admin_registry: BackendAIClientRegistry,
71+
target_vfolder: VFolderFixtureData,
72+
) -> None:
73+
"""Scenario: Admin queries folder usage (file_count, used_bytes) for an
74+
existing vfolder. Verifies the response is a GetUsageResponse with a
75+
data dict containing usage statistics from the storage-proxy."""
76+
result = await admin_registry.vfolder.get_usage(
77+
GetUsageQuery(
78+
folder_host="local",
79+
id=target_vfolder["id"],
80+
),
81+
)
82+
assert isinstance(result, GetUsageResponse)
83+
assert isinstance(result.data, dict)
84+
85+
@pytest.mark.xfail(strict=False, reason="Requires live storage-proxy")
86+
async def test_get_used_bytes(
87+
self,
88+
admin_registry: BackendAIClientRegistry,
89+
target_vfolder: VFolderFixtureData,
90+
) -> None:
91+
"""Scenario: Admin queries the used_bytes metric for an existing vfolder.
92+
This is a lightweight alternative to get_usage that returns only the byte
93+
count. Verifies the response is a GetUsedBytesResponse with a data dict."""
94+
result = await admin_registry.vfolder.get_used_bytes(
95+
GetUsedBytesQuery(
96+
folder_host="local",
97+
id=target_vfolder["id"],
98+
),
99+
)
100+
assert isinstance(result, GetUsedBytesResponse)
101+
assert isinstance(result.data, dict)
102+
103+
@pytest.mark.xfail(strict=False, reason="Requires live storage-proxy")
104+
async def test_regular_user_can_get_own_quota(
105+
self,
106+
user_registry: BackendAIClientRegistry,
107+
vfolder_factory: VFolderFactory,
108+
regular_user_fixture: Any,
109+
) -> None:
110+
"""Scenario: A regular (non-admin) user creates a vfolder under their own
111+
quota scope (QuotaScopeType.USER) and queries its quota. Verifies that
112+
non-admin users are allowed to read quota info for their own vfolders."""
113+
user_uuid = regular_user_fixture.user_uuid
114+
vf = await vfolder_factory(
115+
user=str(user_uuid),
116+
creator="user-test@test.local",
117+
quota_scope_id=str(QuotaScopeID(scope_type=QuotaScopeType.USER, scope_id=user_uuid)),
118+
)
119+
result = await user_registry.vfolder.get_quota(
120+
GetQuotaQuery(folder_host="local", id=vf["id"]),
121+
)
122+
assert isinstance(result, GetQuotaResponse)
123+
124+
@pytest.mark.xfail(strict=False, reason="Requires live storage-proxy")
125+
async def test_regular_user_cannot_update_others_quota(
126+
self,
127+
user_registry: BackendAIClientRegistry,
128+
target_vfolder: VFolderFixtureData,
129+
) -> None:
130+
"""Scenario: A regular user attempts to update the quota of a vfolder owned
131+
by the admin. The server should reject this with BackendAPIError because
132+
quota modification requires admin privileges or ownership of the vfolder."""
133+
with pytest.raises(BackendAPIError):
134+
await user_registry.vfolder.update_quota(
135+
UpdateQuotaReq(
136+
folder_host="local",
137+
id=target_vfolder["id"],
138+
input={"size_bytes": 999999},
139+
),
140+
)

0 commit comments

Comments
 (0)