Skip to content
This repository was archived by the owner on Mar 29, 2025. It is now read-only.

Commit b4de84e

Browse files
committed
fix: test
1 parent 2901eb7 commit b4de84e

File tree

3 files changed

+75
-37
lines changed

3 files changed

+75
-37
lines changed

Makefile

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Makefile for golang-mcp-server-sdk
2-
.PHONY: build test lint clean run example
2+
.PHONY: build test test-race lint clean run example coverage deps update-deps help
33

44
# Go parameters
55
GOCMD=go
@@ -14,6 +14,20 @@ SSE_BIN_SERVER=bin/echo-sse-server
1414
ECHO_SSE_SERVER=cmd/echo-sse-server/main.go
1515
ECHO_STDIO_SERVER=cmd/echo-stdio-server/main.go
1616

17+
help:
18+
@echo "Available commands:"
19+
@echo " make - Run tests and build binaries"
20+
@echo " make build - Build the server binaries"
21+
@echo " make test - Run tests with race detection and coverage"
22+
@echo " make test-race - Run tests with race detection and coverage"
23+
@echo " make coverage - Generate test coverage report"
24+
@echo " make lint - Run linter"
25+
@echo " make clean - Clean build artifacts"
26+
@echo " make deps - Tidy up dependencies"
27+
@echo " make update-deps - Update dependencies"
28+
@echo " make example - Run example SSE server"
29+
@echo " make example-stdio - Run example stdio server"
30+
1731
all: test build
1832

1933
build:
@@ -27,7 +41,10 @@ example-stdio:
2741
cd echo-stdio-test && go run main.go
2842

2943
test:
30-
$(GOTEST) ./... -cover
44+
$(GOTEST) ./... -v -race -cover
45+
46+
test-race:
47+
$(GOTEST) ./... -v -race -cover
3148

3249
coverage:
3350
$(GOTEST) -cover -coverprofile=coverage.out ./...

internal/infrastructure/server/notification_test.go

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -125,49 +125,33 @@ func TestNotificationSender_SendNotification_SessionNotFound(t *testing.T) {
125125
}
126126

