mcp-framework/manifest/manifest_test.go

419 lines
12 KiB
Go
Raw Permalink Normal View History

package manifest
import (
"errors"
"os"
"path/filepath"
"slices"
"testing"
)
func TestFindWalksParents(t *testing.T) {
root := t.TempDir()
manifestPath := filepath.Join(root, DefaultFile)
if err := os.WriteFile(manifestPath, []byte("[update]\n"), 0o600); err != nil {
t.Fatalf("WriteFile manifest: %v", err)
}
startDir := filepath.Join(root, "cmd", "graylog")
if err := os.MkdirAll(startDir, 0o755); err != nil {
t.Fatalf("MkdirAll startDir: %v", err)
}
got, err := Find(startDir)
if err != nil {
t.Fatalf("Find returned error: %v", err)
}
if got != manifestPath {
t.Fatalf("Find = %q, want %q", got, manifestPath)
}
}
func TestFindReturnsNotExistWhenMissing(t *testing.T) {
_, err := Find(t.TempDir())
if err == nil {
t.Fatal("expected error")
}
if !errors.Is(err, os.ErrNotExist) {
t.Fatalf("error = %v, want os.ErrNotExist", err)
}
}
func TestLoadParsesUpdateConfig(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, DefaultFile)
const content = `
[update]
source_name = " Gitea releases "
driver = " Gitea "
repository = " org/repo "
base_url = "https://gitea.example.com/"
latest_release_url = "https://gitea.example.com/api/releases/latest"
asset_name_template = "{binary}_{os}_{arch}{ext}"
checksum_asset_name = "{asset}.sha256"
checksum_required = true
signature_asset_name = "{asset}.sig"
signature_required = true
signature_public_key = " 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef "
signature_public_key_env_names = [" MCP_PUBKEY ", "", "MCP_RELEASE_PUBKEY"]
token_header = " Authorization "
token_prefix = " token "
token_env_names = [" GITEA_TOKEN ", "", "GITEA_RELEASE_TOKEN"]
`
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
t.Fatalf("WriteFile manifest: %v", err)
}
file, err := Load(path)
if err != nil {
t.Fatalf("Load returned error: %v", err)
}
source := file.Update.ReleaseSource()
if source.Name != "Gitea releases" {
t.Fatalf("source name = %q", source.Name)
}
if source.Driver != "gitea" {
t.Fatalf("driver = %q", source.Driver)
}
if source.Repository != "org/repo" {
t.Fatalf("repository = %q", source.Repository)
}
if source.BaseURL != "https://gitea.example.com" {
t.Fatalf("base URL = %q", source.BaseURL)
}
if source.LatestReleaseURL != "https://gitea.example.com/api/releases/latest" {
t.Fatalf("latest release URL = %q", source.LatestReleaseURL)
}
if source.AssetNameTemplate != "{binary}_{os}_{arch}{ext}" {
t.Fatalf("asset name template = %q", source.AssetNameTemplate)
}
if source.ChecksumAssetName != "{asset}.sha256" {
t.Fatalf("checksum asset name = %q", source.ChecksumAssetName)
}
if !source.ChecksumRequired {
t.Fatal("checksum required should be true")
}
if source.SignatureAssetName != "{asset}.sig" {
t.Fatalf("signature asset name = %q", source.SignatureAssetName)
}
if !source.SignatureRequired {
t.Fatal("signature required should be true")
}
if source.SignaturePublicKey == "" {
t.Fatal("signature public key should be set")
}
if len(source.SignaturePublicKeyEnvNames) != 2 {
t.Fatalf("signature env names = %v", source.SignaturePublicKeyEnvNames)
}
if source.TokenHeader != "Authorization" {
t.Fatalf("token header = %q", source.TokenHeader)
}
if source.TokenPrefix != "token" {
t.Fatalf("token prefix = %q", source.TokenPrefix)
}
if len(source.TokenEnvNames) != 2 {
t.Fatalf("token env names = %v", source.TokenEnvNames)
}
}
func TestLoadDefaultFindsManifest(t *testing.T) {
root := t.TempDir()
path := filepath.Join(root, DefaultFile)
if err := os.WriteFile(path, []byte("[update]\nlatest_release_url = \"https://example.com/latest\"\n"), 0o600); err != nil {
t.Fatalf("WriteFile manifest: %v", err)
}
startDir := filepath.Join(root, "cmd", "tool")
if err := os.MkdirAll(startDir, 0o755); err != nil {
t.Fatalf("MkdirAll startDir: %v", err)
}
file, gotPath, err := LoadDefault(startDir)
if err != nil {
t.Fatalf("LoadDefault returned error: %v", err)
}
if gotPath != path {
t.Fatalf("path = %q, want %q", gotPath, path)
}
if file.Update.LatestReleaseURL != "https://example.com/latest" {
t.Fatalf("latest release URL = %q", file.Update.LatestReleaseURL)
}
}
func TestLoadReturnsParseError(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, DefaultFile)
if err := os.WriteFile(path, []byte("[update\n"), 0o600); err != nil {
t.Fatalf("WriteFile manifest: %v", err)
}
_, err := Load(path)
if err == nil {
t.Fatal("expected error")
}
}
func TestLoadParsesExtendedManifestMetadata(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, DefaultFile)
const content = `
binary_name = " my-mcp "
docs_url = " https://docs.example.com/mcp "
[update]
latest_release_url = "https://example.com/latest"
[environment]
known = [" MCP_PROFILE ", "", "MCP_TOKEN"]
[secret_store]
backend_policy = " auto "
[profiles]
default = " prod "
known = [" default ", "", "prod"]
[bootstrap]
description = " Client MCP interne "
`
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
t.Fatalf("WriteFile manifest: %v", err)
}
file, err := Load(path)
if err != nil {
t.Fatalf("Load returned error: %v", err)
}
if file.BinaryName != "my-mcp" {
t.Fatalf("binary name = %q", file.BinaryName)
}
if file.DocsURL != "https://docs.example.com/mcp" {
t.Fatalf("docs URL = %q", file.DocsURL)
}
if !slices.Equal(file.Environment.Known, []string{"MCP_PROFILE", "MCP_TOKEN"}) {
t.Fatalf("environment known = %v", file.Environment.Known)
}
if file.SecretStore.BackendPolicy != "auto" {
t.Fatalf("secret store policy = %q", file.SecretStore.BackendPolicy)
}
if file.Profiles.Default != "prod" {
t.Fatalf("default profile = %q", file.Profiles.Default)
}
if !slices.Equal(file.Profiles.Known, []string{"default", "prod"}) {
t.Fatalf("profiles known = %v", file.Profiles.Known)
}
if file.Bootstrap.Description != "Client MCP interne" {
t.Fatalf("bootstrap description = %q", file.Bootstrap.Description)
}
bootstrap := file.BootstrapInfo()
if bootstrap.BinaryName != "my-mcp" {
t.Fatalf("bootstrap binary name = %q", bootstrap.BinaryName)
}
if bootstrap.DocsURL != "https://docs.example.com/mcp" {
t.Fatalf("bootstrap docs URL = %q", bootstrap.DocsURL)
}
if bootstrap.DefaultProfile != "prod" {
t.Fatalf("bootstrap default profile = %q", bootstrap.DefaultProfile)
}
if !slices.Equal(bootstrap.Profiles, []string{"default", "prod"}) {
t.Fatalf("bootstrap profiles = %v", bootstrap.Profiles)
}
scaffold := file.ScaffoldInfo()
if scaffold.SecretStorePolicy != "auto" {
t.Fatalf("scaffold secret store policy = %q", scaffold.SecretStorePolicy)
}
if !slices.Equal(scaffold.KnownEnvironmentVariables, []string{"MCP_PROFILE", "MCP_TOKEN"}) {
t.Fatalf("scaffold known environment variables = %v", scaffold.KnownEnvironmentVariables)
}
}
func TestLoadParsesSecretStoreBitwardenCache(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, DefaultFile)
const content = `
[secret_store]
backend_policy = "bitwarden-cli"
bitwarden_cache = false
`
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
t.Fatalf("WriteFile manifest: %v", err)
}
file, err := Load(path)
if err != nil {
t.Fatalf("Load returned error: %v", err)
}
if file.SecretStore.BitwardenCache == nil {
t.Fatal("bitwarden cache option is nil, want explicit false pointer")
}
if *file.SecretStore.BitwardenCache {
t.Fatal("bitwarden cache option = true, want false")
}
}
func TestLoadLeavesOmittedBitwardenCacheUnset(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, DefaultFile)
const content = `
[secret_store]
backend_policy = "bitwarden-cli"
`
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
t.Fatalf("WriteFile manifest: %v", err)
}
file, err := Load(path)
if err != nil {
t.Fatalf("Load returned error: %v", err)
}
if file.SecretStore.BitwardenCache != nil {
t.Fatalf("bitwarden cache option = %v, want nil when omitted", *file.SecretStore.BitwardenCache)
}
}
2026-05-02 10:02:23 +00:00
func TestLoadParsesConfigFields(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, DefaultFile)
const content = `
[[config.fields]]
name = " base_url "
flag = "base-url"
env = "BASE_URL"
config_key = "base_url"
type = " url "
label = " Graylog URL "
required = true
sources = [" flag ", "env", "config"]
[[config.fields]]
name = "api_token"
flag = "api-token"
env = "API_TOKEN"
secret_key_template = "profile/{profile}/api-token"
type = "secret"
required = true
sources = ["flag", "env", "secret"]
`
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
t.Fatalf("WriteFile manifest: %v", err)
}
file, err := Load(path)
if err != nil {
t.Fatalf("Load returned error: %v", err)
}
if len(file.Config.Fields) != 2 {
t.Fatalf("config fields = %d, want 2", len(file.Config.Fields))
}
baseURL := file.Config.Fields[0]
if baseURL.Name != "base_url" {
t.Fatalf("base URL name = %q", baseURL.Name)
}
if baseURL.Flag != "base-url" {
t.Fatalf("base URL flag = %q", baseURL.Flag)
}
if baseURL.Env != "BASE_URL" {
t.Fatalf("base URL env = %q", baseURL.Env)
}
if baseURL.ConfigKey != "base_url" {
t.Fatalf("base URL config key = %q", baseURL.ConfigKey)
}
if baseURL.Type != "url" {
t.Fatalf("base URL type = %q", baseURL.Type)
}
if baseURL.Label != "Graylog URL" {
t.Fatalf("base URL label = %q", baseURL.Label)
}
if !baseURL.Required {
t.Fatal("base URL should be required")
}
if !slices.Equal(baseURL.Sources, []string{"flag", "env", "config"}) {
t.Fatalf("base URL sources = %v", baseURL.Sources)
}
token := file.Config.Fields[1]
if token.SecretKeyTemplate != "profile/{profile}/api-token" {
t.Fatalf("token secret key template = %q", token.SecretKeyTemplate)
}
if !slices.Equal(token.Sources, []string{"flag", "env", "secret"}) {
t.Fatalf("token sources = %v", token.Sources)
}
}
func TestLoadEmbeddedParsesContent(t *testing.T) {
file, source, err := LoadEmbedded(`
[update]
latest_release_url = "https://example.com/latest"
`)
if err != nil {
t.Fatalf("LoadEmbedded returned error: %v", err)
}
if source != EmbeddedSource {
t.Fatalf("source = %q, want %q", source, EmbeddedSource)
}
if file.Update.LatestReleaseURL != "https://example.com/latest" {
t.Fatalf("latest release URL = %q", file.Update.LatestReleaseURL)
}
}
func TestLoadEmbeddedReturnsNotExistWhenEmpty(t *testing.T) {
_, _, err := LoadEmbedded(" ")
if !errors.Is(err, os.ErrNotExist) {
t.Fatalf("error = %v, want os.ErrNotExist", err)
}
}
func TestLoadDefaultOrEmbeddedPrefersManifestFile(t *testing.T) {
root := t.TempDir()
path := filepath.Join(root, DefaultFile)
if err := os.WriteFile(path, []byte("[update]\nlatest_release_url = \"https://example.com/from-file\"\n"), 0o600); err != nil {
t.Fatalf("WriteFile manifest: %v", err)
}
file, source, err := LoadDefaultOrEmbedded(root, `
[update]
latest_release_url = "https://example.com/from-embedded"
`)
if err != nil {
t.Fatalf("LoadDefaultOrEmbedded returned error: %v", err)
}
if source != path {
t.Fatalf("source = %q, want %q", source, path)
}
if file.Update.LatestReleaseURL != "https://example.com/from-file" {
t.Fatalf("latest release URL = %q", file.Update.LatestReleaseURL)
}
}
func TestLoadDefaultOrEmbeddedUsesEmbeddedWhenFileMissing(t *testing.T) {
file, source, err := LoadDefaultOrEmbedded(t.TempDir(), `
[update]
latest_release_url = "https://example.com/from-embedded"
`)
if err != nil {
t.Fatalf("LoadDefaultOrEmbedded returned error: %v", err)
}
if source != EmbeddedSource {
t.Fatalf("source = %q, want %q", source, EmbeddedSource)
}
if file.Update.LatestReleaseURL != "https://example.com/from-embedded" {
t.Fatalf("latest release URL = %q", file.Update.LatestReleaseURL)
}
}