@@ -17,6 +17,7 @@ limitations under the License.
1717package  api
1818
1919import  (
20+ 	"bytes" 
2021	"encoding/json" 
2122	"fmt" 
2223	"io" 
@@ -195,8 +196,6 @@ var _ = Describe("WorkspaceKinds Handler", func() {
195196		})
196197	})
197198
198- 	// NOTE: these tests assume a specific state of the cluster, so cannot be run in parallel with other tests. 
199- 	//       therefore, we run them using the `Serial` Ginkgo decorators. 
200199	Context ("with no existing WorkspaceKinds" , Serial , func () {
201200
202201		It ("should return an empty list of WorkspaceKinds" , func () {
@@ -254,4 +253,192 @@ var _ = Describe("WorkspaceKinds Handler", func() {
254253			Expect (rs .StatusCode ).To (Equal (http .StatusNotFound ), descUnexpectedHTTPStatus , rr .Body .String ())
255254		})
256255	})
256+ 
257+ 	// NOTE: these tests create and delete resources on the cluster, so cannot be run in parallel. 
258+ 	//       therefore, we run them using the `Serial` Ginkgo decorator. 
259+ 	Context ("when creating a WorkspaceKind" , Serial , func () {
260+ 
261+ 		var  newWorkspaceKindName  =  "wsk-create-test" 
262+ 		var  validYAML  []byte 
263+ 
264+ 		BeforeEach (func () {
265+ 			validYAML  =  []byte (fmt .Sprintf (` 
266+ apiVersion: workspaces.kubeflow.org/v1beta1 
267+ kind: WorkspaceKind 
268+ metadata: 
269+   name: %s 
270+ spec: 
271+   spawner: 
272+     displayName: "Test Jupyter Environment" 
273+     description: "A valid description for testing." 
274+     icon: 
275+       url: "https://example.com/icon.png" 
276+     logo: 
277+       url: "https://example.com/logo.svg" 
278+   podTemplate: 
279+     options: 
280+       imageConfig: 
281+         spawner: 
282+           default: "default-image" 
283+         values: 
284+         - id: "default-image" 
285+           name: "Jupyter Scipy" 
286+           path: "kubeflownotebooks/jupyter-scipy:v1.9.0" 
287+           spawner: 
288+             displayName: "Jupyter with SciPy v1.9.0" 
289+           spec: 
290+             image: "kubeflownotebooks/jupyter-scipy:v1.9.0" 
291+             ports: 
292+             - id: "notebook-port" 
293+               displayName: "Notebook Port" 
294+               port: 8888 
295+               protocol: "HTTP" 
296+       podConfig: 
297+         spawner: 
298+           default: "default-pod-config" 
299+         values: 
300+         - id: "default-pod-config" 
301+           name: "Default Resources" 
302+           spawner: 
303+             displayName: "Small CPU/RAM" 
304+           resources: 
305+             requests: 
306+               cpu: "500m" 
307+               memory: "1Gi" 
308+             limits: 
309+               cpu: "1" 
310+               memory: "2Gi" 
311+     volumeMounts: 
312+       home: "/home/jovyan" 
313+ ` , newWorkspaceKindName ))
314+ 		})
315+ 
316+ 		AfterEach (func () {
317+ 			By ("cleaning up the created WorkspaceKind" )
318+ 			wsk  :=  & kubefloworgv1beta1.WorkspaceKind {
319+ 				ObjectMeta : metav1.ObjectMeta {
320+ 					Name : newWorkspaceKindName ,
321+ 				},
322+ 			}
323+ 			_  =  k8sClient .Delete (ctx , wsk )
324+ 		})
325+ 
326+ 		It ("should succeed when creating a new WorkspaceKind with valid YAML" , func () {
327+ 			req , err  :=  http .NewRequest (http .MethodPost , AllWorkspaceKindsPath , bytes .NewReader (validYAML ))
328+ 			Expect (err ).NotTo (HaveOccurred ())
329+ 			req .Header .Set ("Content-Type" , ContentTypeYAMLManifest )
330+ 			req .Header .Set (userIdHeader , adminUser )
331+ 
332+ 			rr  :=  httptest .NewRecorder ()
333+ 			a .CreateWorkspaceKindHandler (rr , req , httprouter.Params {})
334+ 			rs  :=  rr .Result ()
335+ 			defer  rs .Body .Close ()
336+ 
337+ 			Expect (rs .StatusCode ).To (Equal (http .StatusCreated ), "Body: %s" , rr .Body .String ())
338+ 
339+ 			By ("verifying the resource was created in the cluster" )
340+ 			createdWsk  :=  & kubefloworgv1beta1.WorkspaceKind {}
341+ 			err  =  k8sClient .Get (ctx , types.NamespacedName {Name : newWorkspaceKindName }, createdWsk )
342+ 			Expect (err ).NotTo (HaveOccurred ())
343+ 		})
344+ 
345+ 		It ("should fail with 400 Bad Request if the YAML is missing a required name" , func () {
346+ 			missingNameYAML  :=  []byte (` 
347+ apiVersion: workspaces.kubeflow.org/v1beta1 
348+ kind: WorkspaceKind 
349+ metadata: {} 
350+ spec: 
351+   spawner: 
352+     displayName: "This will fail"` )
353+ 			req , _  :=  http .NewRequest (http .MethodPost , AllWorkspaceKindsPath , bytes .NewReader (missingNameYAML ))
354+ 			req .Header .Set ("Content-Type" , ContentTypeYAMLManifest )
355+ 			req .Header .Set (userIdHeader , adminUser )
356+ 
357+ 			rr  :=  httptest .NewRecorder ()
358+ 			a .CreateWorkspaceKindHandler (rr , req , httprouter.Params {})
359+ 
360+ 			Expect (rr .Code ).To (Equal (http .StatusBadRequest ))
361+ 			Expect (rr .Body .String ()).To (ContainSubstring ("'.metadata.name' is a required field" ))
362+ 		})
363+ 
364+ 		It ("should return a 409 Conflict when creating a WorkspaceKind that already exists" , func () {
365+ 			By ("creating the resource once successfully" )
366+ 			req1 , _  :=  http .NewRequest (http .MethodPost , AllWorkspaceKindsPath , bytes .NewReader (validYAML ))
367+ 			req1 .Header .Set ("Content-Type" , ContentTypeYAMLManifest )
368+ 			req1 .Header .Set (userIdHeader , adminUser )
369+ 			rr1  :=  httptest .NewRecorder ()
370+ 			a .CreateWorkspaceKindHandler (rr1 , req1 , httprouter.Params {})
371+ 			Expect (rr1 .Code ).To (Equal (http .StatusCreated ))
372+ 
373+ 			By ("attempting to create the exact same resource a second time" )
374+ 			req2 , _  :=  http .NewRequest (http .MethodPost , AllWorkspaceKindsPath , bytes .NewReader (validYAML ))
375+ 			req2 .Header .Set ("Content-Type" , ContentTypeYAMLManifest )
376+ 			req2 .Header .Set (userIdHeader , adminUser )
377+ 			rr2  :=  httptest .NewRecorder ()
378+ 			a .CreateWorkspaceKindHandler (rr2 , req2 , httprouter.Params {})
379+ 
380+ 			Expect (rr2 .Code ).To (Equal (http .StatusConflict ))
381+ 		})
382+ 
383+ 		It ("should fail with 400 Bad Request when the YAML has the wrong kind" , func () {
384+ 			wrongKindYAML  :=  []byte (`apiVersion: v1 
385+ kind: Pod 
386+ metadata: 
387+   name: i-am-the-wrong-kind` )
388+ 			req , _  :=  http .NewRequest (http .MethodPost , AllWorkspaceKindsPath , bytes .NewReader (wrongKindYAML ))
389+ 			req .Header .Set ("Content-Type" , ContentTypeYAMLManifest )
390+ 			req .Header .Set (userIdHeader , adminUser )
391+ 
392+ 			rr  :=  httptest .NewRecorder ()
393+ 			a .CreateWorkspaceKindHandler (rr , req , httprouter.Params {})
394+ 
395+ 			Expect (rr .Code ).To (Equal (http .StatusBadRequest ))
396+ 			Expect (rr .Body .String ()).To (ContainSubstring ("invalid kind in YAML: expected 'WorkspaceKind', got 'Pod'" ))
397+ 		})
398+ 
399+ 		It ("should fail when the body is not valid YAML" , func () {
400+ 			notYAML  :=  []byte (`this is not yaml {` )
401+ 			req , _  :=  http .NewRequest (http .MethodPost , AllWorkspaceKindsPath , bytes .NewReader (notYAML ))
402+ 			req .Header .Set ("Content-Type" , ContentTypeYAMLManifest )
403+ 			req .Header .Set (userIdHeader , adminUser )
404+ 
405+ 			rr  :=  httptest .NewRecorder ()
406+ 			a .CreateWorkspaceKindHandler (rr , req , httprouter.Params {})
407+ 
408+ 			By ("verifying the handler returns a 400 Bad Request with a valid error envelope" )
409+ 			Expect (rr .Code ).To (Equal (http .StatusBadRequest ))
410+ 			var  response  ErrorEnvelope 
411+ 			err  :=  json .Unmarshal (rr .Body .Bytes (), & response )
412+ 			Expect (err ).NotTo (HaveOccurred (), "The error response should be valid JSON" )
413+ 			Expect (response .Error .Message ).To (Equal ("request body is not a valid YAML manifest" ))
414+ 		})
415+ 		It ("should fail with 400 Bad Request for an empty YAML object" , func () {
416+ 			By ("defining an empty YAML object as the payload" )
417+ 			invalidYAML  :=  []byte ("{}" )
418+ 
419+ 			By ("creating the HTTP request" )
420+ 			req , err  :=  http .NewRequest (http .MethodPost , AllWorkspaceKindsPath , bytes .NewReader (invalidYAML ))
421+ 			Expect (err ).NotTo (HaveOccurred ())
422+ 			req .Header .Set ("Content-Type" , ContentTypeYAMLManifest )
423+ 			req .Header .Set (userIdHeader , adminUser )
424+ 
425+ 			By ("executing the CreateWorkspaceKindHandler" )
426+ 			rr  :=  httptest .NewRecorder ()
427+ 			a .CreateWorkspaceKindHandler (rr , req , httprouter.Params {})
428+ 			rs  :=  rr .Result ()
429+ 			defer  rs .Body .Close ()
430+ 
431+ 			By ("verifying the handler returns a 400 Bad Request" )
432+ 			// First, check the status code 
433+ 			Expect (rs .StatusCode ).To (Equal (http .StatusBadRequest ))
434+ 
435+ 			By ("verifying the error message in the response body" )
436+ 			// Second, read the body from the response stream 
437+ 			body , err  :=  io .ReadAll (rs .Body )
438+ 			Expect (err ).NotTo (HaveOccurred ()) // Ensure reading the body didn't cause an error 
439+ 
440+ 			// Finally, assert on the content of the body 
441+ 			Expect (string (body )).To (ContainSubstring ("invalid kind in YAML: expected 'WorkspaceKind', got ''" ))
442+ 		})
443+ 	})
257444})
0 commit comments