Skip to content

Commit e376283

Browse files
committed
add unit test for provisioner pvc volumes
1 parent 4de69fd commit e376283

1 file changed

Lines changed: 178 additions & 0 deletions

File tree

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
"""Regression tests for provisioner PVC volume support."""
2+
3+
from __future__ import annotations
4+
5+
import importlib.util
6+
from pathlib import Path
7+
8+
import pytest
9+
10+
11+
@pytest.fixture()
12+
def provisioner_module():
13+
"""Load docker/provisioner/app.py as an importable test module."""
14+
repo_root = Path(__file__).resolve().parents[2]
15+
module_path = repo_root / "docker" / "provisioner" / "app.py"
16+
spec = importlib.util.spec_from_file_location("provisioner_app_test", module_path)
17+
assert spec is not None
18+
assert spec.loader is not None
19+
module = importlib.util.module_from_spec(spec)
20+
spec.loader.exec_module(module)
21+
return module
22+
23+
24+
# ── _build_volumes ─────────────────────────────────────────────────────
25+
26+
27+
class TestBuildVolumes:
28+
"""Tests for _build_volumes: PVC vs hostPath selection."""
29+
30+
def test_default_uses_hostpath_for_skills(self, provisioner_module):
31+
"""When SKILLS_PVC_NAME is empty, skills volume should use hostPath."""
32+
provisioner_module.SKILLS_PVC_NAME = ""
33+
volumes = provisioner_module._build_volumes("thread-1")
34+
skills_vol = volumes[0]
35+
assert skills_vol.host_path is not None
36+
assert skills_vol.host_path.path == provisioner_module.SKILLS_HOST_PATH
37+
assert skills_vol.host_path.type == "Directory"
38+
assert skills_vol.persistent_volume_claim is None
39+
40+
def test_default_uses_hostpath_for_userdata(self, provisioner_module):
41+
"""When USERDATA_PVC_NAME is empty, user-data volume should use hostPath."""
42+
provisioner_module.USERDATA_PVC_NAME = ""
43+
volumes = provisioner_module._build_volumes("thread-1")
44+
userdata_vol = volumes[1]
45+
assert userdata_vol.host_path is not None
46+
assert userdata_vol.persistent_volume_claim is None
47+
48+
def test_hostpath_userdata_includes_thread_id(self, provisioner_module):
49+
"""hostPath user-data path should include thread_id."""
50+
provisioner_module.USERDATA_PVC_NAME = ""
51+
volumes = provisioner_module._build_volumes("my-thread-42")
52+
userdata_vol = volumes[1]
53+
path = userdata_vol.host_path.path
54+
assert "my-thread-42" in path
55+
assert path.endswith("user-data")
56+
assert userdata_vol.host_path.type == "DirectoryOrCreate"
57+
58+
def test_skills_pvc_overrides_hostpath(self, provisioner_module):
59+
"""When SKILLS_PVC_NAME is set, skills volume should use PVC."""
60+
provisioner_module.SKILLS_PVC_NAME = "my-skills-pvc"
61+
volumes = provisioner_module._build_volumes("thread-1")
62+
skills_vol = volumes[0]
63+
assert skills_vol.persistent_volume_claim is not None
64+
assert skills_vol.persistent_volume_claim.claim_name == "my-skills-pvc"
65+
assert skills_vol.persistent_volume_claim.read_only is True
66+
assert skills_vol.host_path is None
67+
68+
def test_userdata_pvc_overrides_hostpath(self, provisioner_module):
69+
"""When USERDATA_PVC_NAME is set, user-data volume should use PVC."""
70+
provisioner_module.USERDATA_PVC_NAME = "my-userdata-pvc"
71+
volumes = provisioner_module._build_volumes("thread-1")
72+
userdata_vol = volumes[1]
73+
assert userdata_vol.persistent_volume_claim is not None
74+
assert userdata_vol.persistent_volume_claim.claim_name == "my-userdata-pvc"
75+
assert userdata_vol.host_path is None
76+
77+
def test_both_pvc_set(self, provisioner_module):
78+
"""When both PVC names are set, both volumes use PVC."""
79+
provisioner_module.SKILLS_PVC_NAME = "skills-pvc"
80+
provisioner_module.USERDATA_PVC_NAME = "userdata-pvc"
81+
volumes = provisioner_module._build_volumes("thread-1")
82+
assert volumes[0].persistent_volume_claim is not None
83+
assert volumes[1].persistent_volume_claim is not None
84+
85+
def test_returns_two_volumes(self, provisioner_module):
86+
"""Should always return exactly two volumes."""
87+
provisioner_module.SKILLS_PVC_NAME = ""
88+
provisioner_module.USERDATA_PVC_NAME = ""
89+
assert len(provisioner_module._build_volumes("t")) == 2
90+
91+
provisioner_module.SKILLS_PVC_NAME = "a"
92+
provisioner_module.USERDATA_PVC_NAME = "b"
93+
assert len(provisioner_module._build_volumes("t")) == 2
94+
95+
def test_volume_names_are_stable(self, provisioner_module):
96+
"""Volume names must stay 'skills' and 'user-data'."""
97+
volumes = provisioner_module._build_volumes("thread-1")
98+
assert volumes[0].name == "skills"
99+
assert volumes[1].name == "user-data"
100+
101+
102+
# ── _build_volume_mounts ───────────────────────────────────────────────
103+
104+
105+
class TestBuildVolumeMounts:
106+
"""Tests for _build_volume_mounts: mount paths and subPath behavior."""
107+
108+
def test_default_no_subpath(self, provisioner_module):
109+
"""hostPath mode should not set sub_path on user-data mount."""
110+
provisioner_module.USERDATA_PVC_NAME = ""
111+
mounts = provisioner_module._build_volume_mounts("thread-1")
112+
userdata_mount = mounts[1]
113+
assert userdata_mount.sub_path is None
114+
115+
def test_pvc_sets_subpath(self, provisioner_module):
116+
"""PVC mode should set sub_path to threads/{thread_id}/user-data."""
117+
provisioner_module.USERDATA_PVC_NAME = "my-pvc"
118+
mounts = provisioner_module._build_volume_mounts("thread-42")
119+
userdata_mount = mounts[1]
120+
assert userdata_mount.sub_path == "threads/thread-42/user-data"
121+
122+
def test_skills_mount_read_only(self, provisioner_module):
123+
"""Skills mount should always be read-only."""
124+
mounts = provisioner_module._build_volume_mounts("thread-1")
125+
assert mounts[0].read_only is True
126+
127+
def test_userdata_mount_read_write(self, provisioner_module):
128+
"""User-data mount should always be read-write."""
129+
mounts = provisioner_module._build_volume_mounts("thread-1")
130+
assert mounts[1].read_only is False
131+
132+
def test_mount_paths_are_stable(self, provisioner_module):
133+
"""Mount paths must stay /mnt/skills and /mnt/user-data."""
134+
mounts = provisioner_module._build_volume_mounts("thread-1")
135+
assert mounts[0].mount_path == "/mnt/skills"
136+
assert mounts[1].mount_path == "/mnt/user-data"
137+
138+
def test_mount_names_match_volumes(self, provisioner_module):
139+
"""Mount names should match the volume names."""
140+
mounts = provisioner_module._build_volume_mounts("thread-1")
141+
assert mounts[0].name == "skills"
142+
assert mounts[1].name == "user-data"
143+
144+
def test_returns_two_mounts(self, provisioner_module):
145+
"""Should always return exactly two mounts."""
146+
assert len(provisioner_module._build_volume_mounts("t")) == 2
147+
148+
149+
# ── _build_pod integration ─────────────────────────────────────────────
150+
151+
152+
class TestBuildPodVolumes:
153+
"""Integration: _build_pod should wire volumes and mounts correctly."""
154+
155+
def test_pod_spec_has_volumes(self, provisioner_module):
156+
"""Pod spec should contain exactly 2 volumes."""
157+
provisioner_module.SKILLS_PVC_NAME = ""
158+
provisioner_module.USERDATA_PVC_NAME = ""
159+
pod = provisioner_module._build_pod("sandbox-1", "thread-1")
160+
assert len(pod.spec.volumes) == 2
161+
162+
def test_pod_spec_has_volume_mounts(self, provisioner_module):
163+
"""Container should have exactly 2 volume mounts."""
164+
provisioner_module.SKILLS_PVC_NAME = ""
165+
provisioner_module.USERDATA_PVC_NAME = ""
166+
pod = provisioner_module._build_pod("sandbox-1", "thread-1")
167+
assert len(pod.spec.containers[0].volume_mounts) == 2
168+
169+
def test_pod_pvc_mode(self, provisioner_module):
170+
"""Pod should use PVC volumes when PVC names are configured."""
171+
provisioner_module.SKILLS_PVC_NAME = "skills-pvc"
172+
provisioner_module.USERDATA_PVC_NAME = "userdata-pvc"
173+
pod = provisioner_module._build_pod("sandbox-1", "thread-1")
174+
assert pod.spec.volumes[0].persistent_volume_claim is not None
175+
assert pod.spec.volumes[1].persistent_volume_claim is not None
176+
# subPath should be set on user-data mount
177+
userdata_mount = pod.spec.containers[0].volume_mounts[1]
178+
assert userdata_mount.sub_path == "threads/thread-1/user-data"

0 commit comments

Comments
 (0)