127127
// Test SendNotification - Channel full or closed
128-
func TestNotificationSender_SendNotification_ChannelFullOrClosed(t *testing.T) { // Renamed for clarity
128+
func TestNotificationSender_SendNotification_ChannelFullOrClosed(t *testing.T) {
129129
sender := NewNotificationSender(testJsonrpcVersion)
130130
sessionID := "full-channel-session"
131131
session := NewMCPSession(sessionID, "agent", 0) // Buffer size 0 - will block immediately
132132
sender.RegisterSession(session)
133-
// Do not start a receiver goroutine
134133

135134
notification := &domain.Notification{Method: "test"}
136-
ctx := context.Background()
137135

138-
sendAttempted := make(chan struct{}) // Channel to signal send attempt
139-
var wg sync.WaitGroup
140-
var err error
141-
wg.Add(1)
136+
// Create a context with timeout to avoid test hanging
137+
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
138+
defer cancel()
142139

140+
// Now unregister the session in a separate goroutine
141+
// This will cause the channel to close while SendNotification is potentially trying to send
143142
go func() {
144-
defer wg.Done()
145-
// Signal that we are about to attempt the send, potentially blocking
146-
close(sendAttempted)
147-
// This send will block because the channel is unbuffered and no one is reading.
148-
// It will error out when the channel is closed by UnregisterSession.
149-
err = sender.SendNotification(ctx, sessionID, notification)
143+
// Give a short delay to let SendNotification start
144+
time.Sleep(10 * time.Millisecond)
145+
sender.UnregisterSession(sessionID)
150146
}()
151147

152-
// Wait until the goroutine signals it's attempting the send
153-
<-sendAttempted
154-
155-
// Give the send a very brief moment to potentially enter the blocking select case within SendNotification.
156-
// This helps ensure the send is actually blocked before we close the channel.
157-
// Still a small timing element, but more targeted than the previous fixed sleep.
158-
time.Sleep(5 * time.Millisecond)
159-
160-
// Now unregister the session, which closes the channel, causing the blocked send to error out.
161-
sender.UnregisterSession(sessionID)
162-
163-
wg.Wait() // Wait for the SendNotification goroutine to finish
148+
// This should fail with an error either because the channel is full or closed
149+
err := sender.SendNotification(ctx, sessionID, notification)
164150

165-
require.Error(t, err, "Expected an error when sending to a closed/full channel")
166-
// Check that the error message indicates the problem, potentially mentioning the session.
167-
// The exact wording might depend on SendNotification's internal error handling (e.g., context vs channel state).
168-
assert.Contains(t, err.Error(), sessionID, "Error message should ideally mention the session ID")
169-
// Example of a more specific check if the error is known:
170-
// assert.Contains(t, err.Error(), "channel closed", "Error message should indicate channel closure")
151+
require.Error(t, err)
152+
// The error might be either about a full channel or a closed channel
153+
// depending on timing, but it should mention the session ID
154+
assert.Contains(t, err.Error(), sessionID)
171155
}
172156

173157
// Test SendNotification - Context cancelled

internal/infrastructure/server/sse_session_test.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package server
22

33
import (
4+
"bytes"
45
"net/http"
56
"net/http/httptest"
7+
"sync"
68
"testing"
79
"time"
810

@@ -14,6 +16,7 @@ import (
1416
type mockResponseWriterFlusher struct {
1517
*httptest.ResponseRecorder
1618
flushed bool
19+
mu sync.Mutex
1720
}
1821

1922
func newMockResponseWriterFlusher() *mockResponseWriterFlusher {
@@ -24,9 +27,43 @@ func newMockResponseWriterFlusher() *mockResponseWriterFlusher {
2427
}
2528

2629
func (m *mockResponseWriterFlusher) Flush() {
30+
m.mu.Lock()
31+
defer m.mu.Unlock()
2732
m.flushed = true
2833
}
2934

35+
func (m *mockResponseWriterFlusher) Header() http.Header {
36+
m.mu.Lock()
37+
defer m.mu.Unlock()
38+
return m.ResponseRecorder.Header()
39+
}
40+
41+
func (m *mockResponseWriterFlusher) Write(p []byte) (int, error) {
42+
m.mu.Lock()
43+
defer m.mu.Unlock()
44+
return m.ResponseRecorder.Write(p)
45+
}
46+
47+
func (m *mockResponseWriterFlusher) WriteHeader(statusCode int) {
48+
m.mu.Lock()
49+
defer m.mu.Unlock()
50+
m.ResponseRecorder.WriteHeader(statusCode)
51+
}
52+
53+
// Get the current body content safely
54+
func (m *mockResponseWriterFlusher) Body() *bytes.Buffer {
55+
m.mu.Lock()
56+
defer m.mu.Unlock()
57+
return m.ResponseRecorder.Body
58+
}
59+
60+
// Get the flushed status safely
61+
func (m *mockResponseWriterFlusher) Flushed() bool {
62+
m.mu.Lock()
63+
defer m.mu.Unlock()
64+
return m.flushed
65+
}
66+
3067
// strictNonFlusherWriter implements http.ResponseWriter but *not* http.Flusher
3168
type strictNonFlusherWriter struct{}
3269

@@ -142,12 +179,12 @@ func TestSSESession_Start(t *testing.T) {
142179
time.Sleep(50 * time.Millisecond)
143180

144181
// Verify headers are set correctly
145-
headers := w.ResponseRecorder.Header()
182+
headers := w.Header()
146183
assert.Equal(t, "text/event-stream", headers.Get("Content-Type"))
147184
assert.Equal(t, "no-cache", headers.Get("Cache-Control"))
148185
assert.Equal(t, "keep-alive", headers.Get("Connection"))
149186
assert.Equal(t, "*", headers.Get("Access-Control-Allow-Origin"))
150-
assert.True(t, w.flushed, "The response should be flushed")
187+
assert.True(t, w.Flushed(), "The response should be flushed")
151188

152189
// Send an event to the session
153190
testEvent := "event: test\ndata: test message\n\n"
@@ -157,7 +194,7 @@ func TestSSESession_Start(t *testing.T) {
157194
time.Sleep(50 * time.Millisecond)
158195

159196
// Verify the event was written to the response
160-
responseBody := w.ResponseRecorder.Body.String()
197+
responseBody := w.Body().String()
161198
assert.Contains(t, responseBody, "event: test")
162199
assert.Contains(t, responseBody, "data: test message")
163200

@@ -195,7 +232,7 @@ func TestSSESession_StartWithNotification(t *testing.T) {
195232
time.Sleep(50 * time.Millisecond)
196233

197234
// Verify the notification was converted to an SSE event and written to the response
198-
responseBody := w.ResponseRecorder.Body.String()
235+
responseBody := w.Body().String()
199236
assert.Contains(t, responseBody, "event: message")
200237
assert.Contains(t, responseBody, `"jsonrpc":"2.0"`)
201238
assert.Contains(t, responseBody, `"method":"test.method"`)

0 commit comments

Comments
 (0)