main_test.go

  1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: LicenseRef-MutuaL-1.2
  4
  5package main
  6
  7import (
  8	"context"
  9	"errors"
 10	"runtime/debug"
 11	"testing"
 12
 13	"git.secluded.site/cooked-mcp/internal/appconfig"
 14)
 15
 16type fakeMCPRunner struct {
 17	stdioCalls int
 18	httpCalls  int
 19	httpAddr   string
 20	httpToken  string
 21	err        error
 22}
 23
 24func (r *fakeMCPRunner) RunStdio(context.Context) error {
 25	r.stdioCalls++
 26
 27	return r.err
 28}
 29
 30func (r *fakeMCPRunner) RunHTTP(_ context.Context, addr, token string) error {
 31	r.httpCalls++
 32	r.httpAddr = addr
 33	r.httpToken = token
 34
 35	return r.err
 36}
 37
 38// server.CONFIG.8
 39func TestParseRunOptionsACIDServerConfig8ParsesHTTPAddr(t *testing.T) {
 40	got, err := parseRunOptions([]string{"--http-addr", "127.0.0.1:9999"})
 41	if err != nil {
 42		t.Fatalf("parseRunOptions() error = %v", err)
 43	}
 44
 45	if got.httpAddr != "127.0.0.1:9999" {
 46		t.Fatalf("httpAddr = %q, want CLI address", got.httpAddr)
 47	}
 48	if got.transport != "stdio" {
 49		t.Fatalf("transport = %q, want stdio", got.transport)
 50	}
 51}
 52
 53// server.TRANSPORT.4
 54func TestValidateTransportACIDServerTransport4AcceptsStdio(t *testing.T) {
 55	if err := validateTransport("stdio"); err != nil {
 56		t.Fatalf("validateTransport() error = %v, want nil", err)
 57	}
 58}
 59
 60// server.TRANSPORT.2-2
 61func TestValidateTransportACIDServerTransport2_2AcceptsHTTP(t *testing.T) {
 62	if err := validateTransport("http"); err != nil {
 63		t.Fatalf("validateTransport() error = %v, want nil", err)
 64	}
 65}
 66
 67// server.TRANSPORT.4
 68func TestValidateTransportACIDServerTransport4RejectsUnknownTransport(t *testing.T) {
 69	err := validateTransport("bogus")
 70	if err == nil {
 71		t.Fatal("validateTransport() error = nil, want unsupported transport error")
 72	}
 73	if err.Error() != `unsupported transport "bogus" (supported: stdio, http)` {
 74		t.Fatalf("validateTransport() error = %q", err.Error())
 75	}
 76}
 77
 78// server.TRANSPORT.2-1
 79func TestRunMCPServerACIDServerTransport2_1SelectsStdio(t *testing.T) {
 80	runner := &fakeMCPRunner{}
 81	err := runMCPServer(context.Background(), runner, appconfig.Config{}, "stdio")
 82	if err != nil {
 83		t.Fatalf("runMCPServer() error = %v", err)
 84	}
 85
 86	if runner.stdioCalls != 1 {
 87		t.Fatalf("stdio calls = %d, want 1", runner.stdioCalls)
 88	}
 89	if runner.httpCalls != 0 {
 90		t.Fatalf("http calls = %d, want 0", runner.httpCalls)
 91	}
 92}
 93
 94// server.TRANSPORT.2-2 server.TRANSPORT.3
 95func TestRunMCPServerACIDServerTransport2_2SelectsHTTP(t *testing.T) {
 96	runner := &fakeMCPRunner{}
 97	err := runMCPServer(context.Background(), runner, appconfig.Config{
 98		HTTPAddr:  "127.0.0.1:8123",
 99		HTTPToken: "secret-token",
100	}, "http")
101	if err != nil {
102		t.Fatalf("runMCPServer() error = %v", err)
103	}
104
105	if runner.httpCalls != 1 {
106		t.Fatalf("http calls = %d, want 1", runner.httpCalls)
107	}
108	if runner.httpAddr != "127.0.0.1:8123" {
109		t.Fatalf("http addr = %q, want configured address", runner.httpAddr)
110	}
111	if runner.httpToken != "secret-token" {
112		t.Fatal("HTTP token was not passed to RunHTTP")
113	}
114	if runner.stdioCalls != 0 {
115		t.Fatalf("stdio calls = %d, want 0", runner.stdioCalls)
116	}
117}
118
119func TestRunMCPServerTreatsContextCancellationAsCleanShutdown(t *testing.T) {
120	runner := &fakeMCPRunner{err: context.Canceled}
121	err := runMCPServer(context.Background(), runner, appconfig.Config{}, "http")
122	if err != nil {
123		t.Fatalf("runMCPServer() error = %v, want nil", err)
124	}
125}
126
127func TestRunMCPServerReturnsTransportErrors(t *testing.T) {
128	wantErr := errors.New("transport failed")
129	runner := &fakeMCPRunner{err: wantErr}
130	err := runMCPServer(context.Background(), runner, appconfig.Config{}, "stdio")
131	if !errors.Is(err, wantErr) {
132		t.Fatalf("runMCPServer() error = %v, want %v", err, wantErr)
133	}
134}
135
136func TestVersionFromBuildInfoPrefersModuleVersion(t *testing.T) {
137	got := versionFromBuildInfo(&debug.BuildInfo{Main: debug.Module{Version: "1.2.3"}})
138	if got != "1.2.3" {
139		t.Fatalf("versionFromBuildInfo() = %q, want 1.2.3", got)
140	}
141}
142
143func TestVersionFromBuildInfoFallsBackToVCSRevision(t *testing.T) {
144	got := versionFromBuildInfo(&debug.BuildInfo{
145		Main: debug.Module{Version: "(devel)"},
146		Settings: []debug.BuildSetting{
147			{Key: "vcs.revision", Value: "abc123"},
148			{Key: "vcs.modified", Value: "true"},
149		},
150	})
151	if got != "abc123-dirty" {
152		t.Fatalf("versionFromBuildInfo() = %q, want abc123-dirty", got)
153	}
154}