@@ -141,12 +141,7 @@ def test_create_workload_builds_correct_manifest(self, mock_k8s_client):
141141 execd_image = "execd:latest" ,
142142 )
143143
144- assert result == {
145- "name" : "test-id" ,
146- "uid" : "test-uid" ,
147- "apiVersion" : "sandbox.opensandbox.io/v1alpha1" ,
148- "kind" : "BatchSandbox" ,
149- }
144+ assert result == {"name" : "test-id" , "uid" : "test-uid" , "apiVersion" : "sandbox.opensandbox.io/v1alpha1" , "kind" : "BatchSandbox" }
150145
151146 # Verify API call
152147 call_args = mock_k8s_client .create_custom_object .call_args
@@ -874,12 +869,7 @@ def test_create_workload_updates_informer_cache(self, mock_k8s_client):
874869 execd_image = "execd:latest" ,
875870 )
876871
877- assert result == {
878- "name" : "test-id" ,
879- "uid" : "test-uid" ,
880- "apiVersion" : "sandbox.opensandbox.io/v1alpha1" ,
881- "kind" : "BatchSandbox" ,
882- }
872+ assert result == {"name" : "test-id" , "uid" : "test-uid" , "apiVersion" : "sandbox.opensandbox.io/v1alpha1" , "kind" : "BatchSandbox" }
883873
884874 # ===== Workload List Tests =====
885875
@@ -1334,12 +1324,7 @@ def test_create_workload_poolref_ignores_image_spec(self, mock_k8s_client):
13341324 )
13351325
13361326 # Should succeed and return workload info
1337- assert result == {
1338- "name" : "sandbox-test-id" ,
1339- "uid" : "test-uid" ,
1340- "apiVersion" : "sandbox.opensandbox.io/v1alpha1" ,
1341- "kind" : "BatchSandbox" ,
1342- }
1327+ assert result == {"name" : "sandbox-test-id" , "uid" : "test-uid" , "apiVersion" : "sandbox.opensandbox.io/v1alpha1" , "kind" : "BatchSandbox" }
13431328
13441329 # Verify poolRef is used
13451330 body = mock_k8s_client .create_custom_object .call_args .kwargs ["body" ]
@@ -1371,12 +1356,7 @@ def test_create_workload_poolref_ignores_resource_limits(self, mock_k8s_client):
13711356 )
13721357
13731358 # Should succeed and return workload info
1374- assert result == {
1375- "name" : "sandbox-test-id" ,
1376- "uid" : "test-uid" ,
1377- "apiVersion" : "sandbox.opensandbox.io/v1alpha1" ,
1378- "kind" : "BatchSandbox" ,
1379- }
1359+ assert result == {"name" : "sandbox-test-id" , "uid" : "test-uid" , "apiVersion" : "sandbox.opensandbox.io/v1alpha1" , "kind" : "BatchSandbox" }
13801360
13811361 # Verify poolRef is used
13821362 body = mock_k8s_client .create_custom_object .call_args .kwargs ["body" ]
@@ -1406,12 +1386,7 @@ def test_create_workload_poolref_allows_entrypoint_and_env(self, mock_k8s_client
14061386 extensions = {"poolRef" : "my-pool" },
14071387 )
14081388
1409- assert result == {
1410- "name" : "sandbox-test-id" ,
1411- "uid" : "test-uid" ,
1412- "apiVersion" : "sandbox.opensandbox.io/v1alpha1" ,
1413- "kind" : "BatchSandbox" ,
1414- }
1389+ assert result == {"name" : "sandbox-test-id" , "uid" : "test-uid" , "apiVersion" : "sandbox.opensandbox.io/v1alpha1" , "kind" : "BatchSandbox" }
14151390
14161391 # Verify the call
14171392 body = mock_k8s_client .create_custom_object .call_args .kwargs ["body" ]
@@ -1645,9 +1620,7 @@ def test_create_workload_poolref_default_entrypoint_with_env_includes_task_templ
16451620 task_template = body ["spec" ]["taskTemplate" ]
16461621 assert task_template ["spec" ]["process" ]["env" ] == [{"name" : "VERSION" , "value" : "11" }]
16471622
1648- def test_create_workload_poolref_none_entrypoint_no_env_omits_task_template (
1649- self , mock_k8s_client
1650- ):
1623+ def test_create_workload_poolref_none_entrypoint_no_env_omits_task_template (self , mock_k8s_client ):
16511624 """When entrypoint is None and env is empty, taskTemplate is omitted.
16521625
16531626 SDK pool mode callers omit entrypoint entirely (None), expecting the pool's
@@ -2664,12 +2637,7 @@ def test_create_workload_with_pvc_volume(self, mock_k8s_client):
26642637 volumes = volumes ,
26652638 )
26662639
2667- assert result == {
2668- "name" : "test-id" ,
2669- "uid" : "test-uid" ,
2670- "apiVersion" : "sandbox.opensandbox.io/v1alpha1" ,
2671- "kind" : "BatchSandbox" ,
2672- }
2640+ assert result == {"name" : "test-id" , "uid" : "test-uid" , "apiVersion" : "sandbox.opensandbox.io/v1alpha1" , "kind" : "BatchSandbox" }
26732641
26742642 def test_create_workload_poolref_rejects_platform (self , mock_k8s_client ):
26752643 provider = BatchSandboxProvider (mock_k8s_client )
@@ -3009,3 +2977,67 @@ def test_apply_volumes_to_pod_spec_same_pvc_multiple_mounts(self, mock_k8s_clien
30092977 assert by_path ["/path/to/skills" ].get ("subPath" ) == "skill-hub/publish"
30102978 assert by_path ["/path/to/draft" ]["name" ] == "skills"
30112979 assert by_path ["/path/to/draft" ].get ("subPath" ) == "skill-hub/draft"
2980+
2981+ def test_apply_volumes_to_pod_spec_same_pvc_multiple_mounts_readwrite (self , mock_k8s_client ):
2982+ """Shared PVC mounts with read_only=False should keep source-level readOnly false."""
2983+ from opensandbox_server .api .schema import Volume , PVC
2984+
2985+ pod_spec = {
2986+ "containers" : [{"name" : "main" , "volumeMounts" : []}],
2987+ "volumes" : [],
2988+ }
2989+ volumes = [
2990+ Volume (
2991+ name = "skills" ,
2992+ pvc = PVC (claim_name = "oss-pvc-rw" ),
2993+ mount_path = "/path/to/skills" ,
2994+ sub_path = "skill-hub/publish" ,
2995+ read_only = False ,
2996+ ),
2997+ Volume (
2998+ name = "draft" ,
2999+ pvc = PVC (claim_name = "oss-pvc-rw" ),
3000+ mount_path = "/path/to/draft" ,
3001+ sub_path = "skill-hub/draft" ,
3002+ read_only = False ,
3003+ ),
3004+ ]
3005+
3006+ apply_volumes_to_pod_spec (pod_spec , volumes )
3007+
3008+ assert len (pod_spec ["volumes" ]) == 1
3009+ shared_volume = next ((v for v in pod_spec ["volumes" ] if v ["name" ] == "skills" ), None )
3010+ assert shared_volume is not None
3011+ assert shared_volume ["persistentVolumeClaim" ]["claimName" ] == "oss-pvc-rw"
3012+ assert shared_volume ["persistentVolumeClaim" ]["readOnly" ] is False
3013+
3014+ mounts = pod_spec ["containers" ][0 ]["volumeMounts" ]
3015+ assert len (mounts ) == 2
3016+ assert all (mount ["name" ] == "skills" for mount in mounts )
3017+ assert all (mount ["readOnly" ] is False for mount in mounts )
3018+
3019+ def test_apply_volumes_to_pod_spec_same_pvc_mixed_read_only_raises (self , mock_k8s_client ):
3020+ """Shared PVC mounts with mixed read_only values should fail fast."""
3021+ from opensandbox_server .api .schema import Volume , PVC
3022+
3023+ pod_spec = {
3024+ "containers" : [{"name" : "main" , "volumeMounts" : []}],
3025+ "volumes" : [],
3026+ }
3027+ volumes = [
3028+ Volume (
3029+ name = "skills" ,
3030+ pvc = PVC (claim_name = "oss-pvc-mixed" ),
3031+ mount_path = "/path/to/skills" ,
3032+ read_only = False ,
3033+ ),
3034+ Volume (
3035+ name = "draft" ,
3036+ pvc = PVC (claim_name = "oss-pvc-mixed" ),
3037+ mount_path = "/path/to/draft" ,
3038+ read_only = True ,
3039+ ),
3040+ ]
3041+
3042+ with pytest .raises (ValueError , match = "mixed read_only values" ):
3043+ apply_volumes_to_pod_spec (pod_spec , volumes )
0 commit comments