verae-mcp-timestamping-serv.../internal/server/server_test.go
George Lambert 2eb9ec72c6
Some checks failed
test-and-deploy / test (push) Failing after 10m46s
test-and-deploy / deploy-138 (push) Has been cancelled
feat(mcp): oauth_start / oauth_complete for social login
Browser OAuth via customer.verae.ai portal (authentik at auth.verae.ai).
oauth_start returns authorize_url+state; oauth_complete polls until xVerae-
access_token. Open without bearer on public MCP; welcome/register copy updated.
2026-10-20 16:40:50 -04:00

107 lines
2.8 KiB
Go

package server_test
import (
"context"
"encoding/json"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/internal/ledger"
"git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/internal/server"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
const sha256Empty = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
func TestStreamableHTTP_InitializeListStampLocal(t *testing.T) {
t.Parallel()
dir := t.TempDir()
led, err := ledger.Open(filepath.Join(dir, "ledger.json"))
if err != nil {
t.Fatal(err)
}
authPath := filepath.Join(dir, "auth.properties")
if err := os.WriteFile(authPath, []byte("username=x\npassword=y\n"), 0o600); err != nil {
t.Fatal(err)
}
srv, err := server.New(server.Options{
Ledger: led,
AuthPath: authPath,
GlobalURL: "http://127.0.0.1:1", // unused for local stamp
RequireBearer: false,
JSONResponse: true,
Stateless: true,
})
if err != nil {
t.Fatal(err)
}
httpServer := httptest.NewServer(srv.Handler())
t.Cleanup(httpServer.Close)
ctx := context.Background()
client := mcp.NewClient(&mcp.Implementation{Name: "test-client", Version: "0.0.1"}, nil)
session, err := client.Connect(ctx, &mcp.StreamableClientTransport{Endpoint: httpServer.URL + "/mcp"}, nil)
if err != nil {
t.Fatalf("connect: %v", err)
}
t.Cleanup(func() { _ = session.Close() })
tools, err := session.ListTools(ctx, nil)
if err != nil {
t.Fatalf("tools/list: %v", err)
}
wantTools := []string{"welcome", "stamp", "verify", "hash_report", "signup", "login", "oauth_start", "oauth_complete", "session", "create_user"}
got := map[string]bool{}
for _, tool := range tools.Tools {
got[tool.Name] = true
}
for _, name := range wantTools {
if !got[name] {
t.Fatalf("tools/list missing %q; got %#v", name, got)
}
}
res, err := session.CallTool(ctx, &mcp.CallToolParams{
Name: "stamp",
Arguments: map[string]any{
"sha256": sha256Empty,
"scope": "local",
},
})
if err != nil {
t.Fatalf("tools/call stamp: %v", err)
}
if res.IsError {
t.Fatalf("stamp tool error: %+v", res.Content)
}
raw, err := json.Marshal(res.StructuredContent)
if err != nil {
t.Fatal(err)
}
var out struct {
Receipts []struct {
Hash string `json:"hash"`
Scope string `json:"scope"`
} `json:"receipts"`
}
if err := json.Unmarshal(raw, &out); err != nil {
t.Fatalf("decode structured: %v raw=%s", err, raw)
}
if len(out.Receipts) != 1 {
t.Fatalf("want 1 receipt, got %s", raw)
}
if out.Receipts[0].Hash != sha256Empty {
t.Fatalf("hash=%q", out.Receipts[0].Hash)
}
if out.Receipts[0].Scope != "local" {
t.Fatalf("scope=%q", out.Receipts[0].Scope)
}
if !strings.Contains(string(raw), "receipt_id") {
t.Fatalf("missing receipt_id in %s", raw)
}
}