test(mcp): pin session concurrency, document error codes, assert id round-trip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mathias Bergqvist
2026-05-04 20:46:07 +02:00
parent ea19516109
commit 50a3b27825
3 changed files with 47 additions and 4 deletions

View File

@@ -2,12 +2,28 @@ package mcp
import "encoding/json"
// JSON-RPC application-defined error codes (range -32000 to -32099 per spec).
// Tool handlers return one of these from tools/call to signal a typed failure.
const (
// CodePermissionDenied: caller authenticated but lacks permission for this
// resource (e.g. owner not in the allowlist).
CodePermissionDenied = -32001
CodeNotFound = -32002
CodeConflict = -32003
CodeValidation = -32004
CodeUpstreamGitea = -32005
// CodeNotFound: target repo, file, branch, PR, issue, or workflow run does
// not exist.
CodeNotFound = -32002
// CodeConflict: write attempted on stale state (branch already exists,
// non-fast-forward push, file modified concurrently).
CodeConflict = -32003
// CodeValidation: arguments failed input validation (bad regex, oversized
// payload, missing required field).
CodeValidation = -32004
// CodeUpstreamGitea: Gitea API returned an error this server could not map
// to one of the codes above. The original status is in error.data.
CodeUpstreamGitea = -32005
)
type Request struct {

View File

@@ -15,6 +15,9 @@ func TestRequestUnmarshal(t *testing.T) {
require.NoError(t, json.Unmarshal(raw, &req))
assert.Equal(t, "2.0", req.JSONRPC)
assert.Equal(t, "initialize", req.Method)
// ID is opaque; encoding/json decodes JSON numbers into float64 by default.
// We don't type-assert in the server — we echo it back unchanged.
assert.Equal(t, float64(1), req.ID)
}
func TestErrorResponseShape(t *testing.T) {

View File

@@ -1,6 +1,7 @@
package mcp_test
import (
"sync"
"testing"
"gitea.d-ma.be/mathias/gitea-mcp/internal/mcp"
@@ -20,3 +21,26 @@ func TestSessionStoreIssueAndCheck(t *testing.T) {
s.Drop(id)
assert.False(t, s.Valid(id))
}
func TestSessionStoreConcurrency(t *testing.T) {
s := mcp.NewSessionStore()
const goroutines = 32
const perGoroutine = 100
var wg sync.WaitGroup
wg.Add(goroutines)
for i := 0; i < goroutines; i++ {
go func() {
defer wg.Done()
for j := 0; j < perGoroutine; j++ {
id := s.Issue()
if !s.Valid(id) {
t.Errorf("issued id %s reported invalid", id)
}
s.Drop(id)
}
}()
}
wg.Wait()
}