blob: be79ef26b93fb7cbc5e90dae1c790a4829f43d4f [file] [log] [blame]
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mcp
import (
"path/filepath"
"runtime"
"strings"
"testing"
)
func TestFileRoot(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("TODO: fix for Windows")
}
for _, tt := range []struct {
uri string
want string
wantErr string // error must contain this string
}{
{uri: "file:///foo", want: "/foo"},
{uri: "file:///foo/bar", want: "/foo/bar"},
{uri: "file:///foo/../bar", want: "/bar"},
{uri: "file:/foo", want: "/foo"},
{uri: "http:///foo", wantErr: "not a file"},
{uri: "file://foo", wantErr: "empty path"},
{uri: ":", wantErr: "missing protocol scheme"},
} {
got, err := fileRoot(&Root{URI: tt.uri})
if err != nil {
if tt.wantErr == "" {
t.Errorf("%s: got %v, want success", tt.uri, err)
continue
}
if !strings.Contains(err.Error(), tt.wantErr) {
t.Errorf("%s: got %v, does not contain %q", tt.uri, err, tt.wantErr)
continue
}
} else if tt.wantErr != "" {
t.Errorf("%s: succeeded, but wanted error with %q", tt.uri, tt.wantErr)
} else if got != tt.want {
t.Errorf("%s: got %q, want %q", tt.uri, got, tt.want)
}
}
}
func TestComputeURIFilepath(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("TODO: fix for Windows")
}
// TODO(jba): test with Windows \\host paths and C: paths
dirFilepath := filepath.FromSlash("/files")
rootFilepaths := []string{
filepath.FromSlash("/files/public"),
filepath.FromSlash("/files/shared"),
}
for _, tt := range []struct {
uri string
want string
wantErr string // error must contain this
}{
{"file:///public", "public", ""},
{"file:///public/file", "public/file", ""},
{"file:///shared/file", "shared/file", ""},
{"http:///foo", "", "not a file"},
{"file://foo", "", "empty"},
{"file://foo/../bar", "", "localized"},
{"file:///secret", "", "root"},
{"file:///secret/file", "", "root"},
{"file:///private/file", "", "root"},
} {
t.Run(tt.uri, func(t *testing.T) {
tt.want = filepath.FromSlash(tt.want) // handle Windows
got, gotErr := computeURIFilepath(tt.uri, dirFilepath, rootFilepaths)
if gotErr != nil {
if tt.wantErr == "" {
t.Fatalf("got %v, wanted success", gotErr)
}
if !strings.Contains(gotErr.Error(), tt.wantErr) {
t.Fatalf("got error %v, does not contain %q", gotErr, tt.wantErr)
}
return
}
if tt.wantErr != "" {
t.Fatal("succeeded unexpectedly")
}
if got != tt.want {
t.Errorf("got %q, want %q", got, tt.want)
}
})
}
}
func TestReadFileResource(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("TODO: fix for Windows")
}
abs, err := filepath.Abs("testdata")
if err != nil {
t.Fatal(err)
}
dirFilepath := filepath.Join(abs, "files")
got, err := readFileResource("file:///info.txt", dirFilepath, nil)
if err != nil {
t.Fatal(err)
}
want := "Contents\n"
if g := string(got); g != want {
t.Errorf("got %q, want %q", g, want)
}
}
func TestTemplateMatch(t *testing.T) {
uri := "file:///path/to/file"
for _, tt := range []struct {
template string
want bool
}{
{"file:///{}/{a}/{b}", true},
{"file:///{a}/{b}", false},
{"file:///{+path}", true},
{"file:///{a}/{+path}", true},
} {
re, err := uriTemplateToRegexp(tt.template)
if err != nil {
t.Fatalf("%s: %v", tt.template, err)
}
got := re.MatchString(uri)
if got != tt.want {
t.Errorf("%s: got %t, want %t", tt.template, got, tt.want)
}
}
}