|
6 | 6 | "testing" |
7 | 7 |
|
8 | 8 | "github.com/stretchr/testify/assert" |
| 9 | + "github.com/stretchr/testify/require" |
9 | 10 |
|
10 | 11 | "github.com/modelcontextprotocol/registry/internal/config" |
11 | 12 | "github.com/modelcontextprotocol/registry/internal/validators" |
@@ -1302,8 +1303,8 @@ func TestValidateArgument_ValidNamedArguments(t *testing.T) { |
1302 | 1303 |
|
1303 | 1304 | func TestValidateArgument_ValidPositionalArguments(t *testing.T) { |
1304 | 1305 | positionalCases := []model.Argument{ |
1305 | | - {Type: model.ArgumentTypePositional, Name: "anything with spaces"}, |
1306 | | - {Type: model.ArgumentTypePositional, Name: "anything<with>brackets"}, |
| 1306 | + {Type: model.ArgumentTypePositional, Name: "anything with spaces", ValueHint: "target_dir"}, |
| 1307 | + {Type: model.ArgumentTypePositional, Name: "anything<with>brackets", ValueHint: "target_dir"}, |
1307 | 1308 | { |
1308 | 1309 | InputWithVariables: model.InputWithVariables{Input: model.Input{Value: "--port 8080"}}, |
1309 | 1310 | Type: model.ArgumentTypePositional, |
@@ -1457,6 +1458,105 @@ func TestValidateArgument_ValidValueFields(t *testing.T) { |
1457 | 1458 | } |
1458 | 1459 | } |
1459 | 1460 |
|
| 1461 | +func TestValidateArgument_InvalidArgumentType(t *testing.T) { |
| 1462 | + invalidTypeCases := []struct { |
| 1463 | + name string |
| 1464 | + arg model.Argument |
| 1465 | + }{ |
| 1466 | + { |
| 1467 | + // Same shape as live entry io.github.Jordan-Horner/[email protected] |
| 1468 | + // (there under packageArguments): name and value are set, so only |
| 1469 | + // the type check can fire |
| 1470 | + "empty_type", |
| 1471 | + model.Argument{ |
| 1472 | + InputWithVariables: model.InputWithVariables{Input: model.Input{Value: "mcp"}}, |
| 1473 | + Type: "", |
| 1474 | + Name: "command", |
| 1475 | + }, |
| 1476 | + }, |
| 1477 | + {"literal", model.Argument{Type: "literal"}}, |
| 1478 | + {"flag", model.Argument{Type: "flag", Name: "--verbose"}}, |
| 1479 | + {"environment", model.Argument{Type: "environment", Name: "API_KEY"}}, |
| 1480 | + // Type values are case-sensitive: "Positional" is not "positional" |
| 1481 | + {"uppercase_positional", model.Argument{Type: "Positional"}}, |
| 1482 | + } |
| 1483 | + |
| 1484 | + for _, tc := range invalidTypeCases { |
| 1485 | + t.Run("Invalid_"+tc.name, func(t *testing.T) { |
| 1486 | + server := createValidServerWithArgument(tc.arg) |
| 1487 | + // ValidateServerJSON returns all validation results; using FirstError() to preserve existing test behavior |
| 1488 | + // In future, consider using result.Issues for comprehensive error reporting |
| 1489 | + result := validators.ValidateServerJSON(&server, validators.ValidationSchemaVersionAndSemantic) |
| 1490 | + err := result.FirstError() |
| 1491 | + require.Error(t, err, "Expected error for invalid argument type: %+v", tc.arg) |
| 1492 | + assert.Contains(t, err.Error(), "argument type must be 'positional' or 'named'") |
| 1493 | + }) |
| 1494 | + } |
| 1495 | +} |
| 1496 | + |
| 1497 | +func TestValidateArgument_PositionalValueOrHint(t *testing.T) { |
| 1498 | + positionalCases := []struct { |
| 1499 | + name string |
| 1500 | + arg model.Argument |
| 1501 | + expectError bool |
| 1502 | + }{ |
| 1503 | + { |
| 1504 | + "missing_value_and_value_hint", |
| 1505 | + model.Argument{Type: model.ArgumentTypePositional}, |
| 1506 | + true, |
| 1507 | + }, |
| 1508 | + { |
| 1509 | + "value_only", |
| 1510 | + model.Argument{ |
| 1511 | + InputWithVariables: model.InputWithVariables{Input: model.Input{Value: "input.txt"}}, |
| 1512 | + Type: model.ArgumentTypePositional, |
| 1513 | + }, |
| 1514 | + false, |
| 1515 | + }, |
| 1516 | + { |
| 1517 | + "value_hint_only", |
| 1518 | + model.Argument{Type: model.ArgumentTypePositional, ValueHint: "file_path"}, |
| 1519 | + false, |
| 1520 | + }, |
| 1521 | + { |
| 1522 | + "value_and_value_hint", |
| 1523 | + model.Argument{ |
| 1524 | + InputWithVariables: model.InputWithVariables{Input: model.Input{Value: "input.txt"}}, |
| 1525 | + Type: model.ArgumentTypePositional, |
| 1526 | + ValueHint: "file_path", |
| 1527 | + }, |
| 1528 | + false, |
| 1529 | + }, |
| 1530 | + } |
| 1531 | + |
| 1532 | + for _, tc := range positionalCases { |
| 1533 | + t.Run(tc.name, func(t *testing.T) { |
| 1534 | + server := createValidServerWithArgument(tc.arg) |
| 1535 | + // ValidateServerJSON returns all validation results; using FirstError() to preserve existing test behavior |
| 1536 | + // In future, consider using result.Issues for comprehensive error reporting |
| 1537 | + result := validators.ValidateServerJSON(&server, validators.ValidationSchemaVersionAndSemantic) |
| 1538 | + err := result.FirstError() |
| 1539 | + if tc.expectError { |
| 1540 | + require.Error(t, err, "Expected error for positional argument without value or valueHint: %+v", tc.arg) |
| 1541 | + assert.Contains(t, err.Error(), "positional argument must provide a value or a valueHint") |
| 1542 | + } else { |
| 1543 | + assert.NoError(t, err, "Expected valid positional argument %+v", tc.arg) |
| 1544 | + } |
| 1545 | + }) |
| 1546 | + } |
| 1547 | + |
| 1548 | + // The same check must apply to arguments under packageArguments |
| 1549 | + t.Run("missing_value_and_value_hint_in_package_arguments", func(t *testing.T) { |
| 1550 | + server := createValidServerWithPackageArgument(model.Argument{Type: model.ArgumentTypePositional}) |
| 1551 | + // ValidateServerJSON returns all validation results; using FirstError() to preserve existing test behavior |
| 1552 | + // In future, consider using result.Issues for comprehensive error reporting |
| 1553 | + result := validators.ValidateServerJSON(&server, validators.ValidationSchemaVersionAndSemantic) |
| 1554 | + err := result.FirstError() |
| 1555 | + require.Error(t, err, "Expected error for positional package argument without value or valueHint") |
| 1556 | + assert.Contains(t, err.Error(), "positional argument must provide a value or a valueHint") |
| 1557 | + }) |
| 1558 | +} |
| 1559 | + |
1460 | 1560 | // Helper function to create a valid server with a specific argument for testing |
1461 | 1561 | func TestValidate_TransportValidation(t *testing.T) { |
1462 | 1562 | tests := []struct { |
@@ -2041,6 +2141,15 @@ func createValidServerWithArgument(arg model.Argument) apiv0.ServerJSON { |
2041 | 2141 | } |
2042 | 2142 | } |
2043 | 2143 |
|
| 2144 | +// createValidServerWithPackageArgument is createValidServerWithArgument with the |
| 2145 | +// argument under packageArguments instead of runtimeArguments |
| 2146 | +func createValidServerWithPackageArgument(arg model.Argument) apiv0.ServerJSON { |
| 2147 | + server := createValidServerWithArgument(arg) |
| 2148 | + server.Packages[0].RuntimeArguments = nil |
| 2149 | + server.Packages[0].PackageArguments = []model.Argument{arg} |
| 2150 | + return server |
| 2151 | +} |
| 2152 | + |
2044 | 2153 | func TestValidateTitle(t *testing.T) { |
2045 | 2154 | tests := []struct { |
2046 | 2155 | name string |
|
0 commit comments