Skip to content

Commit 95fbb1e

Browse files
author
Yehudit Kerido
committed
test(ws): Notebooks 2.0 // Backend // Add tests
Signed-off-by: Yehudit Kerido <[email protected]>
1 parent 2c3e75e commit 95fbb1e

File tree

9 files changed

+808
-0
lines changed

9 files changed

+808
-0
lines changed

workspaces/backend/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ require (
6060
github.com/modern-go/reflect2 v1.0.2 // indirect
6161
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
6262
github.com/pkg/errors v0.9.1 // indirect
63+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
6364
github.com/prometheus/client_golang v1.19.1 // indirect
6465
github.com/prometheus/client_model v0.6.1 // indirect
6566
github.com/prometheus/common v0.55.0 // indirect
@@ -93,6 +94,7 @@ require (
9394
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
9495
google.golang.org/grpc v1.65.0 // indirect
9596
google.golang.org/protobuf v1.34.2 // indirect
97+
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
9698
gopkg.in/inf.v0 v0.9.1 // indirect
9799
gopkg.in/yaml.v2 v2.4.0 // indirect
98100
gopkg.in/yaml.v3 v3.0.1 // indirect
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
Copyright 2024.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package auth
18+
19+
import (
20+
"net/http"
21+
22+
. "github.com/onsi/ginkgo/v2"
23+
. "github.com/onsi/gomega"
24+
)
25+
26+
var _ = Describe("NewRequestAuthenticator", func() {
27+
const (
28+
userHeader = "X-User"
29+
groupsHeader = "X-Groups"
30+
userPrefix = "service-account:"
31+
)
32+
33+
type authTestInput struct {
34+
userHeaderValue string
35+
groupsHeaderValue string
36+
userPrefix string
37+
expectAuthenticated bool
38+
expectedUserName string
39+
expectedGroups []string
40+
}
41+
42+
runAuthTest := func(input authTestInput) {
43+
authn, err := NewRequestAuthenticator(userHeader, input.userPrefix, groupsHeader)
44+
Expect(err).NotTo(HaveOccurred())
45+
46+
req, _ := http.NewRequest("GET", "/", http.NoBody)
47+
if input.userHeaderValue != "" {
48+
req.Header.Set(userHeader, input.userHeaderValue)
49+
}
50+
if input.groupsHeaderValue != "" {
51+
req.Header.Set(groupsHeader, input.groupsHeaderValue)
52+
}
53+
54+
resp, ok, err := authn.AuthenticateRequest(req)
55+
Expect(err).NotTo(HaveOccurred())
56+
Expect(ok).To(Equal(input.expectAuthenticated))
57+
if input.expectAuthenticated {
58+
Expect(resp).NotTo(BeNil())
59+
Expect(resp.User.GetName()).To(Equal(input.expectedUserName))
60+
Expect(resp.User.GetGroups()).To(Equal(input.expectedGroups))
61+
} else {
62+
Expect(resp).To(BeNil())
63+
}
64+
}
65+
It("authenticates user without prefix", func() {
66+
runAuthTest(authTestInput{
67+
userHeaderValue: "test-user",
68+
groupsHeaderValue: "group-a,group-b",
69+
userPrefix: "",
70+
expectAuthenticated: true,
71+
expectedUserName: "test-user",
72+
expectedGroups: []string{"group-a,group-b"},
73+
})
74+
})
75+
76+
It("authenticates user and trims prefix", func() {
77+
runAuthTest(authTestInput{
78+
userHeaderValue: userPrefix + "test-user",
79+
groupsHeaderValue: "group-c",
80+
userPrefix: userPrefix,
81+
expectAuthenticated: true,
82+
expectedUserName: "test-user",
83+
expectedGroups: []string{"group-c"},
84+
})
85+
})
86+
87+
It("authenticates user when prefix is configured but not present", func() {
88+
runAuthTest(authTestInput{
89+
userHeaderValue: "another-user",
90+
groupsHeaderValue: "",
91+
userPrefix: userPrefix,
92+
expectAuthenticated: true,
93+
expectedUserName: "another-user",
94+
expectedGroups: []string{},
95+
})
96+
})
97+
98+
It("handles unauthenticated request", func() {
99+
runAuthTest(authTestInput{
100+
userHeaderValue: "",
101+
groupsHeaderValue: "some-group",
102+
userPrefix: userPrefix,
103+
expectAuthenticated: false,
104+
})
105+
})
106+
})
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
Copyright 2024.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package auth
18+
19+
import (
20+
. "github.com/onsi/ginkgo/v2"
21+
. "github.com/onsi/gomega"
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
"k8s.io/apimachinery/pkg/runtime"
24+
"k8s.io/apimachinery/pkg/runtime/schema"
25+
"k8s.io/apiserver/pkg/authentication/user"
26+
)
27+
28+
type mockObject struct {
29+
metav1.ObjectMeta
30+
metav1.TypeMeta
31+
}
32+
33+
func (m *mockObject) GetObjectKind() schema.ObjectKind { return &m.TypeMeta }
34+
35+
func (m *mockObject) DeepCopyObject() runtime.Object {
36+
return &mockObject{
37+
ObjectMeta: *m.ObjectMeta.DeepCopy(),
38+
TypeMeta: m.TypeMeta,
39+
}
40+
}
41+
42+
var _ runtime.Object = &mockObject{}
43+
var _ = Describe("NewResourcePolicy", func() {
44+
It("creates policy for a namespaced resource", func() {
45+
mock := &mockObject{}
46+
mock.SetName("my-deployment")
47+
mock.SetNamespace("my-namespace")
48+
mock.SetGroupVersionKind(schema.GroupVersionKind{
49+
Group: "apps",
50+
Version: "v1",
51+
Kind: "Deployment",
52+
})
53+
54+
policy := NewResourcePolicy(ResourceVerbGet, mock)
55+
56+
Expect(policy).NotTo(BeNil())
57+
Expect(policy.Verb).To(Equal(ResourceVerbGet))
58+
Expect(policy.Group).To(Equal("apps"))
59+
Expect(policy.Version).To(Equal("v1"))
60+
Expect(policy.Kind).To(Equal("Deployment"))
61+
Expect(policy.Namespace).To(Equal("my-namespace"))
62+
Expect(policy.Name).To(Equal("my-deployment"))
63+
})
64+
65+
It("creates policy for a cluster-scoped resource", func() {
66+
mock := &mockObject{}
67+
mock.SetName("my-cluster-role")
68+
mock.SetGroupVersionKind(schema.GroupVersionKind{
69+
Group: "rbac.authorization.k8s.io",
70+
Version: "v1",
71+
Kind: "ClusterRole",
72+
})
73+
74+
policy := NewResourcePolicy(ResourceVerbDelete, mock)
75+
76+
Expect(policy).NotTo(BeNil())
77+
Expect(policy.Verb).To(Equal(ResourceVerbDelete))
78+
Expect(policy.Group).To(Equal("rbac.authorization.k8s.io"))
79+
Expect(policy.Kind).To(Equal("ClusterRole"))
80+
Expect(policy.Name).To(Equal("my-cluster-role"))
81+
Expect(policy.Namespace).To(BeEmpty())
82+
})
83+
})
84+
85+
var _ = Describe("AttributesFor", func() {
86+
userInfo := &user.DefaultInfo{
87+
Name: "test-user",
88+
Groups: []string{"group-a", "system:authenticated"},
89+
}
90+
91+
It("creates attributes for a specific resource", func() {
92+
policy := &ResourcePolicy{
93+
Verb: ResourceVerbUpdate,
94+
Group: "kubeflow.org",
95+
Version: "v1beta1",
96+
Kind: "Workspace",
97+
Namespace: "user-namespace",
98+
Name: "my-workspace",
99+
}
100+
101+
attrs := policy.AttributesFor(userInfo)
102+
Expect(attrs).NotTo(BeNil())
103+
Expect(attrs.GetUser()).To(Equal(userInfo))
104+
Expect(attrs.GetVerb()).To(Equal("update"))
105+
Expect(attrs.GetNamespace()).To(Equal("user-namespace"))
106+
Expect(attrs.GetAPIGroup()).To(Equal("kubeflow.org"))
107+
Expect(attrs.GetAPIVersion()).To(Equal("v1beta1"))
108+
Expect(attrs.GetResource()).To(Equal("Workspace"))
109+
Expect(attrs.GetName()).To(Equal("my-workspace"))
110+
Expect(attrs.IsResourceRequest()).To(BeTrue())
111+
})
112+
113+
It("creates attributes for a collection of resources", func() {
114+
policy := &ResourcePolicy{
115+
Verb: ResourceVerbList,
116+
Group: "kubeflow.org",
117+
Version: "v1beta1",
118+
Kind: "Workspace",
119+
Namespace: "user-namespace",
120+
Name: "",
121+
}
122+
123+
attrs := policy.AttributesFor(userInfo)
124+
Expect(attrs).NotTo(BeNil())
125+
Expect(attrs.GetUser()).To(Equal(userInfo))
126+
Expect(attrs.GetVerb()).To(Equal("list"))
127+
Expect(attrs.GetNamespace()).To(Equal("user-namespace"))
128+
Expect(attrs.GetName()).To(BeEmpty())
129+
Expect(attrs.IsResourceRequest()).To(BeTrue())
130+
})
131+
})
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Copyright 2024.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package auth
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/ginkgo/v2"
23+
. "github.com/onsi/gomega"
24+
)
25+
26+
func TestAuth(t *testing.T) {
27+
RegisterFailHandler(Fail)
28+
RunSpecs(t, "Auth Suite")
29+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
Copyright 2024.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package helper
18+
19+
import (
20+
. "github.com/onsi/ginkgo/v2"
21+
. "github.com/onsi/gomega"
22+
corev1 "k8s.io/api/core/v1"
23+
"k8s.io/apimachinery/pkg/runtime/schema"
24+
)
25+
26+
var _ = Describe("Helper functions", func() {
27+
Describe("BuildScheme", func() {
28+
It("should return a scheme that recognizes Pod and Workspace types", func() {
29+
scheme, err := BuildScheme()
30+
Expect(err).NotTo(HaveOccurred())
31+
Expect(scheme).NotTo(BeNil())
32+
33+
podGVK := corev1.SchemeGroupVersion.WithKind("Pod")
34+
Expect(scheme.Recognizes(podGVK)).To(BeTrue())
35+
36+
workspaceGVK := schema.GroupVersionKind{
37+
Group: "kubeflow.org",
38+
Version: "v1beta1",
39+
Kind: "Workspace",
40+
}
41+
Expect(scheme.Recognizes(workspaceGVK)).To(BeTrue())
42+
})
43+
})
44+
})
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Copyright 2024.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package helper
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/ginkgo/v2"
23+
. "github.com/onsi/gomega"
24+
)
25+
26+
func TestHelper(t *testing.T) {
27+
RegisterFailHandler(Fail)
28+
RunSpecs(t, "Helper Suite")
29+
}

0 commit comments

Comments
 (0)