@@ -2,6 +2,7 @@ package inventory
22
33import (
44 "context"
5+ "encoding/base64"
56 "encoding/json"
67 "fmt"
78 "testing"
@@ -1309,6 +1310,9 @@ func TestBuilderOwnsFeatureMetadataInputs(t *testing.T) {
13091310 prompts := []ServerPrompt {mockPrompt ("prompt" , "toolset1" )}
13101311 prompts [0 ].FeatureRule = promptRule
13111312 prompts [0 ].Prompt .Meta = nestedTestMeta ("prompt" , "prompt" )
1313+ expectedToolMeta := mustMarshalJSON (t , tools [0 ].Tool .Meta )
1314+ expectedResourceMeta := mustMarshalJSON (t , resources [0 ].Template .Meta )
1315+ expectedPromptMeta := mustMarshalJSON (t , prompts [0 ].Prompt .Meta )
13121316
13131317 checker := func (_ context.Context , flag FeatureFlag ) (bool , error ) {
13141318 return flag == "tool" || flag == "resource" || flag == "prompt" || flag == mcpAppsFeatureFlag , nil
@@ -1320,16 +1324,16 @@ func TestBuilderOwnsFeatureMetadataInputs(t *testing.T) {
13201324 WithToolsets ([]string {"all" }).
13211325 WithFeatureChecker (checker )
13221326
1323- setNestedTestMeta (tools [0 ].Tool .Meta , "ui" , "after-set" )
1324- setNestedTestMeta (resources [0 ].Template .Meta , "resource" , "after-set" )
1325- setNestedTestMeta (prompts [0 ].Prompt .Meta , "prompt" , "after-set" )
1327+ mutateSourceTestMeta (tools [0 ].Tool .Meta , "ui" , "after-set" , 's' )
1328+ mutateSourceTestMeta (resources [0 ].Template .Meta , "resource" , "after-set" , 's' )
1329+ mutateSourceTestMeta (prompts [0 ].Prompt .Meta , "prompt" , "after-set" , 's' )
13261330 inv := mustBuild (t , builder )
13271331 tools [0 ].FeatureRule .features [0 ] = "changed"
1328- setNestedTestMeta (tools [0 ].Tool .Meta , "ui" , "after-build" )
1332+ mutateSourceTestMeta (tools [0 ].Tool .Meta , "ui" , "after-build" , 'b' )
13291333 resources [0 ].FeatureRule .features [0 ] = "changed"
1330- setNestedTestMeta (resources [0 ].Template .Meta , "resource" , "after-build" )
1334+ mutateSourceTestMeta (resources [0 ].Template .Meta , "resource" , "after-build" , 'b' )
13311335 prompts [0 ].FeatureRule .features [0 ] = "changed"
1332- setNestedTestMeta (prompts [0 ].Prompt .Meta , "prompt" , "after-build" )
1336+ mutateSourceTestMeta (prompts [0 ].Prompt .Meta , "prompt" , "after-build" , 'b' )
13331337
13341338 require .Equal (t , []FeatureFlag {"prompt" , "remote_mcp_ui_apps" , "resource" , "tool" }, inv .RequiredFeatures ())
13351339 availableTools := inv .AvailableTools (context .Background ())
@@ -1342,18 +1346,18 @@ func TestBuilderOwnsFeatureMetadataInputs(t *testing.T) {
13421346 requireNestedTestMeta (t , availableResources [0 ].Template .Meta , "resource" , "resource" )
13431347 requireNestedTestMeta (t , availablePrompts [0 ].Prompt .Meta , "prompt" , "prompt" )
13441348
1345- setNestedTestMeta (availableTools [0 ].Tool .Meta , "ui" , "after-available" )
1349+ mutateNormalizedTestMeta (availableTools [0 ].Tool .Meta , "ui" , "after-available" )
13461350 availableTools [0 ].FeatureRule .features [0 ] = "changed"
1347- setNestedTestMeta (availableResources [0 ].Template .Meta , "resource" , "after-available" )
1351+ mutateNormalizedTestMeta (availableResources [0 ].Template .Meta , "resource" , "after-available" )
13481352 availableResources [0 ].FeatureRule .features [0 ] = "changed"
1349- setNestedTestMeta (availablePrompts [0 ].Prompt .Meta , "prompt" , "after-available" )
1353+ mutateNormalizedTestMeta (availablePrompts [0 ].Prompt .Meta , "prompt" , "after-available" )
13501354 availablePrompts [0 ].FeatureRule .features [0 ] = "changed"
13511355
13521356 allTools := inv .AllTools ()
1353- setNestedTestMeta (allTools [0 ].Tool .Meta , "ui" , "after-all" )
1357+ mutateNormalizedTestMeta (allTools [0 ].Tool .Meta , "ui" , "after-all" )
13541358 foundTool , _ , err := inv .FindToolByName ("tool" )
13551359 require .NoError (t , err )
1356- setNestedTestMeta (foundTool .Tool .Meta , "ui" , "after-find" )
1360+ mutateNormalizedTestMeta (foundTool .Tool .Meta , "ui" , "after-find" )
13571361
13581362 server := mcp .NewServer (& mcp.Implementation {Name : "test-server" , Version : "v0.0.1" }, nil )
13591363 inv .RegisterAll (context .Background (), server , nil )
@@ -1369,40 +1373,110 @@ func TestBuilderOwnsFeatureMetadataInputs(t *testing.T) {
13691373 registeredTools , err := clientSession .ListTools (context .Background (), nil )
13701374 require .NoError (t , err )
13711375 requireNestedTestMeta (t , registeredTools .Tools [0 ].Meta , "ui" , "tool" )
1376+ require .JSONEq (t , string (expectedToolMeta ), string (mustMarshalJSON (t , registeredTools .Tools [0 ].Meta )))
13721377 registeredResources , err := clientSession .ListResourceTemplates (context .Background (), nil )
13731378 require .NoError (t , err )
13741379 requireNestedTestMeta (t , registeredResources .ResourceTemplates [0 ].Meta , "resource" , "resource" )
1380+ require .JSONEq (t , string (expectedResourceMeta ), string (mustMarshalJSON (t , registeredResources .ResourceTemplates [0 ].Meta )))
13751381 registeredPrompts , err := clientSession .ListPrompts (context .Background (), nil )
13761382 require .NoError (t , err )
13771383 requireNestedTestMeta (t , registeredPrompts .Prompts [0 ].Meta , "prompt" , "prompt" )
1384+ require .JSONEq (t , string (expectedPromptMeta ), string (mustMarshalJSON (t , registeredPrompts .Prompts [0 ].Meta )))
1385+ }
1386+
1387+ type testMetaMap map [string ]string
1388+ type testMetaSlice []string
1389+ type testMetaPointer struct {
1390+ Value string `json:"value"`
13781391}
13791392
13801393func nestedTestMeta (key , value string ) mcp.Meta {
13811394 return mcp.Meta {
13821395 key : map [string ]any {
1383- "objects" : []any {map [string ]any {"value" : value }},
1384- "strings" : []string {value },
1396+ "objects" : []any {map [string ]any {"value" : value }},
1397+ "strings" : []string {value },
1398+ "typed_map" : testMetaMap {"value" : value },
1399+ "typed_slice" : testMetaSlice {value },
1400+ "bytes" : []byte (value ),
1401+ "pointer" : & testMetaPointer {Value : value },
13851402 },
13861403 }
13871404}
13881405
1389- func setNestedTestMeta (meta mcp.Meta , key , value string ) {
1406+ func mutateSourceTestMeta (meta mcp.Meta , key , value string , marker byte ) {
13901407 nested := meta [key ].(map [string ]any )
13911408 nested ["objects" ].([]any )[0 ].(map [string ]any )["value" ] = value
13921409 nested ["strings" ].([]string )[0 ] = value
1410+ nested ["typed_map" ].(testMetaMap )["value" ] = value
1411+ nested ["typed_slice" ].(testMetaSlice )[0 ] = value
1412+ nested ["bytes" ].([]byte )[0 ] = marker
1413+ nested ["pointer" ].(* testMetaPointer ).Value = value
1414+ }
1415+
1416+ func mutateNormalizedTestMeta (meta mcp.Meta , key , value string ) {
1417+ nested := meta [key ].(map [string ]any )
1418+ nested ["objects" ].([]any )[0 ].(map [string ]any )["value" ] = value
1419+ nested ["strings" ].([]any )[0 ] = value
1420+ nested ["typed_map" ].(map [string ]any )["value" ] = value
1421+ nested ["typed_slice" ].([]any )[0 ] = value
1422+ nested ["bytes" ] = value
1423+ nested ["pointer" ].(map [string ]any )["value" ] = value
13931424}
13941425
13951426func requireNestedTestMeta (t * testing.T , meta mcp.Meta , key , value string ) {
13961427 t .Helper ()
13971428 nested := meta [key ].(map [string ]any )
13981429 require .Equal (t , value , nested ["objects" ].([]any )[0 ].(map [string ]any )["value" ])
1399- switch strings := nested ["strings" ].(type ) {
1400- case []string :
1401- require .Equal (t , value , strings [0 ])
1402- case []any :
1403- require .Equal (t , value , strings [0 ])
1404- default :
1405- require .Failf (t , "unexpected strings metadata" , "type %T" , strings )
1430+ require .Equal (t , value , nested ["strings" ].([]any )[0 ])
1431+ require .Equal (t , value , nested ["typed_map" ].(map [string ]any )["value" ])
1432+ require .Equal (t , value , nested ["typed_slice" ].([]any )[0 ])
1433+ require .Equal (t , base64 .StdEncoding .EncodeToString ([]byte (value )), nested ["bytes" ])
1434+ require .Equal (t , value , nested ["pointer" ].(map [string ]any )["value" ])
1435+ }
1436+
1437+ func mustMarshalJSON (t * testing.T , value any ) []byte {
1438+ t .Helper ()
1439+ data , err := json .Marshal (value )
1440+ require .NoError (t , err )
1441+ return data
1442+ }
1443+
1444+ func TestBuildRejectsInvalidMetadata (t * testing.T ) {
1445+ tests := []struct {
1446+ name string
1447+ builder * Builder
1448+ errorText string
1449+ }{
1450+ {
1451+ name : "tool" ,
1452+ builder : NewBuilder ().SetTools ([]ServerTool {{
1453+ Tool : mcp.Tool {Name : "tool" , Meta : mcp.Meta {"invalid" : make (chan int )}},
1454+ }}),
1455+ errorText : `tool "tool" metadata` ,
1456+ },
1457+ {
1458+ name : "resource" ,
1459+ builder : NewBuilder ().SetResources ([]ServerResourceTemplate {{
1460+ Template : mcp.ResourceTemplate {Name : "resource" , Meta : mcp.Meta {"invalid" : make (chan int )}},
1461+ }}),
1462+ errorText : `resource template "resource" metadata` ,
1463+ },
1464+ {
1465+ name : "prompt" ,
1466+ builder : NewBuilder ().SetPrompts ([]ServerPrompt {{
1467+ Prompt : mcp.Prompt {Name : "prompt" , Meta : mcp.Meta {"invalid" : make (chan int )}},
1468+ }}),
1469+ errorText : `prompt "prompt" metadata` ,
1470+ },
1471+ }
1472+
1473+ for _ , tt := range tests {
1474+ t .Run (tt .name , func (t * testing.T ) {
1475+ _ , err := tt .builder .Build ()
1476+ require .ErrorContains (t , err , "invalid inventory metadata" )
1477+ require .ErrorContains (t , err , tt .errorText )
1478+ require .ErrorContains (t , err , "unsupported type: chan int" )
1479+ })
14061480 }
14071481}
14081482
0 commit comments