This is page 4 of 5. Use http://codebase.md/razorpay/razorpay-mcp-server?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .cursor │ └── rules │ └── new-tool-from-docs.mdc ├── .cursorignore ├── .dockerignore ├── .github │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.md │ │ ├── config.yml │ │ └── feature_request.md │ ├── pull_request_template.md │ └── workflows │ ├── assign.yml │ ├── build.yml │ ├── ci.yml │ ├── docker-publish.yml │ ├── lint.yml │ └── release.yml ├── .gitignore ├── .golangci.yaml ├── .goreleaser.yaml ├── cmd │ └── razorpay-mcp-server │ ├── main.go │ └── stdio.go ├── codecov.yml ├── CONTRIBUTING.md ├── Dockerfile ├── go.mod ├── go.sum ├── LICENSE ├── Makefile ├── pkg │ ├── contextkey │ │ └── context_key.go │ ├── log │ │ ├── config.go │ │ ├── log.go │ │ ├── slog_test.go │ │ └── slog.go │ ├── mcpgo │ │ ├── README.md │ │ ├── server.go │ │ ├── stdio.go │ │ ├── tool.go │ │ └── transport.go │ ├── observability │ │ └── observability.go │ ├── razorpay │ │ ├── mock │ │ │ ├── server_test.go │ │ │ └── server.go │ │ ├── orders_test.go │ │ ├── orders.go │ │ ├── payment_links_test.go │ │ ├── payment_links.go │ │ ├── payments_test.go │ │ ├── payments.go │ │ ├── payouts_test.go │ │ ├── payouts.go │ │ ├── qr_codes_test.go │ │ ├── qr_codes.go │ │ ├── README.md │ │ ├── refunds_test.go │ │ ├── refunds.go │ │ ├── server.go │ │ ├── settlements_test.go │ │ ├── settlements.go │ │ ├── test_helpers.go │ │ ├── tokens_test.go │ │ ├── tokens.go │ │ ├── tools_params_test.go │ │ ├── tools_params.go │ │ ├── tools_test.go │ │ └── tools.go │ └── toolsets │ └── toolsets.go ├── README.md └── SECURITY.md ``` # Files -------------------------------------------------------------------------------- /pkg/razorpay/tools_params_test.go: -------------------------------------------------------------------------------- ```go 1 | package razorpay 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/razorpay/razorpay-mcp-server/pkg/mcpgo" 9 | ) 10 | 11 | func TestValidator(t *testing.T) { 12 | tests := []struct { 13 | name string 14 | args map[string]interface{} 15 | paramName string 16 | validationFunc func(*Validator, map[string]interface{}, string) *Validator 17 | expectError bool 18 | expectValue interface{} 19 | expectKey string 20 | }{ 21 | // String tests 22 | { 23 | name: "required string - valid", 24 | args: map[string]interface{}{"test_param": "test_value"}, 25 | paramName: "test_param", 26 | validationFunc: (*Validator).ValidateAndAddRequiredString, 27 | expectError: false, 28 | expectValue: "test_value", 29 | expectKey: "test_param", 30 | }, 31 | { 32 | name: "required string - missing", 33 | args: map[string]interface{}{}, 34 | paramName: "test_param", 35 | validationFunc: (*Validator).ValidateAndAddRequiredString, 36 | expectError: true, 37 | expectValue: nil, 38 | expectKey: "test_param", 39 | }, 40 | { 41 | name: "optional string - valid", 42 | args: map[string]interface{}{"test_param": "test_value"}, 43 | paramName: "test_param", 44 | validationFunc: (*Validator).ValidateAndAddOptionalString, 45 | expectError: false, 46 | expectValue: "test_value", 47 | expectKey: "test_param", 48 | }, 49 | { 50 | name: "optional string - empty", 51 | args: map[string]interface{}{"test_param": ""}, 52 | paramName: "test_param", 53 | validationFunc: (*Validator).ValidateAndAddOptionalString, 54 | expectError: false, 55 | expectValue: "", 56 | expectKey: "test_param", 57 | }, 58 | 59 | // Int tests 60 | { 61 | name: "required int - valid", 62 | args: map[string]interface{}{"test_param": float64(123)}, 63 | paramName: "test_param", 64 | validationFunc: (*Validator).ValidateAndAddRequiredInt, 65 | expectError: false, 66 | expectValue: int64(123), 67 | expectKey: "test_param", 68 | }, 69 | { 70 | name: "optional int - valid", 71 | args: map[string]interface{}{"test_param": float64(123)}, 72 | paramName: "test_param", 73 | validationFunc: (*Validator).ValidateAndAddOptionalInt, 74 | expectError: false, 75 | expectValue: int64(123), 76 | expectKey: "test_param", 77 | }, 78 | { 79 | name: "optional int - zero", 80 | args: map[string]interface{}{"test_param": float64(0)}, 81 | paramName: "test_param", 82 | validationFunc: (*Validator).ValidateAndAddOptionalInt, 83 | expectError: false, 84 | expectValue: int64(0), // we expect the zero values as is 85 | expectKey: "test_param", 86 | }, 87 | 88 | // Float tests 89 | { 90 | name: "required float - valid", 91 | args: map[string]interface{}{"test_param": float64(123.45)}, 92 | paramName: "test_param", 93 | validationFunc: (*Validator).ValidateAndAddRequiredFloat, 94 | expectError: false, 95 | expectValue: float64(123.45), 96 | expectKey: "test_param", 97 | }, 98 | { 99 | name: "optional float - valid", 100 | args: map[string]interface{}{"test_param": float64(123.45)}, 101 | paramName: "test_param", 102 | validationFunc: (*Validator).ValidateAndAddOptionalFloat, 103 | expectError: false, 104 | expectValue: float64(123.45), 105 | expectKey: "test_param", 106 | }, 107 | { 108 | name: "optional float - zero", 109 | args: map[string]interface{}{"test_param": float64(0)}, 110 | paramName: "test_param", 111 | validationFunc: (*Validator).ValidateAndAddOptionalFloat, 112 | expectError: false, 113 | expectValue: float64(0), 114 | expectKey: "test_param", 115 | }, 116 | 117 | // Bool tests 118 | { 119 | name: "required bool - true", 120 | args: map[string]interface{}{"test_param": true}, 121 | paramName: "test_param", 122 | validationFunc: (*Validator).ValidateAndAddRequiredBool, 123 | expectError: false, 124 | expectValue: true, 125 | expectKey: "test_param", 126 | }, 127 | { 128 | name: "required bool - false", 129 | args: map[string]interface{}{"test_param": false}, 130 | paramName: "test_param", 131 | validationFunc: (*Validator).ValidateAndAddRequiredBool, 132 | expectError: false, 133 | expectValue: false, 134 | expectKey: "test_param", 135 | }, 136 | { 137 | name: "optional bool - true", 138 | args: map[string]interface{}{"test_param": true}, 139 | paramName: "test_param", 140 | validationFunc: (*Validator).ValidateAndAddOptionalBool, 141 | expectError: false, 142 | expectValue: true, 143 | expectKey: "test_param", 144 | }, 145 | { 146 | name: "optional bool - false", 147 | args: map[string]interface{}{"test_param": false}, 148 | paramName: "test_param", 149 | validationFunc: (*Validator).ValidateAndAddOptionalBool, 150 | expectError: false, 151 | expectValue: false, 152 | expectKey: "test_param", 153 | }, 154 | 155 | // Map tests 156 | { 157 | name: "required map - valid", 158 | args: map[string]interface{}{ 159 | "test_param": map[string]interface{}{"key": "value"}, 160 | }, 161 | paramName: "test_param", 162 | validationFunc: (*Validator).ValidateAndAddRequiredMap, 163 | expectError: false, 164 | expectValue: map[string]interface{}{"key": "value"}, 165 | expectKey: "test_param", 166 | }, 167 | { 168 | name: "optional map - valid", 169 | args: map[string]interface{}{ 170 | "test_param": map[string]interface{}{"key": "value"}, 171 | }, 172 | paramName: "test_param", 173 | validationFunc: (*Validator).ValidateAndAddOptionalMap, 174 | expectError: false, 175 | expectValue: map[string]interface{}{"key": "value"}, 176 | expectKey: "test_param", 177 | }, 178 | { 179 | name: "optional map - empty", 180 | args: map[string]interface{}{ 181 | "test_param": map[string]interface{}{}, 182 | }, 183 | paramName: "test_param", 184 | validationFunc: (*Validator).ValidateAndAddOptionalMap, 185 | expectError: false, 186 | expectValue: map[string]interface{}{}, 187 | expectKey: "test_param", 188 | }, 189 | 190 | // Array tests 191 | { 192 | name: "required array - valid", 193 | args: map[string]interface{}{ 194 | "test_param": []interface{}{"value1", "value2"}, 195 | }, 196 | paramName: "test_param", 197 | validationFunc: (*Validator).ValidateAndAddRequiredArray, 198 | expectError: false, 199 | expectValue: []interface{}{"value1", "value2"}, 200 | expectKey: "test_param", 201 | }, 202 | { 203 | name: "optional array - valid", 204 | args: map[string]interface{}{ 205 | "test_param": []interface{}{"value1", "value2"}, 206 | }, 207 | paramName: "test_param", 208 | validationFunc: (*Validator).ValidateAndAddOptionalArray, 209 | expectError: false, 210 | expectValue: []interface{}{"value1", "value2"}, 211 | expectKey: "test_param", 212 | }, 213 | { 214 | name: "optional array - empty", 215 | args: map[string]interface{}{"test_param": []interface{}{}}, 216 | paramName: "test_param", 217 | validationFunc: (*Validator).ValidateAndAddOptionalArray, 218 | expectError: false, 219 | expectValue: []interface{}{}, 220 | expectKey: "test_param", 221 | }, 222 | 223 | // Invalid type tests 224 | { 225 | name: "required string - wrong type", 226 | args: map[string]interface{}{"test_param": 123}, 227 | paramName: "test_param", 228 | validationFunc: (*Validator).ValidateAndAddRequiredString, 229 | expectError: true, 230 | expectValue: nil, 231 | expectKey: "test_param", 232 | }, 233 | { 234 | name: "required int - wrong type", 235 | args: map[string]interface{}{"test_param": "not a number"}, 236 | paramName: "test_param", 237 | validationFunc: (*Validator).ValidateAndAddRequiredInt, 238 | expectError: true, 239 | expectValue: nil, 240 | expectKey: "test_param", 241 | }, 242 | } 243 | 244 | for _, tt := range tests { 245 | t.Run(tt.name, func(t *testing.T) { 246 | result := make(map[string]interface{}) 247 | request := &mcpgo.CallToolRequest{ 248 | Arguments: tt.args, 249 | } 250 | validator := NewValidator(request) 251 | 252 | tt.validationFunc(validator, result, tt.paramName) 253 | 254 | if tt.expectError { 255 | assert.True(t, validator.HasErrors(), "Expected validation error") 256 | } else { 257 | assert.False(t, validator.HasErrors(), "Did not expect validation error") 258 | assert.Equal(t, 259 | tt.expectValue, 260 | result[tt.expectKey], 261 | "Parameter value mismatch", 262 | ) 263 | } 264 | }) 265 | } 266 | } 267 | 268 | func TestValidatorPagination(t *testing.T) { 269 | tests := []struct { 270 | name string 271 | args map[string]interface{} 272 | expectCount interface{} 273 | expectSkip interface{} 274 | expectError bool 275 | }{ 276 | { 277 | name: "valid pagination params", 278 | args: map[string]interface{}{ 279 | "count": float64(10), 280 | "skip": float64(5), 281 | }, 282 | expectCount: int64(10), 283 | expectSkip: int64(5), 284 | expectError: false, 285 | }, 286 | { 287 | name: "zero pagination params", 288 | args: map[string]interface{}{"count": float64(0), "skip": float64(0)}, 289 | expectCount: int64(0), 290 | expectSkip: int64(0), 291 | expectError: false, 292 | }, 293 | { 294 | name: "invalid count type", 295 | args: map[string]interface{}{ 296 | "count": "not a number", 297 | "skip": float64(5), 298 | }, 299 | expectCount: nil, 300 | expectSkip: int64(5), 301 | expectError: true, 302 | }, 303 | } 304 | 305 | for _, tt := range tests { 306 | t.Run(tt.name, func(t *testing.T) { 307 | result := make(map[string]interface{}) 308 | request := &mcpgo.CallToolRequest{ 309 | Arguments: tt.args, 310 | } 311 | validator := NewValidator(request) 312 | 313 | validator.ValidateAndAddPagination(result) 314 | 315 | if tt.expectError { 316 | assert.True(t, validator.HasErrors(), "Expected validation error") 317 | } else { 318 | assert.False(t, validator.HasErrors(), "Did not expect validation error") 319 | } 320 | 321 | if tt.expectCount != nil { 322 | assert.Equal(t, tt.expectCount, result["count"], "Count mismatch") 323 | } else { 324 | _, exists := result["count"] 325 | assert.False(t, exists, "Count should not be added") 326 | } 327 | 328 | if tt.expectSkip != nil { 329 | assert.Equal(t, tt.expectSkip, result["skip"], "Skip mismatch") 330 | } else { 331 | _, exists := result["skip"] 332 | assert.False(t, exists, "Skip should not be added") 333 | } 334 | }) 335 | } 336 | } 337 | 338 | func TestValidatorExpand(t *testing.T) { 339 | tests := []struct { 340 | name string 341 | args map[string]interface{} 342 | expectExpand string 343 | expectError bool 344 | }{ 345 | { 346 | name: "valid expand param", 347 | args: map[string]interface{}{"expand": []interface{}{"payments"}}, 348 | expectExpand: "payments", 349 | expectError: false, 350 | }, 351 | { 352 | name: "empty expand array", 353 | args: map[string]interface{}{"expand": []interface{}{}}, 354 | expectExpand: "", 355 | expectError: false, 356 | }, 357 | { 358 | name: "invalid expand type", 359 | args: map[string]interface{}{"expand": "not an array"}, 360 | expectExpand: "", 361 | expectError: true, 362 | }, 363 | } 364 | 365 | for _, tt := range tests { 366 | t.Run(tt.name, func(t *testing.T) { 367 | result := make(map[string]interface{}) 368 | request := &mcpgo.CallToolRequest{ 369 | Arguments: tt.args, 370 | } 371 | validator := NewValidator(request) 372 | 373 | validator.ValidateAndAddExpand(result) 374 | 375 | if tt.expectError { 376 | assert.True(t, validator.HasErrors(), "Expected validation error") 377 | } else { 378 | assert.False(t, validator.HasErrors(), "Did not expect validation error") 379 | if tt.expectExpand != "" { 380 | assert.Equal(t, 381 | tt.expectExpand, 382 | result["expand[]"], 383 | "Expand value mismatch", 384 | ) 385 | } else { 386 | _, exists := result["expand[]"] 387 | assert.False(t, exists, "Expand should not be added") 388 | } 389 | } 390 | }) 391 | } 392 | } 393 | 394 | // Test validator "To" functions which write to target maps 395 | func TestValidatorToFunctions(t *testing.T) { 396 | tests := []struct { 397 | name string 398 | args map[string]interface{} 399 | paramName string 400 | targetKey string 401 | testFunc func( 402 | *Validator, map[string]interface{}, string, string, 403 | ) *Validator 404 | expectValue interface{} 405 | expectError bool 406 | }{ 407 | // ValidateAndAddOptionalStringToPath tests 408 | { 409 | name: "optional string to target - valid", 410 | args: map[string]interface{}{"customer_name": "Test User"}, 411 | paramName: "customer_name", 412 | targetKey: "name", 413 | testFunc: (*Validator).ValidateAndAddOptionalStringToPath, 414 | expectValue: "Test User", 415 | expectError: false, 416 | }, 417 | { 418 | name: "optional string to target - empty", 419 | args: map[string]interface{}{"customer_name": ""}, 420 | paramName: "customer_name", 421 | targetKey: "name", 422 | testFunc: (*Validator).ValidateAndAddOptionalStringToPath, 423 | expectValue: "", 424 | expectError: false, 425 | }, 426 | { 427 | name: "optional string to target - missing", 428 | args: map[string]interface{}{}, 429 | paramName: "customer_name", 430 | targetKey: "name", 431 | testFunc: (*Validator).ValidateAndAddOptionalStringToPath, 432 | expectValue: nil, 433 | expectError: false, 434 | }, 435 | { 436 | name: "optional string to target - wrong type", 437 | args: map[string]interface{}{"customer_name": 123}, 438 | paramName: "customer_name", 439 | targetKey: "name", 440 | testFunc: (*Validator).ValidateAndAddOptionalStringToPath, 441 | expectValue: nil, 442 | expectError: true, 443 | }, 444 | 445 | // ValidateAndAddOptionalBoolToPath tests 446 | { 447 | name: "optional bool to target - true", 448 | args: map[string]interface{}{"notify_sms": true}, 449 | paramName: "notify_sms", 450 | targetKey: "sms", 451 | testFunc: (*Validator).ValidateAndAddOptionalBoolToPath, 452 | expectValue: true, 453 | expectError: false, 454 | }, 455 | { 456 | name: "optional bool to target - false", 457 | args: map[string]interface{}{"notify_sms": false}, 458 | paramName: "notify_sms", 459 | targetKey: "sms", 460 | testFunc: (*Validator).ValidateAndAddOptionalBoolToPath, 461 | expectValue: false, 462 | expectError: false, 463 | }, 464 | { 465 | name: "optional bool to target - wrong type", 466 | args: map[string]interface{}{"notify_sms": "not a bool"}, 467 | paramName: "notify_sms", 468 | targetKey: "sms", 469 | testFunc: (*Validator).ValidateAndAddOptionalBoolToPath, 470 | expectValue: nil, 471 | expectError: true, 472 | }, 473 | 474 | // ValidateAndAddOptionalIntToPath tests 475 | { 476 | name: "optional int to target - valid", 477 | args: map[string]interface{}{"age": float64(25)}, 478 | paramName: "age", 479 | targetKey: "customer_age", 480 | testFunc: (*Validator).ValidateAndAddOptionalIntToPath, 481 | expectValue: int64(25), 482 | expectError: false, 483 | }, 484 | { 485 | name: "optional int to target - zero", 486 | args: map[string]interface{}{"age": float64(0)}, 487 | paramName: "age", 488 | targetKey: "customer_age", 489 | testFunc: (*Validator).ValidateAndAddOptionalIntToPath, 490 | expectValue: int64(0), 491 | expectError: false, 492 | }, 493 | { 494 | name: "optional int to target - missing", 495 | args: map[string]interface{}{}, 496 | paramName: "age", 497 | targetKey: "customer_age", 498 | testFunc: (*Validator).ValidateAndAddOptionalIntToPath, 499 | expectValue: nil, 500 | expectError: false, 501 | }, 502 | { 503 | name: "optional int to target - wrong type", 504 | args: map[string]interface{}{"age": "not a number"}, 505 | paramName: "age", 506 | targetKey: "customer_age", 507 | testFunc: (*Validator).ValidateAndAddOptionalIntToPath, 508 | expectValue: nil, 509 | expectError: true, 510 | }, 511 | } 512 | 513 | for _, tt := range tests { 514 | t.Run(tt.name, func(t *testing.T) { 515 | // Create a target map for this specific test 516 | target := make(map[string]interface{}) 517 | 518 | // Create the request and validator 519 | request := &mcpgo.CallToolRequest{ 520 | Arguments: tt.args, 521 | } 522 | validator := NewValidator(request) 523 | 524 | // Call the test function with target and verify its return value 525 | tt.testFunc(validator, target, tt.paramName, tt.targetKey) 526 | 527 | // Check if we got the expected errors 528 | if tt.expectError { 529 | assert.True(t, validator.HasErrors(), "Expected validation error") 530 | } else { 531 | assert.False(t, validator.HasErrors(), "Did not expect validation error") 532 | 533 | // For non-error cases, check target map value 534 | if tt.expectValue != nil { 535 | // Should have the value with the target key 536 | assert.Equal(t, 537 | tt.expectValue, 538 | target[tt.targetKey], 539 | "Target map value mismatch") 540 | } else { 541 | // Target key should not exist 542 | _, exists := target[tt.targetKey] 543 | assert.False(t, exists, "Key should not be in target map when value is empty") // nolint:lll 544 | } 545 | } 546 | }) 547 | } 548 | } 549 | 550 | // Test for nested validation with multiple fields into target maps 551 | func TestValidatorNestedObjects(t *testing.T) { 552 | t.Run("customer object validation", func(t *testing.T) { 553 | // Create request with customer details 554 | args := map[string]interface{}{ 555 | "customer_name": "John Doe", 556 | "customer_email": "[email protected]", 557 | "customer_contact": "+1234567890", 558 | } 559 | request := &mcpgo.CallToolRequest{ 560 | Arguments: args, 561 | } 562 | 563 | // Customer target map 564 | customer := make(map[string]interface{}) 565 | 566 | // Create validator and validate customer fields 567 | validator := NewValidator(request). 568 | ValidateAndAddOptionalStringToPath(customer, "customer_name", "name"). 569 | ValidateAndAddOptionalStringToPath(customer, "customer_email", "email"). 570 | ValidateAndAddOptionalStringToPath(customer, "customer_contact", "contact") 571 | 572 | // Should not have errors 573 | assert.False(t, validator.HasErrors()) 574 | 575 | // Customer map should have all three fields 576 | assert.Equal(t, "John Doe", customer["name"]) 577 | assert.Equal(t, "[email protected]", customer["email"]) 578 | assert.Equal(t, "+1234567890", customer["contact"]) 579 | }) 580 | 581 | t.Run("notification object validation", func(t *testing.T) { 582 | // Create request with notification settings 583 | args := map[string]interface{}{ 584 | "notify_sms": true, 585 | "notify_email": false, 586 | } 587 | request := &mcpgo.CallToolRequest{ 588 | Arguments: args, 589 | } 590 | 591 | // Notify target map 592 | notify := make(map[string]interface{}) 593 | 594 | // Create validator and validate notification fields 595 | validator := NewValidator(request). 596 | ValidateAndAddOptionalBoolToPath(notify, "notify_sms", "sms"). 597 | ValidateAndAddOptionalBoolToPath(notify, "notify_email", "email") 598 | 599 | // Should not have errors 600 | assert.False(t, validator.HasErrors()) 601 | 602 | // Notify map should have both fields 603 | assert.Equal(t, true, notify["sms"]) 604 | assert.Equal(t, false, notify["email"]) 605 | }) 606 | 607 | t.Run("mixed object with error", func(t *testing.T) { 608 | // Create request with mixed valid and invalid data 609 | args := map[string]interface{}{ 610 | "customer_name": "Jane Doe", 611 | "customer_email": 12345, // Wrong type 612 | } 613 | request := &mcpgo.CallToolRequest{ 614 | Arguments: args, 615 | } 616 | 617 | // Target map 618 | customer := make(map[string]interface{}) 619 | 620 | // Create validator and validate fields 621 | validator := NewValidator(request). 622 | ValidateAndAddOptionalStringToPath(customer, "customer_name", "name"). 623 | ValidateAndAddOptionalStringToPath(customer, "customer_email", "email") 624 | 625 | // Should have errors 626 | assert.True(t, validator.HasErrors()) 627 | 628 | // Customer map should have only the valid field 629 | assert.Equal(t, "Jane Doe", customer["name"]) 630 | _, hasEmail := customer["email"] 631 | assert.False(t, hasEmail, "Invalid field should not be added to target map") 632 | }) 633 | } 634 | 635 | // Test for optional bool handling 636 | func TestOptionalBoolBehavior(t *testing.T) { 637 | t.Run("explicit bool values", func(t *testing.T) { 638 | // Create request with explicit bool values 639 | args := map[string]interface{}{ 640 | "true_param": true, 641 | "false_param": false, 642 | } 643 | request := &mcpgo.CallToolRequest{ 644 | Arguments: args, 645 | } 646 | 647 | // Create result map 648 | result := make(map[string]interface{}) 649 | 650 | // Validate both parameters 651 | validator := NewValidator(request). 652 | ValidateAndAddOptionalBool(result, "true_param"). 653 | ValidateAndAddOptionalBool(result, "false_param") 654 | 655 | // Verify no errors occurred 656 | assert.False(t, validator.HasErrors()) 657 | 658 | // Both parameters should be set in the result 659 | assert.Equal(t, true, result["true_param"]) 660 | assert.Equal(t, false, result["false_param"]) 661 | }) 662 | 663 | t.Run("missing bool parameter", func(t *testing.T) { 664 | // Create request without bool parameters 665 | args := map[string]interface{}{ 666 | "other_param": "some value", 667 | } 668 | request := &mcpgo.CallToolRequest{ 669 | Arguments: args, 670 | } 671 | 672 | // Create result map 673 | result := make(map[string]interface{}) 674 | 675 | // Try to validate missing bool parameters 676 | validator := NewValidator(request). 677 | ValidateAndAddOptionalBool(result, "true_param"). 678 | ValidateAndAddOptionalBool(result, "false_param") 679 | 680 | // Verify no errors occurred 681 | assert.False(t, validator.HasErrors()) 682 | 683 | // Result should be empty since no bool values were provided 684 | assert.Empty(t, result) 685 | }) 686 | 687 | t.Run("explicit bool values with 'To' functions", func(t *testing.T) { 688 | // Create request with explicit bool values 689 | args := map[string]interface{}{ 690 | "notify_sms": true, 691 | "notify_email": false, 692 | } 693 | request := &mcpgo.CallToolRequest{ 694 | Arguments: args, 695 | } 696 | 697 | // Create target map 698 | target := make(map[string]interface{}) 699 | 700 | // Validate both parameters 701 | validator := NewValidator(request). 702 | ValidateAndAddOptionalBoolToPath(target, "notify_sms", "sms"). 703 | ValidateAndAddOptionalBoolToPath(target, "notify_email", "email") 704 | 705 | // Verify no errors occurred 706 | assert.False(t, validator.HasErrors()) 707 | 708 | // Both parameters should be set in the target map 709 | assert.Equal(t, true, target["sms"]) 710 | assert.Equal(t, false, target["email"]) 711 | }) 712 | 713 | t.Run("missing bool parameter with 'To' functions", func(t *testing.T) { 714 | // Create request without bool parameters 715 | args := map[string]interface{}{ 716 | "other_param": "some value", 717 | } 718 | request := &mcpgo.CallToolRequest{ 719 | Arguments: args, 720 | } 721 | 722 | // Create target map 723 | target := make(map[string]interface{}) 724 | 725 | // Try to validate missing bool parameters 726 | validator := NewValidator(request). 727 | ValidateAndAddOptionalBoolToPath(target, "notify_sms", "sms"). 728 | ValidateAndAddOptionalBoolToPath(target, "notify_email", "email") 729 | 730 | // Verify no errors occurred 731 | assert.False(t, validator.HasErrors()) 732 | 733 | // Target map should be empty since no bool values were provided 734 | assert.Empty(t, target) 735 | }) 736 | } 737 | 738 | // Test for extractValueGeneric function edge cases 739 | func TestExtractValueGeneric(t *testing.T) { 740 | t.Run("invalid arguments type", func(t *testing.T) { 741 | request := &mcpgo.CallToolRequest{ 742 | Arguments: "invalid_type", // Not a map 743 | } 744 | 745 | result, err := extractValueGeneric[string](request, "test", false) 746 | assert.Error(t, err) 747 | assert.Equal(t, "invalid arguments type", err.Error()) 748 | assert.Nil(t, result) 749 | }) 750 | 751 | t.Run("json marshal error", func(t *testing.T) { 752 | // Create a value that can't be marshaled to JSON 753 | args := map[string]interface{}{ 754 | "test_param": make(chan int), // Channels can't be marshaled 755 | } 756 | request := &mcpgo.CallToolRequest{ 757 | Arguments: args, 758 | } 759 | 760 | result, err := extractValueGeneric[string](request, "test_param", false) 761 | assert.Error(t, err) 762 | assert.Equal(t, "invalid parameter type: test_param", err.Error()) 763 | assert.Nil(t, result) 764 | }) 765 | 766 | t.Run("json unmarshal error", func(t *testing.T) { 767 | // Provide a value that can't be unmarshaled to the target type 768 | args := map[string]interface{}{ 769 | "test_param": []interface{}{1, 2, 3}, // Array can't be unmarshaled to string 770 | } 771 | request := &mcpgo.CallToolRequest{ 772 | Arguments: args, 773 | } 774 | 775 | result, err := extractValueGeneric[string](request, "test_param", false) 776 | assert.Error(t, err) 777 | assert.Equal(t, "invalid parameter type: test_param", err.Error()) 778 | assert.Nil(t, result) 779 | }) 780 | } 781 | 782 | // Test for validateAndAddRequired function 783 | func TestValidateAndAddRequired(t *testing.T) { 784 | t.Run("successful validation", func(t *testing.T) { 785 | args := map[string]interface{}{ 786 | "test_param": "test_value", 787 | } 788 | request := &mcpgo.CallToolRequest{ 789 | Arguments: args, 790 | } 791 | 792 | params := make(map[string]interface{}) 793 | validator := NewValidator(request) 794 | 795 | result := validateAndAddRequired[string](validator, params, "test_param") 796 | 797 | assert.False(t, result.HasErrors()) 798 | assert.Equal(t, "test_value", params["test_param"]) 799 | }) 800 | 801 | t.Run("validation error", func(t *testing.T) { 802 | request := &mcpgo.CallToolRequest{ 803 | Arguments: "invalid_type", 804 | } 805 | 806 | params := make(map[string]interface{}) 807 | validator := NewValidator(request) 808 | 809 | result := validateAndAddRequired[string](validator, params, "test_param") 810 | 811 | assert.True(t, result.HasErrors()) 812 | assert.Empty(t, params) 813 | }) 814 | 815 | t.Run("nil value after successful extraction", func(t *testing.T) { 816 | // This edge case is hard to trigger directly, but we can simulate it 817 | // by using a type that extractValueGeneric might return as nil 818 | args := map[string]interface{}{ 819 | "test_param": nil, 820 | } 821 | request := &mcpgo.CallToolRequest{ 822 | Arguments: args, 823 | } 824 | 825 | params := make(map[string]interface{}) 826 | validator := NewValidator(request) 827 | 828 | result := validateAndAddRequired[string](validator, params, "test_param") 829 | 830 | // This should result in an error because the parameter is required 831 | assert.True(t, result.HasErrors()) 832 | assert.Empty(t, params) 833 | }) 834 | } 835 | 836 | // Test for validateAndAddOptional function 837 | func TestValidateAndAddOptional(t *testing.T) { 838 | t.Run("successful validation", func(t *testing.T) { 839 | args := map[string]interface{}{ 840 | "test_param": "test_value", 841 | } 842 | request := &mcpgo.CallToolRequest{ 843 | Arguments: args, 844 | } 845 | 846 | params := make(map[string]interface{}) 847 | validator := NewValidator(request) 848 | 849 | result := validateAndAddOptional[string](validator, params, "test_param") 850 | 851 | assert.False(t, result.HasErrors()) 852 | assert.Equal(t, "test_value", params["test_param"]) 853 | }) 854 | 855 | t.Run("validation error", func(t *testing.T) { 856 | request := &mcpgo.CallToolRequest{ 857 | Arguments: "invalid_type", 858 | } 859 | 860 | params := make(map[string]interface{}) 861 | validator := NewValidator(request) 862 | 863 | result := validateAndAddOptional[string](validator, params, "test_param") 864 | 865 | assert.True(t, result.HasErrors()) 866 | assert.Empty(t, params) 867 | }) 868 | 869 | t.Run("nil value handling", func(t *testing.T) { 870 | args := map[string]interface{}{ 871 | "test_param": nil, 872 | } 873 | request := &mcpgo.CallToolRequest{ 874 | Arguments: args, 875 | } 876 | 877 | params := make(map[string]interface{}) 878 | validator := NewValidator(request) 879 | 880 | result := validateAndAddOptional[string](validator, params, "test_param") 881 | 882 | assert.False(t, result.HasErrors()) 883 | assert.Empty(t, params) 884 | }) 885 | } 886 | 887 | // Test for validateAndAddToPath function 888 | func TestValidateAndAddToPath(t *testing.T) { 889 | t.Run("successful validation", func(t *testing.T) { 890 | args := map[string]interface{}{ 891 | "test_param": "test_value", 892 | } 893 | request := &mcpgo.CallToolRequest{ 894 | Arguments: args, 895 | } 896 | 897 | target := make(map[string]interface{}) 898 | validator := NewValidator(request) 899 | 900 | result := validateAndAddToPath[string]( 901 | validator, target, "test_param", "target_key") 902 | 903 | assert.False(t, result.HasErrors()) 904 | assert.Equal(t, "test_value", target["target_key"]) 905 | }) 906 | 907 | t.Run("validation error", func(t *testing.T) { 908 | request := &mcpgo.CallToolRequest{ 909 | Arguments: "invalid_type", 910 | } 911 | 912 | target := make(map[string]interface{}) 913 | validator := NewValidator(request) 914 | 915 | result := validateAndAddToPath[string]( 916 | validator, target, "test_param", "target_key") 917 | 918 | assert.True(t, result.HasErrors()) 919 | assert.Empty(t, target) 920 | }) 921 | 922 | t.Run("nil value handling", func(t *testing.T) { 923 | args := map[string]interface{}{ 924 | "test_param": nil, 925 | } 926 | request := &mcpgo.CallToolRequest{ 927 | Arguments: args, 928 | } 929 | 930 | target := make(map[string]interface{}) 931 | validator := NewValidator(request) 932 | 933 | result := validateAndAddToPath[string]( 934 | validator, target, "test_param", "target_key") 935 | 936 | assert.False(t, result.HasErrors()) 937 | assert.Empty(t, target) 938 | }) 939 | } 940 | 941 | // Test for ValidateAndAddPagination function 942 | func TestValidateAndAddPagination(t *testing.T) { 943 | t.Run("all pagination parameters", func(t *testing.T) { 944 | args := map[string]interface{}{ 945 | "count": 10, 946 | "skip": 5, 947 | } 948 | request := &mcpgo.CallToolRequest{ 949 | Arguments: args, 950 | } 951 | 952 | params := make(map[string]interface{}) 953 | validator := NewValidator(request).ValidateAndAddPagination(params) 954 | 955 | assert.False(t, validator.HasErrors()) 956 | assert.Equal(t, int64(10), params["count"]) 957 | assert.Equal(t, int64(5), params["skip"]) 958 | }) 959 | 960 | t.Run("missing pagination parameters", func(t *testing.T) { 961 | args := map[string]interface{}{} 962 | request := &mcpgo.CallToolRequest{ 963 | Arguments: args, 964 | } 965 | 966 | params := make(map[string]interface{}) 967 | validator := NewValidator(request).ValidateAndAddPagination(params) 968 | 969 | assert.False(t, validator.HasErrors()) 970 | assert.Empty(t, params) 971 | }) 972 | 973 | t.Run("invalid count type", func(t *testing.T) { 974 | args := map[string]interface{}{ 975 | "count": "invalid", 976 | } 977 | request := &mcpgo.CallToolRequest{ 978 | Arguments: args, 979 | } 980 | 981 | params := make(map[string]interface{}) 982 | validator := NewValidator(request).ValidateAndAddPagination(params) 983 | 984 | assert.True(t, validator.HasErrors()) 985 | }) 986 | } 987 | 988 | // Test for ValidateAndAddExpand function 989 | func TestValidateAndAddExpand(t *testing.T) { 990 | t.Run("valid expand parameter", func(t *testing.T) { 991 | args := map[string]interface{}{ 992 | "expand": []string{"payments", "customer"}, 993 | } 994 | request := &mcpgo.CallToolRequest{ 995 | Arguments: args, 996 | } 997 | 998 | params := make(map[string]interface{}) 999 | validator := NewValidator(request).ValidateAndAddExpand(params) 1000 | 1001 | assert.False(t, validator.HasErrors()) 1002 | // The function sets expand[] for each value, so check the last one 1003 | assert.Equal(t, "customer", params["expand[]"]) 1004 | }) 1005 | 1006 | t.Run("missing expand parameter", func(t *testing.T) { 1007 | args := map[string]interface{}{} 1008 | request := &mcpgo.CallToolRequest{ 1009 | Arguments: args, 1010 | } 1011 | 1012 | params := make(map[string]interface{}) 1013 | validator := NewValidator(request).ValidateAndAddExpand(params) 1014 | 1015 | assert.False(t, validator.HasErrors()) 1016 | assert.Empty(t, params) 1017 | }) 1018 | 1019 | t.Run("invalid expand type", func(t *testing.T) { 1020 | args := map[string]interface{}{ 1021 | "expand": "invalid", // Should be []string, not string 1022 | } 1023 | request := &mcpgo.CallToolRequest{ 1024 | Arguments: args, 1025 | } 1026 | 1027 | params := make(map[string]interface{}) 1028 | validator := NewValidator(request).ValidateAndAddExpand(params) 1029 | 1030 | assert.True(t, validator.HasErrors()) 1031 | }) 1032 | } 1033 | 1034 | // Test for token validation functions edge cases 1035 | func TestTokenValidationEdgeCases(t *testing.T) { 1036 | t.Run("validateTokenMaxAmount - int conversion", func(t *testing.T) { 1037 | token := map[string]interface{}{ 1038 | "max_amount": 100, // int instead of float64 1039 | } 1040 | 1041 | request := &mcpgo.CallToolRequest{Arguments: map[string]interface{}{}} 1042 | validator := NewValidator(request).validateTokenMaxAmount(token) 1043 | 1044 | assert.False(t, validator.HasErrors()) 1045 | assert.Equal(t, float64(100), token["max_amount"]) 1046 | }) 1047 | 1048 | t.Run("validateTokenExpireAt - int conversion", func(t *testing.T) { 1049 | token := map[string]interface{}{ 1050 | "expire_at": 1234567890, // int instead of float64 1051 | } 1052 | 1053 | request := &mcpgo.CallToolRequest{Arguments: map[string]interface{}{}} 1054 | validator := NewValidator(request).validateTokenExpireAt(token) 1055 | 1056 | assert.False(t, validator.HasErrors()) 1057 | assert.Equal(t, float64(1234567890), token["expire_at"]) 1058 | }) 1059 | 1060 | t.Run("validateTokenExpireAt - zero value", func(t *testing.T) { 1061 | token := map[string]interface{}{ 1062 | "expire_at": 0, 1063 | } 1064 | 1065 | request := &mcpgo.CallToolRequest{Arguments: map[string]interface{}{}} 1066 | validator := NewValidator(request).validateTokenExpireAt(token) 1067 | 1068 | assert.True(t, validator.HasErrors()) 1069 | }) 1070 | 1071 | t.Run("validateTokenMaxAmount - zero value", func(t *testing.T) { 1072 | token := map[string]interface{}{ 1073 | "max_amount": 0, 1074 | } 1075 | 1076 | request := &mcpgo.CallToolRequest{Arguments: map[string]interface{}{}} 1077 | validator := NewValidator(request).validateTokenMaxAmount(token) 1078 | 1079 | assert.True(t, validator.HasErrors()) 1080 | }) 1081 | } 1082 | 1083 | // Test for ValidateAndAddToken edge cases 1084 | func TestValidateAndAddTokenEdgeCases(t *testing.T) { 1085 | t.Run("token extraction error", func(t *testing.T) { 1086 | request := &mcpgo.CallToolRequest{ 1087 | Arguments: "invalid_type", 1088 | } 1089 | 1090 | params := make(map[string]interface{}) 1091 | validator := NewValidator(request).ValidateAndAddToken(params, "token") 1092 | 1093 | assert.True(t, validator.HasErrors()) 1094 | assert.Empty(t, params) 1095 | }) 1096 | 1097 | t.Run("nil token value", func(t *testing.T) { 1098 | args := map[string]interface{}{ 1099 | "token": nil, 1100 | } 1101 | request := &mcpgo.CallToolRequest{ 1102 | Arguments: args, 1103 | } 1104 | 1105 | params := make(map[string]interface{}) 1106 | validator := NewValidator(request).ValidateAndAddToken(params, "token") 1107 | 1108 | assert.False(t, validator.HasErrors()) 1109 | assert.Empty(t, params) 1110 | }) 1111 | 1112 | t.Run("token validation errors", func(t *testing.T) { 1113 | args := map[string]interface{}{ 1114 | "token": map[string]interface{}{ 1115 | "max_amount": -100, // Invalid value 1116 | }, 1117 | } 1118 | request := &mcpgo.CallToolRequest{ 1119 | Arguments: args, 1120 | } 1121 | 1122 | params := make(map[string]interface{}) 1123 | validator := NewValidator(request).ValidateAndAddToken(params, "token") 1124 | 1125 | assert.True(t, validator.HasErrors()) 1126 | assert.Empty(t, params) 1127 | }) 1128 | } 1129 | ```