Skip to content

Commit 43744f2

Browse files
authored
test: extract mock-server for reutilization (#247)
Signed-off-by: Marc Nuri <[email protected]>
1 parent 9ec5c82 commit 43744f2

File tree

5 files changed

+47
-37
lines changed

5 files changed

+47
-37
lines changed

pkg/mcp/mock_server_test.go renamed to internal/test/mock_server.go

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
package mcp
1+
package test
22

33
import (
44
"encoding/json"
55
"errors"
66
"io"
7+
"net/http"
8+
"net/http/httptest"
9+
710
v1 "k8s.io/api/core/v1"
811
apierrors "k8s.io/apimachinery/pkg/api/errors"
912
"k8s.io/apimachinery/pkg/runtime"
1013
"k8s.io/apimachinery/pkg/runtime/serializer"
1114
"k8s.io/apimachinery/pkg/util/httpstream"
1215
"k8s.io/apimachinery/pkg/util/httpstream/spdy"
1316
"k8s.io/client-go/rest"
14-
"net/http"
15-
"net/http/httptest"
1617
)
1718

1819
type MockServer struct {
@@ -51,7 +52,11 @@ func (m *MockServer) Handle(handler http.Handler) {
5152
m.restHandlers = append(m.restHandlers, handler.ServeHTTP)
5253
}
5354

54-
func writeObject(w http.ResponseWriter, obj runtime.Object) {
55+
func (m *MockServer) Config() *rest.Config {
56+
return m.config
57+
}
58+
59+
func WriteObject(w http.ResponseWriter, obj runtime.Object) {
5560
w.Header().Set("Content-Type", runtime.ContentTypeJSON)
5661
if err := json.NewEncoder(w).Encode(obj); err != nil {
5762
http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -63,11 +68,11 @@ type streamAndReply struct {
6368
replySent <-chan struct{}
6469
}
6570

66-
type streamContext struct {
67-
conn io.Closer
68-
stdinStream io.ReadCloser
69-
stdoutStream io.WriteCloser
70-
stderrStream io.WriteCloser
71+
type StreamContext struct {
72+
Closer io.Closer
73+
StdinStream io.ReadCloser
74+
StdoutStream io.WriteCloser
75+
StderrStream io.WriteCloser
7176
writeStatus func(status *apierrors.StatusError) error
7277
}
7378

@@ -87,20 +92,20 @@ func v4WriteStatusFunc(stream io.Writer) func(status *apierrors.StatusError) err
8792
return err
8893
}
8994
}
90-
func createHTTPStreams(w http.ResponseWriter, req *http.Request, opts *StreamOptions) (*streamContext, error) {
95+
func CreateHTTPStreams(w http.ResponseWriter, req *http.Request, opts *StreamOptions) (*StreamContext, error) {
9196
_, err := httpstream.Handshake(req, w, []string{"v4.channel.k8s.io"})
9297
if err != nil {
9398
return nil, err
9499
}
95100

96101
upgrader := spdy.NewResponseUpgrader()
97102
streamCh := make(chan streamAndReply)
98-
conn := upgrader.UpgradeResponse(w, req, func(stream httpstream.Stream, replySent <-chan struct{}) error {
103+
connection := upgrader.UpgradeResponse(w, req, func(stream httpstream.Stream, replySent <-chan struct{}) error {
99104
streamCh <- streamAndReply{Stream: stream, replySent: replySent}
100105
return nil
101106
})
102-
ctx := &streamContext{
103-
conn: conn,
107+
ctx := &StreamContext{
108+
Closer: connection,
104109
}
105110

106111
// wait for stream
@@ -128,13 +133,13 @@ WaitForStreams:
128133
ctx.writeStatus = v4WriteStatusFunc(stream)
129134
case v1.StreamTypeStdout:
130135
replyChan <- struct{}{}
131-
ctx.stdoutStream = stream
136+
ctx.StdoutStream = stream
132137
case v1.StreamTypeStdin:
133138
replyChan <- struct{}{}
134-
ctx.stdinStream = stream
139+
ctx.StdinStream = stream
135140
case v1.StreamTypeStderr:
136141
replyChan <- struct{}{}
137-
ctx.stderrStream = stream
142+
ctx.StderrStream = stream
138143
default:
139144
// add other stream ...
140145
return nil, errors.New("unimplemented stream type")

pkg/mcp/mcp_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"testing"
1010
"time"
1111

12+
"github.com/containers/kubernetes-mcp-server/internal/test"
1213
"github.com/mark3labs/mcp-go/client"
1314
"github.com/mark3labs/mcp-go/mcp"
1415
)
@@ -48,10 +49,10 @@ func TestWatchKubeConfig(t *testing.T) {
4849
}
4950

5051
func TestSseHeaders(t *testing.T) {
51-
mockServer := NewMockServer()
52+
mockServer := test.NewMockServer()
5253
defer mockServer.Close()
5354
before := func(c *mcpContext) {
54-
c.withKubeConfig(mockServer.config)
55+
c.withKubeConfig(mockServer.Config())
5556
c.clientOptions = append(c.clientOptions, client.WithHeaders(map[string]string{"kubernetes-authorization": "Bearer a-token-from-mcp-client"}))
5657
}
5758
pathHeaders := make(map[string]http.Header, 0)

pkg/mcp/pods_exec_test.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,29 @@ package mcp
22

33
import (
44
"bytes"
5-
"github.com/containers/kubernetes-mcp-server/pkg/config"
6-
"github.com/mark3labs/mcp-go/mcp"
75
"io"
8-
v1 "k8s.io/api/core/v1"
9-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
106
"net/http"
117
"strings"
128
"testing"
9+
10+
"github.com/containers/kubernetes-mcp-server/internal/test"
11+
"github.com/containers/kubernetes-mcp-server/pkg/config"
12+
"github.com/mark3labs/mcp-go/mcp"
13+
v1 "k8s.io/api/core/v1"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1315
)
1416

1517
func TestPodsExec(t *testing.T) {
1618
testCase(t, func(c *mcpContext) {
17-
mockServer := NewMockServer()
19+
mockServer := test.NewMockServer()
1820
defer mockServer.Close()
19-
c.withKubeConfig(mockServer.config)
21+
c.withKubeConfig(mockServer.Config())
2022
mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
2123
if req.URL.Path != "/api/v1/namespaces/default/pods/pod-to-exec/exec" {
2224
return
2325
}
2426
var stdin, stdout bytes.Buffer
25-
ctx, err := createHTTPStreams(w, req, &StreamOptions{
27+
ctx, err := test.CreateHTTPStreams(w, req, &test.StreamOptions{
2628
Stdin: &stdin,
2729
Stdout: &stdout,
2830
})
@@ -31,15 +33,15 @@ func TestPodsExec(t *testing.T) {
3133
_, _ = w.Write([]byte(err.Error()))
3234
return
3335
}
34-
defer func(conn io.Closer) { _ = conn.Close() }(ctx.conn)
35-
_, _ = io.WriteString(ctx.stdoutStream, "command:"+strings.Join(req.URL.Query()["command"], " ")+"\n")
36-
_, _ = io.WriteString(ctx.stdoutStream, "container:"+strings.Join(req.URL.Query()["container"], " ")+"\n")
36+
defer func(conn io.Closer) { _ = conn.Close() }(ctx.Closer)
37+
_, _ = io.WriteString(ctx.StdoutStream, "command:"+strings.Join(req.URL.Query()["command"], " ")+"\n")
38+
_, _ = io.WriteString(ctx.StdoutStream, "container:"+strings.Join(req.URL.Query()["container"], " ")+"\n")
3739
}))
3840
mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
3941
if req.URL.Path != "/api/v1/namespaces/default/pods/pod-to-exec" {
4042
return
4143
}
42-
writeObject(w, &v1.Pod{
44+
test.WriteObject(w, &v1.Pod{
4345
ObjectMeta: metav1.ObjectMeta{
4446
Namespace: "default",
4547
Name: "pod-to-exec",

pkg/mcp/pods_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package mcp
22

33
import (
4-
"github.com/containers/kubernetes-mcp-server/pkg/config"
5-
"github.com/containers/kubernetes-mcp-server/pkg/output"
64
"regexp"
75
"strings"
86
"testing"
97

8+
"github.com/containers/kubernetes-mcp-server/pkg/config"
9+
"github.com/containers/kubernetes-mcp-server/pkg/output"
10+
1011
"github.com/mark3labs/mcp-go/mcp"
1112
corev1 "k8s.io/api/core/v1"
1213
rbacv1 "k8s.io/api/rbac/v1"

pkg/mcp/pods_top_test.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ import (
55
"regexp"
66
"testing"
77

8+
"github.com/containers/kubernetes-mcp-server/internal/test"
89
"github.com/mark3labs/mcp-go/mcp"
910

1011
"github.com/containers/kubernetes-mcp-server/pkg/config"
1112
)
1213

1314
func TestPodsTopMetricsUnavailable(t *testing.T) {
1415
testCase(t, func(c *mcpContext) {
15-
mockServer := NewMockServer()
16+
mockServer := test.NewMockServer()
1617
defer mockServer.Close()
17-
c.withKubeConfig(mockServer.config)
18+
c.withKubeConfig(mockServer.Config())
1819
mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
1920
w.Header().Set("Content-Type", "application/json")
2021
// Request Performed by DiscoveryClient to Kube API (Get API Groups legacy -core-)
@@ -45,9 +46,9 @@ func TestPodsTopMetricsUnavailable(t *testing.T) {
4546

4647
func TestPodsTopMetricsAvailable(t *testing.T) {
4748
testCase(t, func(c *mcpContext) {
48-
mockServer := NewMockServer()
49+
mockServer := test.NewMockServer()
4950
defer mockServer.Close()
50-
c.withKubeConfig(mockServer.config)
51+
c.withKubeConfig(mockServer.Config())
5152
mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
5253
println("Request received:", req.Method, req.URL.Path) // TODO: REMOVE LINE
5354
w.Header().Set("Content-Type", "application/json")
@@ -211,9 +212,9 @@ func TestPodsTopMetricsAvailable(t *testing.T) {
211212
func TestPodsTopDenied(t *testing.T) {
212213
deniedResourcesServer := &config.StaticConfig{DeniedResources: []config.GroupVersionKind{{Group: "metrics.k8s.io", Version: "v1beta1"}}}
213214
testCaseWithContext(t, &mcpContext{staticConfig: deniedResourcesServer}, func(c *mcpContext) {
214-
mockServer := NewMockServer()
215+
mockServer := test.NewMockServer()
215216
defer mockServer.Close()
216-
c.withKubeConfig(mockServer.config)
217+
c.withKubeConfig(mockServer.Config())
217218
mockServer.Handle(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
218219
w.Header().Set("Content-Type", "application/json")
219220
// Request Performed by DiscoveryClient to Kube API (Get API Groups legacy -core-)

0 commit comments

Comments
 (0)