From a0c8e6619e2b0628f8e409c5db3a4b50d933c771 Mon Sep 17 00:00:00 2001 From: Leo Date: Tue, 28 Oct 2025 10:35:08 +0800 Subject: [PATCH 1/2] chore: format code --- cmd/mcp-gateway/main_more_test.go | 2 +- cmd/mock-server/backend/http-server.go | 2555 +++++++++++++++++ .../backend/http-server_routes_test.go | 117 +- .../backend/mcp-server_more_test.go | 62 +- .../apiserver/handler/auth_missing_test.go | 4 +- internal/core/auth_authorize_test.go | 134 +- internal/core/handler_chain_test.go | 2 +- internal/core/handler_test.go | 2 +- .../core/mcpproxy/transports_early_test.go | 84 +- internal/core/server_options_shutdown_test.go | 41 +- internal/core/sse_message_post_test.go | 174 +- internal/core/state/state_build_test.go | 95 +- internal/core/tool_test.go | 1 - .../mcp/storage/db_list_deleteversion_test.go | 123 +- internal/mcp/storage/factory_test.go | 44 +- internal/mcp/storage/notifier/factory_test.go | 76 +- pkg/trace/trace.go | 64 +- pkg/trace/trace_additional_test.go | 2 +- pkg/trace/trace_test.go | 74 +- 19 files changed, 3125 insertions(+), 531 deletions(-) diff --git a/cmd/mcp-gateway/main_more_test.go b/cmd/mcp-gateway/main_more_test.go index c0fc2f56..1e714521 100644 --- a/cmd/mcp-gateway/main_more_test.go +++ b/cmd/mcp-gateway/main_more_test.go @@ -200,4 +200,4 @@ func TestCommandTimeout(t *testing.T) { case <-time.After(timeout): t.Error("command took too long to complete") } -} \ No newline at end of file +} diff --git a/cmd/mock-server/backend/http-server.go b/cmd/mock-server/backend/http-server.go index 9dd80f3c..e4a0fa88 100644 --- a/cmd/mock-server/backend/http-server.go +++ b/cmd/mock-server/backend/http-server.go @@ -140,6 +140,2561 @@ func NewHTTPServer() *HTTPServer { }) }) + // Custom JSON response endpoint - returns predefined JSON + router.POST("/custom/json", func(c *gin.Context) { + // You can paste your custom JSON string here + customJSONString := `{ + "request_id": "7069a2bf-aa3f-4881-8420-d8551dcffbbb", + "code": 0, + "data": + { + "fields": + [ + "ts_code", + "trade_date", + "open", + "high", + "low", + "close", + "pre_close", + "change", + "pct_chg", + "vol", + "amount" + ], + "items": + [ + [ + "600519.SH", + "20251023", + 1455.0, + 1468.8, + 1447.2, + 1467.98, + 1458.7, + 9.28, + 0.6362, + 29322.35, + 4274652.09 + ], + [ + "600519.SH", + "20251022", + 1462.08, + 1465.73, + 1456.0, + 1458.7, + 1462.26, + -3.56, + -0.2435, + 18194.92, + 2657486.636 + ], + [ + "600519.SH", + "20251021", + 1459.0, + 1469.94, + 1455.5, + 1462.26, + 1457.93, + 4.33, + 0.297, + 25442.67, + 3727984.13 + ], + [ + "600519.SH", + "20251020", + 1455.0, + 1469.5, + 1454.88, + 1457.93, + 1455.0, + 2.93, + 0.2014, + 25949.88, + 3793283.759 + ], + [ + "600519.SH", + "20251017", + 1483.1, + 1488.0, + 1454.03, + 1455.0, + 1484.91, + -29.91, + -2.0143, + 38085.89, + 5594724.9 + ], + [ + "600519.SH", + "20251016", + 1461.92, + 1484.95, + 1458.88, + 1484.91, + 1462.0, + 22.91, + 1.567, + 45730.15, + 6749162.949 + ], + [ + "600519.SH", + "20251015", + 1450.98, + 1463.0, + 1445.08, + 1462.0, + 1451.02, + 10.98, + 0.7567, + 42785.4, + 6233407.482 + ], + [ + "600519.SH", + "20251014", + 1429.99, + 1464.0, + 1429.99, + 1451.02, + 1419.2, + 31.82, + 2.2421, + 66672.33, + 9639484.605 + ], + [ + "600519.SH", + "20251013", + 1415.7, + 1422.85, + 1415.12, + 1419.2, + 1430.0, + -10.8, + -0.7552, + 46068.8, + 6533147.458 + ], + [ + "600519.SH", + "20251010", + 1437.6, + 1439.94, + 1427.5, + 1430.0, + 1436.78, + -6.78, + -0.4719, + 36001.44, + 5157459.262 + ], + [ + "600519.SH", + "20251009", + 1436.0, + 1439.38, + 1420.0, + 1436.78, + 1443.99, + -7.21, + -0.4993, + 54918.12, + 7845448.947 + ], + [ + "600519.SH", + "20250930", + 1460.0, + 1460.76, + 1440.0, + 1443.99, + 1460.86, + -16.87, + -1.1548, + 39363.62, + 5687474.876 + ], + [ + "600519.SH", + "20250929", + 1439.38, + 1469.99, + 1435.0, + 1460.86, + 1435.0, + 25.86, + 1.8021, + 53684.03, + 7798434.586 + ], + [ + "600519.SH", + "20250926", + 1441.18, + 1447.11, + 1428.01, + 1435.0, + 1439.0, + -4.0, + -0.278, + 45120.71, + 6480889.597 + ], + [ + "600519.SH", + "20250925", + 1442.83, + 1445.21, + 1436.0, + 1439.0, + 1442.0, + -3.0, + -0.208, + 33869.73, + 4873274.661 + ], + [ + "600519.SH", + "20250924", + 1434.07, + 1456.78, + 1434.07, + 1442.0, + 1447.42, + -5.42, + -0.3745, + 30743.5, + 4443052.872 + ], + [ + "600519.SH", + "20250923", + 1450.5, + 1457.5, + 1440.0, + 1447.42, + 1453.35, + -5.93, + -0.408, + 38663.0, + 5588384.388 + ], + [ + "600519.SH", + "20250922", + 1465.09, + 1467.97, + 1450.01, + 1453.35, + 1467.97, + -14.62, + -0.9959, + 34947.08, + 5085057.536 + ], + [ + "600519.SH", + "20250919", + 1467.99, + 1475.5, + 1457.01, + 1467.97, + 1467.96, + 0.01, + 0.0007, + 32636.62, + 4784528.851 + ], + [ + "600519.SH", + "20250918", + 1492.0, + 1497.8, + 1463.5, + 1467.96, + 1493.0, + -25.04, + -1.6772, + 49721.25, + 7347477.479 + ], + [ + "600519.SH", + "20250917", + 1499.99, + 1510.28, + 1490.01, + 1493.0, + 1499.98, + -6.98, + -0.4653, + 30330.54, + 4534770.634 + ], + [ + "600519.SH", + "20250916", + 1515.1, + 1520.99, + 1496.21, + 1499.98, + 1515.1, + -15.12, + -0.998, + 32717.89, + 4928374.478 + ], + [ + "600519.SH", + "20250915", + 1515.87, + 1517.48, + 1501.5, + 1515.1, + 1516.0, + -0.9, + -0.0594, + 25827.03, + 3901929.718 + ], + [ + "600519.SH", + "20250912", + 1526.0, + 1538.02, + 1510.53, + 1516.0, + 1523.5, + -7.5, + -0.4923, + 33722.09, + 5132184.935 + ], + [ + "600519.SH", + "20250911", + 1522.01, + 1526.02, + 1508.5, + 1523.5, + 1522.01, + 1.49, + 0.0979, + 37238.96, + 5642778.456 + ], + [ + "600519.SH", + "20250910", + 1506.66, + 1529.95, + 1496.0, + 1522.01, + 1505.0, + 17.01, + 1.1302, + 49663.31, + 7541460.049 + ], + [ + "600519.SH", + "20250909", + 1505.0, + 1509.95, + 1493.42, + 1505.0, + 1501.23, + 3.77, + 0.2511, + 35742.77, + 5366650.005 + ], + [ + "600519.SH", + "20250908", + 1483.0, + 1506.44, + 1477.5, + 1501.23, + 1483.0, + 18.23, + 1.2293, + 51382.98, + 7682065.696 + ], + [ + "600519.SH", + "20250905", + 1471.0, + 1486.97, + 1464.0, + 1483.0, + 1472.66, + 10.34, + 0.7021, + 37388.48, + 5519312.163 + ], + [ + "600519.SH", + "20250904", + 1472.0, + 1479.3, + 1460.47, + 1472.66, + 1480.55, + -7.89, + -0.5329, + 47746.76, + 7017084.75 + ], + [ + "600519.SH", + "20250903", + 1491.0, + 1503.5, + 1466.0, + 1480.55, + 1491.3, + -10.75, + -0.7208, + 45045.03, + 6656011.145 + ], + [ + "600519.SH", + "20250902", + 1478.66, + 1509.0, + 1478.0, + 1491.3, + 1476.1, + 15.2, + 1.0297, + 56688.37, + 8470012.816 + ], + [ + "600519.SH", + "20250901", + 1482.2, + 1488.0, + 1465.7, + 1476.1, + 1480.0, + -3.9, + -0.2635, + 45123.38, + 6658918.555 + ], + [ + "600519.SH", + "20250829", + 1453.0, + 1482.58, + 1452.0, + 1480.0, + 1446.1, + 33.9, + 2.3442, + 62256.48, + 9159701.454 + ], + [ + "600519.SH", + "20250828", + 1447.97, + 1456.1, + 1438.77, + 1446.1, + 1448.0, + -1.9, + -0.1312, + 39281.77, + 5683673.273 + ], + [ + "600519.SH", + "20250827", + 1481.88, + 1484.93, + 1448.0, + 1448.0, + 1481.61, + -33.61, + -2.2685, + 56006.09, + 8184169.113 + ], + [ + "600519.SH", + "20250826", + 1490.32, + 1494.23, + 1480.01, + 1481.61, + 1490.33, + -8.72, + -0.5851, + 39602.13, + 5883917.0 + ], + [ + "600519.SH", + "20250825", + 1470.01, + 1496.0, + 1466.0, + 1490.33, + 1463.95, + 26.38, + 1.802, + 65324.55, + 9676719.498 + ], + [ + "600519.SH", + "20250822", + 1448.88, + 1464.0, + 1444.77, + 1463.95, + 1448.25, + 15.7, + 1.0841, + 44970.58, + 6544867.368 + ], + [ + "600519.SH", + "20250821", + 1453.45, + 1454.99, + 1443.65, + 1448.25, + 1450.0, + -1.75, + -0.1207, + 30890.57, + 4470441.662 + ], + [ + "600519.SH", + "20250820", + 1438.0, + 1452.8, + 1430.02, + 1450.0, + 1438.0, + 12.0, + 0.8345, + 45804.66, + 6623095.725 + ], + [ + "600519.SH", + "20250819", + 1433.5, + 1446.66, + 1432.0, + 1438.0, + 1428.5, + 9.5, + 0.665, + 46371.86, + 6678541.001 + ], + [ + "600519.SH", + "20250818", + 1426.99, + 1436.64, + 1423.1, + 1428.5, + 1422.08, + 6.42, + 0.4515, + 47377.98, + 6773477.871 + ], + [ + "600519.SH", + "20250815", + 1426.01, + 1428.66, + 1420.22, + 1422.08, + 1426.99, + -4.91, + -0.3441, + 47581.65, + 6772436.997 + ], + [ + "600519.SH", + "20250814", + 1420.94, + 1447.51, + 1420.94, + 1426.99, + 1420.05, + 6.94, + 0.4887, + 48129.3, + 6897535.33 + ], + [ + "600519.SH", + "20250813", + 1425.0, + 1433.68, + 1420.0, + 1420.05, + 1437.04, + -16.99, + -1.1823, + 65527.54, + 9325460.101 + ], + [ + "600519.SH", + "20250812", + 1449.0, + 1465.07, + 1436.0, + 1437.04, + 1445.0, + -7.96, + -0.5509, + 42019.23, + 6095342.036 + ], + [ + "600519.SH", + "20250811", + 1423.5, + 1451.99, + 1423.0, + 1445.0, + 1420.97, + 24.03, + 1.6911, + 47159.07, + 6794034.09 + ], + [ + "600519.SH", + "20250808", + 1423.05, + 1426.5, + 1418.0, + 1420.97, + 1422.35, + -1.38, + -0.097, + 18655.28, + 2651478.118 + ], + [ + "600519.SH", + "20250807", + 1423.88, + 1427.7, + 1420.06, + 1422.35, + 1423.88, + -1.53, + -0.1075, + 28001.34, + 3988765.795 + ], + [ + "600519.SH", + "20250806", + 1429.0, + 1429.0, + 1419.3, + 1423.88, + 1427.74, + -3.86, + -0.2704, + 23699.28, + 3372334.94 + ], + [ + "600519.SH", + "20250805", + 1421.0, + 1429.94, + 1417.11, + 1427.74, + 1419.0, + 8.74, + 0.6159, + 25342.27, + 3607931.488 + ], + [ + "600519.SH", + "20250804", + 1415.0, + 1419.8, + 1414.0, + 1419.0, + 1417.0, + 2.0, + 0.1411, + 18687.95, + 2647105.74 + ], + [ + "600519.SH", + "20250801", + 1421.87, + 1425.96, + 1414.0, + 1417.0, + 1421.67, + -4.67, + -0.3285, + 29635.59, + 4200850.79 + ], + [ + "600519.SH", + "20250731", + 1443.0, + 1444.87, + 1418.0, + 1421.67, + 1449.44, + -27.77, + -1.9159, + 51797.51, + 7386302.758 + ], + [ + "600519.SH", + "20250730", + 1444.0, + 1457.16, + 1437.0, + 1449.44, + 1439.0, + 10.44, + 0.7255, + 31867.71, + 4612883.04 + ], + [ + "600519.SH", + "20250729", + 1439.6, + 1448.79, + 1435.01, + 1439.0, + 1438.66, + 0.34, + 0.0236, + 26304.08, + 3785848.102 + ], + [ + "600519.SH", + "20250728", + 1453.0, + 1454.99, + 1436.3, + 1438.66, + 1455.0, + -16.34, + -1.123, + 38586.32, + 5560966.386 + ], + [ + "600519.SH", + "20250725", + 1491.4, + 1491.4, + 1453.0, + 1455.0, + 1491.5, + -36.5, + -2.4472, + 41911.26, + 6140078.741 + ], + [ + "600519.SH", + "20250724", + 1476.0, + 1499.0, + 1475.0, + 1491.5, + 1475.5, + 16.0, + 1.0844, + 38803.64, + 5786337.377 + ], + [ + "600519.SH", + "20250723", + 1469.0, + 1495.0, + 1468.95, + 1475.5, + 1464.98, + 10.52, + 0.7181, + 45044.84, + 6676301.462 + ], + [ + "600519.SH", + "20250722", + 1444.0, + 1466.6, + 1440.68, + 1464.98, + 1443.0, + 21.98, + 1.5232, + 42118.98, + 6127636.851 + ], + [ + "600519.SH", + "20250721", + 1436.99, + 1446.8, + 1435.0, + 1443.0, + 1437.0, + 6.0, + 0.4175, + 26203.31, + 3779891.829 + ], + [ + "600519.SH", + "20250718", + 1420.0, + 1437.81, + 1416.38, + 1437.0, + 1416.35, + 20.65, + 1.458, + 41823.41, + 5984949.774 + ], + [ + "600519.SH", + "20250717", + 1413.98, + 1423.01, + 1412.88, + 1416.35, + 1411.03, + 5.32, + 0.377, + 23105.54, + 3273100.406 + ], + [ + "600519.SH", + "20250716", + 1410.01, + 1417.49, + 1409.95, + 1411.03, + 1411.0, + 0.03, + 0.0021, + 21286.05, + 3006882.579 + ], + [ + "600519.SH", + "20250715", + 1420.98, + 1422.93, + 1408.32, + 1411.0, + 1423.6, + -12.6, + -0.8851, + 35405.78, + 5005150.165 + ], + [ + "600519.SH", + "20250714", + 1430.0, + 1434.98, + 1421.6, + 1423.6, + 1427.0, + -3.4, + -0.2383, + 27780.94, + 3961736.006 + ], + [ + "600519.SH", + "20250711", + 1423.16, + 1458.0, + 1423.16, + 1427.0, + 1426.5, + 0.5, + 0.0351, + 57243.35, + 8239010.39 + ], + [ + "600519.SH", + "20250710", + 1418.97, + 1436.18, + 1410.4, + 1426.5, + 1418.88, + 7.62, + 0.537, + 38725.03, + 5503329.754 + ], + [ + "600519.SH", + "20250709", + 1415.0, + 1429.97, + 1414.17, + 1418.88, + 1416.11, + 2.77, + 0.1956, + 27745.32, + 3950810.723 + ], + [ + "600519.SH", + "20250708", + 1410.71, + 1420.0, + 1410.7, + 1416.11, + 1410.7, + 5.41, + 0.3835, + 18673.7, + 2643302.691 + ], + [ + "600519.SH", + "20250707", + 1422.28, + 1423.5, + 1410.01, + 1410.7, + 1422.22, + -11.52, + -0.81, + 23854.67, + 3370562.659 + ], + [ + "600519.SH", + "20250704", + 1415.7, + 1431.89, + 1410.01, + 1422.22, + 1415.6, + 6.62, + 0.4676, + 28766.91, + 4086547.268 + ], + [ + "600519.SH", + "20250703", + 1412.0, + 1422.69, + 1408.0, + 1415.6, + 1409.6, + 6.0, + 0.4257, + 24412.67, + 3456733.002 + ], + [ + "600519.SH", + "20250702", + 1409.5, + 1414.8, + 1400.0, + 1409.6, + 1405.1, + 4.5, + 0.3203, + 26218.25, + 3689246.316 + ], + [ + "600519.SH", + "20250701", + 1409.0, + 1411.94, + 1403.31, + 1405.1, + 1409.52, + -4.42, + -0.3136, + 19878.19, + 2794423.203 + ], + [ + "600519.SH", + "20250630", + 1403.5, + 1413.23, + 1402.0, + 1409.52, + 1403.09, + 6.43, + 0.4583, + 30457.3, + 4287373.691 + ], + [ + "600519.SH", + "20250627", + 1420.01, + 1423.0, + 1403.09, + 1403.09, + 1420.0, + -16.91, + -1.1908, + 38249.73, + 5404312.675 + ], + [ + "600519.SH", + "20250626", + 1415.0, + 1428.0, + 1410.08, + 1420.0, + 1408.26, + 11.74, + 0.8337, + 34860.25, + 4955829.982 + ], + [ + "600519.SH", + "20250625", + 1439.11, + 1449.2, + 1420.51, + 1435.86, + 1437.2, + -1.34, + -0.0932, + 41050.07, + 5869292.227 + ], + [ + "600519.SH", + "20250624", + 1423.35, + 1451.68, + 1423.35, + 1437.2, + 1420.0, + 17.2, + 1.2113, + 36311.51, + 5229183.514 + ], + [ + "600519.SH", + "20250623", + 1420.0, + 1433.0, + 1405.18, + 1420.0, + 1428.66, + -8.66, + -0.6062, + 26723.31, + 3794012.343 + ], + [ + "600519.SH", + "20250620", + 1423.58, + 1441.14, + 1420.2, + 1428.66, + 1426.0, + 2.66, + 0.1865, + 34815.81, + 4990683.314 + ], + [ + "600519.SH", + "20250619", + 1426.0, + 1433.67, + 1417.18, + 1426.0, + 1425.0, + 1.0, + 0.0702, + 24500.72, + 3490268.568 + ], + [ + "600519.SH", + "20250618", + 1440.0, + 1443.44, + 1421.01, + 1425.0, + 1427.0, + -2.0, + -0.1402, + 30760.99, + 4404131.364 + ], + [ + "600519.SH", + "20250617", + 1420.0, + 1427.0, + 1412.18, + 1427.0, + 1422.29, + 4.71, + 0.3312, + 27231.51, + 3868364.671 + ], + [ + "600519.SH", + "20250616", + 1401.2, + 1425.04, + 1401.18, + 1422.29, + 1426.95, + -4.66, + -0.3266, + 42887.87, + 6060448.438 + ], + [ + "600519.SH", + "20250613", + 1444.41, + 1449.97, + 1425.06, + 1426.95, + 1459.0, + -32.05, + -2.1967, + 59113.25, + 8466515.559 + ], + [ + "600519.SH", + "20250612", + 1480.0, + 1483.87, + 1454.1, + 1459.0, + 1480.0, + -21.0, + -1.4189, + 49903.61, + 7306800.8 + ], + [ + "600519.SH", + "20250611", + 1476.8, + 1494.0, + 1476.1, + 1480.0, + 1475.01, + 4.99, + 0.3383, + 30892.82, + 4585522.755 + ], + [ + "600519.SH", + "20250610", + 1486.0, + 1492.0, + 1474.8, + 1475.01, + 1486.11, + -11.1, + -0.7469, + 30492.2, + 4511934.584 + ], + [ + "600519.SH", + "20250609", + 1505.02, + 1508.7, + 1485.68, + 1486.11, + 1506.39, + -20.28, + -1.3463, + 44958.67, + 6723839.138 + ], + [ + "600519.SH", + "20250606", + 1513.8, + 1518.97, + 1503.0, + 1506.39, + 1514.0, + -7.61, + -0.5026, + 25023.07, + 3777727.616 + ], + [ + "600519.SH", + "20250605", + 1514.0, + 1516.0, + 1502.2, + 1514.0, + 1509.96, + 4.04, + 0.2676, + 23213.66, + 3504925.524 + ], + [ + "600519.SH", + "20250604", + 1510.5, + 1523.0, + 1509.22, + 1509.96, + 1509.0, + 0.96, + 0.0636, + 23008.09, + 3480363.797 + ], + [ + "600519.SH", + "20250603", + 1508.01, + 1519.0, + 1505.02, + 1509.0, + 1522.0, + -13.0, + -0.8541, + 28979.18, + 4377892.799 + ], + [ + "600519.SH", + "20250530", + 1540.15, + 1545.0, + 1515.24, + 1522.0, + 1540.0, + -18.0, + -1.1688, + 31239.18, + 4764875.947 + ], + [ + "600519.SH", + "20250529", + 1540.0, + 1555.0, + 1532.0, + 1540.0, + 1537.0, + 3.0, + 0.1952, + 21859.58, + 3375465.796 + ], + [ + "600519.SH", + "20250528", + 1543.99, + 1546.46, + 1533.0, + 1537.0, + 1543.9, + -6.9, + -0.4469, + 16265.68, + 2504624.605 + ], + [ + "600519.SH", + "20250527", + 1551.0, + 1558.6, + 1543.51, + 1543.9, + 1550.5, + -6.6, + -0.4257, + 17752.13, + 2748945.675 + ], + [ + "600519.SH", + "20250526", + 1570.0, + 1574.63, + 1545.0, + 1550.5, + 1572.6, + -22.1, + -1.4053, + 27086.53, + 4212944.43 + ], + [ + "600519.SH", + "20250523", + 1575.2, + 1587.9, + 1571.66, + 1572.6, + 1580.0, + -7.4, + -0.4684, + 21537.0, + 3399424.961 + ], + [ + "600519.SH", + "20250522", + 1580.99, + 1584.99, + 1570.1, + 1580.0, + 1580.97, + -0.97, + -0.0614, + 15090.24, + 2381236.586 + ], + [ + "600519.SH", + "20250521", + 1585.0, + 1595.94, + 1580.97, + 1580.97, + 1586.0, + -5.03, + -0.3172, + 19794.64, + 3143941.416 + ], + [ + "600519.SH", + "20250520", + 1580.0, + 1594.8, + 1575.1, + 1586.0, + 1578.98, + 7.02, + 0.4446, + 19521.53, + 3095742.637 + ], + [ + "600519.SH", + "20250519", + 1596.0, + 1600.0, + 1573.88, + 1578.98, + 1614.13, + -35.15, + -2.1776, + 38062.83, + 6021950.01 + ], + [ + "600519.SH", + "20250516", + 1633.99, + 1636.99, + 1614.13, + 1614.13, + 1632.01, + -17.88, + -1.0956, + 22935.22, + 3714508.659 + ], + [ + "600519.SH", + "20250515", + 1634.8, + 1643.59, + 1624.13, + 1632.01, + 1634.99, + -2.98, + -0.1823, + 24732.85, + 4043402.636 + ], + [ + "600519.SH", + "20250514", + 1590.0, + 1645.0, + 1588.18, + 1634.99, + 1590.3, + 44.69, + 2.8102, + 39460.12, + 6394735.058 + ], + [ + "600519.SH", + "20250513", + 1608.92, + 1608.92, + 1585.11, + 1590.3, + 1604.5, + -14.2, + -0.885, + 21258.29, + 3386617.848 + ], + [ + "600519.SH", + "20250512", + 1598.0, + 1618.93, + 1596.61, + 1604.5, + 1591.18, + 13.32, + 0.8371, + 24735.33, + 3967785.8 + ], + [ + "600519.SH", + "20250509", + 1578.99, + 1597.45, + 1575.05, + 1591.18, + 1578.19, + 12.99, + 0.8231, + 23671.9, + 3757574.546 + ], + [ + "600519.SH", + "20250508", + 1553.0, + 1592.78, + 1549.83, + 1578.19, + 1555.0, + 23.19, + 1.4913, + 33481.06, + 5265446.444 + ], + [ + "600519.SH", + "20250507", + 1570.0, + 1570.0, + 1550.2, + 1555.0, + 1550.2, + 4.8, + 0.3096, + 27462.21, + 4275142.569 + ], + [ + "600519.SH", + "20250506", + 1559.0, + 1559.0, + 1544.33, + 1550.2, + 1547.0, + 3.2, + 0.2069, + 18300.26, + 2838614.215 + ], + [ + "600519.SH", + "20250430", + 1549.99, + 1566.66, + 1546.3, + 1547.0, + 1544.0, + 3.0, + 0.1943, + 25754.25, + 4005439.429 + ], + [ + "600519.SH", + "20250429", + 1550.0, + 1552.54, + 1532.02, + 1544.0, + 1550.0, + -6.0, + -0.3871, + 18921.8, + 2917422.938 + ], + [ + "600519.SH", + "20250428", + 1552.0, + 1555.0, + 1546.6, + 1550.0, + 1550.0, + 0.0, + 0.0, + 14659.72, + 2274252.465 + ], + [ + "600519.SH", + "20250425", + 1557.1, + 1561.2, + 1550.0, + 1550.0, + 1552.25, + -2.25, + -0.145, + 14764.9, + 2295029.705 + ], + [ + "600519.SH", + "20250424", + 1552.0, + 1560.68, + 1548.98, + 1552.25, + 1552.0, + 0.25, + 0.0161, + 14871.81, + 2313372.072 + ], + [ + "600519.SH", + "20250423", + 1559.0, + 1559.22, + 1545.0, + 1552.0, + 1548.8, + 3.2, + 0.2066, + 18668.76, + 2895468.892 + ], + [ + "600519.SH", + "20250422", + 1550.0, + 1556.3, + 1543.21, + 1548.8, + 1551.0, + -2.2, + -0.1418, + 18432.14, + 2857525.967 + ], + [ + "600519.SH", + "20250421", + 1565.5, + 1565.5, + 1551.0, + 1551.0, + 1565.94, + -14.94, + -0.9541, + 18057.03, + 2808158.177 + ], + [ + "600519.SH", + "20250418", + 1566.0, + 1575.0, + 1556.0, + 1565.94, + 1570.0, + -4.06, + -0.2586, + 20298.48, + 3179974.34 + ], + [ + "600519.SH", + "20250417", + 1554.0, + 1576.5, + 1549.99, + 1570.0, + 1559.17, + 10.83, + 0.6946, + 23846.05, + 3733924.973 + ], + [ + "600519.SH", + "20250416", + 1552.0, + 1576.0, + 1537.0, + 1559.17, + 1558.0, + 1.17, + 0.0751, + 31156.05, + 4834880.596 + ], + [ + "600519.SH", + "20250415", + 1552.0, + 1565.0, + 1545.0, + 1558.0, + 1551.99, + 6.01, + 0.3872, + 21489.28, + 3339942.729 + ], + [ + "600519.SH", + "20250414", + 1560.97, + 1566.0, + 1551.53, + 1551.99, + 1568.98, + -16.99, + -1.0829, + 21711.44, + 3379425.64 + ], + [ + "600519.SH", + "20250411", + 1579.97, + 1579.97, + 1545.0, + 1568.98, + 1549.0, + 19.98, + 1.2899, + 32634.23, + 5087359.924 + ], + [ + "600519.SH", + "20250410", + 1550.99, + 1555.0, + 1528.0, + 1549.0, + 1541.04, + 7.96, + 0.5165, + 38620.21, + 5963650.791 + ], + [ + "600519.SH", + "20250409", + 1525.02, + 1556.8, + 1520.55, + 1541.04, + 1545.0, + -3.96, + -0.2563, + 55630.04, + 8562928.225 + ], + [ + "600519.SH", + "20250408", + 1513.0, + 1545.0, + 1495.0, + 1545.0, + 1500.0, + 45.0, + 3.0, + 73876.49, + 11226977.557 + ], + [ + "600519.SH", + "20250407", + 1520.01, + 1536.85, + 1462.0, + 1500.0, + 1568.88, + -68.88, + -4.3904, + 101952.67, + 15314738.235 + ], + [ + "600519.SH", + "20250403", + 1530.0, + 1586.0, + 1529.01, + 1568.88, + 1549.02, + 19.86, + 1.2821, + 35480.5, + 5557442.052 + ], + [ + "600519.SH", + "20250402", + 1558.0, + 1567.8, + 1545.5, + 1549.02, + 1556.02, + -7.0, + -0.4499, + 21674.01, + 3371883.024 + ], + [ + "600519.SH", + "20250401", + 1564.0, + 1569.89, + 1552.21, + 1556.02, + 1561.0, + -4.98, + -0.319, + 18556.44, + 2893739.955 + ], + [ + "600519.SH", + "20250331", + 1580.98, + 1586.96, + 1560.06, + 1561.0, + 1585.21, + -24.21, + -1.5272, + 21084.94, + 3311857.714 + ], + [ + "600519.SH", + "20250328", + 1590.93, + 1593.0, + 1576.0, + 1585.21, + 1589.0, + -3.79, + -0.2385, + 16227.89, + 2569470.015 + ], + [ + "600519.SH", + "20250327", + 1576.0, + 1598.25, + 1573.38, + 1589.0, + 1576.0, + 13.0, + 0.8249, + 17955.63, + 2848889.21 + ], + [ + "600519.SH", + "20250326", + 1572.77, + 1582.58, + 1571.0, + 1576.0, + 1575.5, + 0.5, + 0.0317, + 14323.56, + 2257639.75 + ], + [ + "600519.SH", + "20250325", + 1588.0, + 1592.8, + 1566.66, + 1575.5, + 1583.0, + -7.5, + -0.4738, + 17674.38, + 2783286.813 + ], + [ + "600519.SH", + "20250324", + 1570.0, + 1589.66, + 1562.38, + 1583.0, + 1573.75, + 9.25, + 0.5878, + 25565.85, + 4032223.482 + ], + [ + "600519.SH", + "20250321", + 1599.0, + 1612.2, + 1568.83, + 1573.75, + 1604.0, + -30.25, + -1.8859, + 33614.3, + 5324973.19 + ], + [ + "600519.SH", + "20250320", + 1635.71, + 1637.6, + 1595.07, + 1604.0, + 1635.71, + -31.71, + -1.9386, + 36736.77, + 5903879.207 + ], + [ + "600519.SH", + "20250319", + 1624.29, + 1648.0, + 1623.0, + 1635.71, + 1624.0, + 11.71, + 0.7211, + 27389.55, + 4477128.77 + ], + [ + "600519.SH", + "20250318", + 1645.04, + 1652.0, + 1621.7, + 1624.0, + 1637.86, + -13.86, + -0.8462, + 31613.45, + 5153210.436 + ], + [ + "600519.SH", + "20250317", + 1657.0, + 1657.99, + 1626.88, + 1637.86, + 1628.01, + 9.85, + 0.605, + 54689.13, + 8985138.902 + ], + [ + "600519.SH", + "20250314", + 1547.66, + 1628.01, + 1541.0, + 1628.01, + 1537.77, + 90.24, + 5.8682, + 92913.15, + 14882842.708 + ], + [ + "600519.SH", + "20250313", + 1544.0, + 1555.5, + 1530.88, + 1537.77, + 1542.58, + -4.81, + -0.3118, + 25724.39, + 3966369.271 + ], + [ + "600519.SH", + "20250312", + 1546.96, + 1549.68, + 1526.35, + 1542.58, + 1548.68, + -6.1, + -0.3939, + 29603.96, + 4552923.644 + ], + [ + "600519.SH", + "20250311", + 1517.0, + 1548.68, + 1512.0, + 1548.68, + 1522.9, + 25.78, + 1.6928, + 39775.6, + 6094007.854 + ], + [ + "600519.SH", + "20250310", + 1519.81, + 1526.88, + 1506.14, + 1522.9, + 1521.0, + 1.9, + 0.1249, + 31369.07, + 4758789.26 + ], + [ + "600519.SH", + "20250307", + 1503.0, + 1528.36, + 1503.0, + 1521.0, + 1505.98, + 15.02, + 0.9974, + 37991.35, + 5760608.729 + ], + [ + "600519.SH", + "20250306", + 1474.0, + 1510.2, + 1472.08, + 1505.98, + 1466.37, + 39.61, + 2.7012, + 42167.64, + 6297117.4 + ], + [ + "600519.SH", + "20250305", + 1472.0, + 1474.0, + 1460.1, + 1466.37, + 1470.11, + -3.74, + -0.2544, + 24605.22, + 3606932.152 + ], + [ + "600519.SH", + "20250304", + 1485.0, + 1486.0, + 1465.21, + 1470.11, + 1487.02, + -16.91, + -1.1372, + 25211.21, + 3710675.863 + ], + [ + "600519.SH", + "20250303", + 1502.6, + 1520.99, + 1481.5, + 1487.02, + 1500.79, + -13.77, + -0.9175, + 31595.66, + 4736679.903 + ], + [ + "600519.SH", + "20250228", + 1485.5, + 1528.38, + 1482.0, + 1500.79, + 1485.56, + 15.23, + 1.0252, + 56128.95, + 8475738.166 + ], + [ + "600519.SH", + "20250227", + 1460.02, + 1489.9, + 1454.0, + 1485.56, + 1460.01, + 25.55, + 1.75, + 49762.17, + 7368002.361 + ], + [ + "600519.SH", + "20250226", + 1455.45, + 1464.96, + 1445.0, + 1460.01, + 1454.0, + 6.01, + 0.4133, + 26366.09, + 3835949.041 + ], + [ + "600519.SH", + "20250225", + 1470.01, + 1473.39, + 1452.0, + 1454.0, + 1479.07, + -25.07, + -1.695, + 28387.43, + 4142814.459 + ], + [ + "600519.SH", + "20250224", + 1488.0, + 1499.52, + 1474.0, + 1479.07, + 1488.21, + -9.14, + -0.6142, + 34743.73, + 5157907.322 + ], + [ + "600519.SH", + "20250221", + 1480.0, + 1496.73, + 1473.01, + 1488.21, + 1474.0, + 14.21, + 0.964, + 36417.83, + 5418169.587 + ], + [ + "600519.SH", + "20250220", + 1483.0, + 1491.97, + 1473.39, + 1474.0, + 1491.0, + -17.0, + -1.1402, + 23749.73, + 3513552.984 + ], + [ + "600519.SH", + "20250219", + 1475.05, + 1494.44, + 1464.9, + 1491.0, + 1475.0, + 16.0, + 1.0847, + 32393.31, + 4803378.441 + ], + [ + "600519.SH", + "20250218", + 1470.0, + 1492.99, + 1462.08, + 1475.0, + 1471.62, + 3.38, + 0.2297, + 27799.92, + 4113845.397 + ], + [ + "600519.SH", + "20250217", + 1481.0, + 1494.98, + 1467.1, + 1471.62, + 1475.0, + -3.38, + -0.2292, + 32470.59, + 4804663.813 + ], + [ + "600519.SH", + "20250214", + 1465.06, + 1477.0, + 1458.22, + 1475.0, + 1465.06, + 9.94, + 0.6785, + 27099.92, + 3978694.098 + ], + [ + "600519.SH", + "20250213", + 1443.02, + 1475.25, + 1434.51, + 1465.06, + 1443.0, + 22.06, + 1.5288, + 43385.36, + 6335985.851 + ], + [ + "600519.SH", + "20250212", + 1418.0, + 1445.0, + 1416.88, + 1443.0, + 1418.0, + 25.0, + 1.763, + 25475.79, + 3644170.477 + ], + [ + "600519.SH", + "20250211", + 1434.95, + 1435.96, + 1415.9, + 1418.0, + 1431.51, + -13.51, + -0.9438, + 23572.83, + 3348668.867 + ], + [ + "600519.SH", + "20250210", + 1438.0, + 1440.0, + 1427.09, + 1431.51, + 1436.0, + -4.49, + -0.3127, + 20839.79, + 2988101.756 + ], + [ + "600519.SH", + "20250207", + 1413.68, + 1442.68, + 1403.41, + 1436.0, + 1412.84, + 23.16, + 1.6393, + 37242.07, + 5310850.624 + ], + [ + "600519.SH", + "20250206", + 1400.01, + 1421.99, + 1400.01, + 1412.84, + 1403.8, + 9.04, + 0.644, + 27355.82, + 3859568.163 + ], + [ + "600519.SH", + "20250205", + 1440.0, + 1443.0, + 1402.21, + 1403.8, + 1434.99, + -31.19, + -2.1735, + 40756.52, + 5753730.034 + ], + [ + "600519.SH", + "20250127", + 1437.0, + 1443.97, + 1427.02, + 1434.99, + 1436.0, + -1.01, + -0.0703, + 29356.46, + 4213534.543 + ], + [ + "600519.SH", + "20250124", + 1442.0, + 1447.79, + 1430.31, + 1436.0, + 1443.0, + -7.0, + -0.4851, + 31360.34, + 4515322.685 + ], + [ + "600519.SH", + "20250123", + 1449.97, + 1468.0, + 1438.0, + 1443.0, + 1441.0, + 2.0, + 0.1388, + 27125.04, + 3932700.85 + ], + [ + "600519.SH", + "20250122", + 1463.27, + 1467.95, + 1438.0, + 1441.0, + 1468.15, + -27.15, + -1.8493, + 28293.84, + 4088101.682 + ], + [ + "600519.SH", + "20250121", + 1481.0, + 1482.0, + 1465.0, + 1468.15, + 1474.8, + -6.65, + -0.4509, + 16856.04, + 2481821.229 + ], + [ + "600519.SH", + "20250120", + 1461.0, + 1489.79, + 1460.0, + 1474.8, + 1454.75, + 20.05, + 1.3782, + 31409.75, + 4647626.351 + ], + [ + "600519.SH", + "20250117", + 1446.39, + 1461.0, + 1446.38, + 1454.75, + 1446.38, + 8.37, + 0.5787, + 18308.94, + 2663227.309 + ], + [ + "600519.SH", + "20250116", + 1474.0, + 1482.66, + 1442.0, + 1446.38, + 1471.27, + -24.89, + -1.6917, + 26342.13, + 3840411.437 + ], + [ + "600519.SH", + "20250115", + 1467.0, + 1474.6, + 1460.01, + 1471.27, + 1472.5, + -1.23, + -0.0835, + 18399.34, + 2701518.712 + ], + [ + "600519.SH", + "20250114", + 1444.95, + 1478.68, + 1442.0, + 1472.5, + 1443.98, + 28.52, + 1.9751, + 30956.57, + 4535651.504 + ], + [ + "600519.SH", + "20250113", + 1425.0, + 1444.44, + 1422.01, + 1443.98, + 1436.0, + 7.98, + 0.5557, + 21082.51, + 3030806.87 + ], + [ + "600519.SH", + "20250110", + 1447.1, + 1451.95, + 1436.0, + 1436.0, + 1444.0, + -8.0, + -0.554, + 21871.95, + 3154677.598 + ], + [ + "600519.SH", + "20250109", + 1448.99, + 1464.58, + 1432.98, + 1444.0, + 1442.5, + 1.5, + 0.104, + 29705.65, + 4295852.736 + ], + [ + "600519.SH", + "20250108", + 1440.0, + 1451.82, + 1426.66, + 1442.5, + 1440.2, + 2.3, + 0.1597, + 35258.21, + 5071821.246 + ], + [ + "600519.SH", + "20250107", + 1444.66, + 1451.89, + 1439.0, + 1440.2, + 1440.0, + 0.2, + 0.0139, + 24221.18, + 3494634.227 + ], + [ + "600519.SH", + "20250106", + 1453.0, + 1462.66, + 1432.8, + 1440.0, + 1475.0, + -35.0, + -2.3729, + 44255.12, + 6392479.712 + ], + [ + "600519.SH", + "20250103", + 1494.5, + 1494.99, + 1467.01, + 1475.0, + 1488.0, + -13.0, + -0.8737, + 32628.36, + 4836610.288 + ], + [ + "600519.SH", + "20250102", + 1524.0, + 1524.49, + 1480.0, + 1488.0, + 1524.0, + -36.0, + -2.3622, + 50028.7, + 7490883.773 + ] + ], + "has_more": false, + "count": -1 + }, + "msg": "" +}` + c.Data(http.StatusOK, "application/json; charset=utf-8", []byte(customJSONString)) + }) + return &HTTPServer{ router: router, logger: logger, diff --git a/cmd/mock-server/backend/http-server_routes_test.go b/cmd/mock-server/backend/http-server_routes_test.go index de18fe93..c4c1f1bb 100644 --- a/cmd/mock-server/backend/http-server_routes_test.go +++ b/cmd/mock-server/backend/http-server_routes_test.go @@ -1,71 +1,70 @@ package backend import ( - "bytes" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" ) func TestHTTPServer_UserRoutes(t *testing.T) { - s := NewHTTPServer() + s := NewHTTPServer() - // Create user - body := map[string]any{ - "username": "u1", - "email": "u1@example.com", - } - bb, _ := json.Marshal(body) - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodPost, "/users", bytes.NewReader(bb)) - r.Header.Set("Content-Type", "application/json") - s.router.ServeHTTP(w, r) - if w.Code != http.StatusCreated { - t.Fatalf("create user status = %d", w.Code) - } + // Create user + body := map[string]any{ + "username": "u1", + "email": "u1@example.com", + } + bb, _ := json.Marshal(body) + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPost, "/users", bytes.NewReader(bb)) + r.Header.Set("Content-Type", "application/json") + s.router.ServeHTTP(w, r) + if w.Code != http.StatusCreated { + t.Fatalf("create user status = %d", w.Code) + } - // Get by email - w = httptest.NewRecorder() - r = httptest.NewRequest(http.MethodGet, "/users/email/u1@example.com", nil) - s.router.ServeHTTP(w, r) - if w.Code != http.StatusOK { - t.Fatalf("get user status = %d", w.Code) - } + // Get by email + w = httptest.NewRecorder() + r = httptest.NewRequest(http.MethodGet, "/users/email/u1@example.com", nil) + s.router.ServeHTTP(w, r) + if w.Code != http.StatusOK { + t.Fatalf("get user status = %d", w.Code) + } - // Update preferences - prefs := map[string]any{ - "isPublic": true, - "showEmail": false, - "theme": "dark", - "tags": []string{"a", "b"}, - "settings": map[string]any{"k": "v"}, - "notifications": []Notification{{Type: "email", Channel: "system", Enabled: true, Frequency: 0}}, - } - pb, _ := json.Marshal(prefs) - w = httptest.NewRecorder() - r = httptest.NewRequest(http.MethodPut, "/users/u1@example.com/preferences", bytes.NewReader(pb)) - r.Header.Set("Content-Type", "application/json") - s.router.ServeHTTP(w, r) - if w.Code != http.StatusOK { - t.Fatalf("update prefs status = %d", w.Code) - } + // Update preferences + prefs := map[string]any{ + "isPublic": true, + "showEmail": false, + "theme": "dark", + "tags": []string{"a", "b"}, + "settings": map[string]any{"k": "v"}, + "notifications": []Notification{{Type: "email", Channel: "system", Enabled: true, Frequency: 0}}, + } + pb, _ := json.Marshal(prefs) + w = httptest.NewRecorder() + r = httptest.NewRequest(http.MethodPut, "/users/u1@example.com/preferences", bytes.NewReader(pb)) + r.Header.Set("Content-Type", "application/json") + s.router.ServeHTTP(w, r) + if w.Code != http.StatusOK { + t.Fatalf("update prefs status = %d", w.Code) + } - // Upload avatar missing url -> 400 - w = httptest.NewRecorder() - r = httptest.NewRequest(http.MethodPost, "/users/u1@example.com/avatar", nil) - s.router.ServeHTTP(w, r) - if w.Code != http.StatusBadRequest { - t.Fatalf("avatar missing url status = %d", w.Code) - } + // Upload avatar missing url -> 400 + w = httptest.NewRecorder() + r = httptest.NewRequest(http.MethodPost, "/users/u1@example.com/avatar", nil) + s.router.ServeHTTP(w, r) + if w.Code != http.StatusBadRequest { + t.Fatalf("avatar missing url status = %d", w.Code) + } - // Upload avatar with url - w = httptest.NewRecorder() - r = httptest.NewRequest(http.MethodPost, "/users/u1@example.com/avatar", bytes.NewBufferString("url=https://img")) - r.Header.Set("Content-Type", "application/x-www-form-urlencoded") - s.router.ServeHTTP(w, r) - if w.Code != http.StatusOK { - t.Fatalf("avatar ok status = %d", w.Code) - } + // Upload avatar with url + w = httptest.NewRecorder() + r = httptest.NewRequest(http.MethodPost, "/users/u1@example.com/avatar", bytes.NewBufferString("url=https://img")) + r.Header.Set("Content-Type", "application/x-www-form-urlencoded") + s.router.ServeHTTP(w, r) + if w.Code != http.StatusOK { + t.Fatalf("avatar ok status = %d", w.Code) + } } - diff --git a/cmd/mock-server/backend/mcp-server_more_test.go b/cmd/mock-server/backend/mcp-server_more_test.go index a0b9fc9f..d9a3301f 100644 --- a/cmd/mock-server/backend/mcp-server_more_test.go +++ b/cmd/mock-server/backend/mcp-server_more_test.go @@ -1,47 +1,47 @@ package backend import ( - "context" - "encoding/json" - "testing" + "context" + "encoding/json" + "testing" - "github.com/mark3labs/mcp-go/mcp" - "github.com/stretchr/testify/assert" + "github.com/mark3labs/mcp-go/mcp" + "github.com/stretchr/testify/assert" ) func TestNewMCPServer_And_GenerateResources(t *testing.T) { - srv := NewMCPServer() - if srv == nil { - t.Fatal("expected server instance") - } + srv := NewMCPServer() + if srv == nil { + t.Fatal("expected server instance") + } - rs := generateResources() - if len(rs) != 100 { - t.Fatalf("expected 100 resources, got %d", len(rs)) - } + rs := generateResources() + if len(rs) != 100 { + t.Fatalf("expected 100 resources, got %d", len(rs)) + } } func TestHandleSimpleAndComplexPrompts(t *testing.T) { - // simple - out, err := handleSimplePrompt(context.Background(), mcp.GetPromptRequest{}) - assert.NoError(t, err) - assert.GreaterOrEqual(t, len(out.Messages), 1) + // simple + out, err := handleSimplePrompt(context.Background(), mcp.GetPromptRequest{}) + assert.NoError(t, err) + assert.GreaterOrEqual(t, len(out.Messages), 1) - // complex with args - req := mcp.GetPromptRequest{Params: mcp.GetPromptParams{Arguments: map[string]string{ - "temperature": "0.7", - "style": "short", - }}} - out2, err := handleComplexPrompt(context.Background(), req) - assert.NoError(t, err) - assert.GreaterOrEqual(t, len(out2.Messages), 2) + // complex with args + req := mcp.GetPromptRequest{Params: mcp.GetPromptParams{Arguments: map[string]string{ + "temperature": "0.7", + "style": "short", + }}} + out2, err := handleComplexPrompt(context.Background(), req) + assert.NoError(t, err) + assert.GreaterOrEqual(t, len(out2.Messages), 2) } func TestHandleLongRunningOperation_ZeroSteps_NoProgress(t *testing.T) { - // Provide steps=0 to avoid progress notifications and timing - args, _ := json.Marshal(map[string]any{"duration": 0, "steps": 0}) - req := mcp.CallToolRequest{Params: mcp.CallToolParams{Arguments: json.RawMessage(args), Meta: &mcp.Meta{}}} - out, err := handleLongRunningOperationTool(context.Background(), req) - assert.NoError(t, err) - assert.NotNil(t, out) + // Provide steps=0 to avoid progress notifications and timing + args, _ := json.Marshal(map[string]any{"duration": 0, "steps": 0}) + req := mcp.CallToolRequest{Params: mcp.CallToolParams{Arguments: json.RawMessage(args), Meta: &mcp.Meta{}}} + out, err := handleLongRunningOperationTool(context.Background(), req) + assert.NoError(t, err) + assert.NotNil(t, out) } diff --git a/internal/apiserver/handler/auth_missing_test.go b/internal/apiserver/handler/auth_missing_test.go index 2930ac08..612de928 100644 --- a/internal/apiserver/handler/auth_missing_test.go +++ b/internal/apiserver/handler/auth_missing_test.go @@ -214,7 +214,7 @@ func TestHandler_UpdateUserTenants_Success(t *testing.T) { req, _ := http.NewRequest(http.MethodPut, "/users/tenants", nil) r.ServeHTTP(w, req) assert.Equal(t, http.StatusOK, w.Code) - assert.Contains(t, f.removedIds, uint(1)) // removed tenant 1 + assert.Contains(t, f.removedIds, uint(1)) // removed tenant 1 assert.Contains(t, f.addedPairs, [2]uint{5, 3}) // added tenant 3 to user 5 } @@ -272,4 +272,4 @@ func TestHandler_UpdateUserTenants_UserNotFound(t *testing.T) { req, _ := http.NewRequest(http.MethodPut, "/users/tenants", nil) r.ServeHTTP(w, req) assert.Equal(t, http.StatusInternalServerError, w.Code) -} \ No newline at end of file +} diff --git a/internal/core/auth_authorize_test.go b/internal/core/auth_authorize_test.go index a5cb6454..71e0b6cd 100644 --- a/internal/core/auth_authorize_test.go +++ b/internal/core/auth_authorize_test.go @@ -1,94 +1,102 @@ package core import ( - "context" - "net/http" - "net/http/httptest" - "net/url" - "os" - "path/filepath" - "strings" - "testing" + "context" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "strings" + "testing" - inta "github.com/amoylab/unla/internal/auth" - "github.com/amoylab/unla/internal/common/config" - "github.com/gin-gonic/gin" - "go.uber.org/zap" + inta "github.com/amoylab/unla/internal/auth" + "github.com/amoylab/unla/internal/common/config" + "github.com/gin-gonic/gin" + "go.uber.org/zap" ) // fakeAuth implements inta.Auth for focused handler tests. type fakeAuth struct{ inta.OAuth2 } -func (f *fakeAuth) IsOAuth2Enabled() bool { return true } -func (f *fakeAuth) GetOAuth2CORS() *config.CORSConfig { return nil } -func (f *fakeAuth) GetGoogleOAuth() inta.ExternalOAuth { return nil } -func (f *fakeAuth) GetGitHubOAuth() inta.ExternalOAuth { return nil } -func (f *fakeAuth) IsGoogleOAuthEnabled() bool { return false } -func (f *fakeAuth) IsGitHubOAuthEnabled() bool { return false } +func (f *fakeAuth) IsOAuth2Enabled() bool { return true } +func (f *fakeAuth) GetOAuth2CORS() *config.CORSConfig { return nil } +func (f *fakeAuth) GetGoogleOAuth() inta.ExternalOAuth { return nil } +func (f *fakeAuth) GetGitHubOAuth() inta.ExternalOAuth { return nil } +func (f *fakeAuth) IsGoogleOAuthEnabled() bool { return false } +func (f *fakeAuth) IsGitHubOAuthEnabled() bool { return false } type fakeOAuth2 struct{} -func (fakeOAuth2) ServerMetadata(r *http.Request) map[string]interface{} { return map[string]any{"ok": true} } +func (fakeOAuth2) ServerMetadata(r *http.Request) map[string]interface{} { + return map[string]any{"ok": true} +} func (fakeOAuth2) Authorize(_ context.Context, _ *http.Request) (*inta.AuthorizationResponse, error) { - return &inta.AuthorizationResponse{Code: "abc", State: "s1"}, nil + return &inta.AuthorizationResponse{Code: "abc", State: "s1"}, nil +} +func (fakeOAuth2) Token(_ context.Context, _ *http.Request) (*inta.TokenResponse, error) { + return &inta.TokenResponse{AccessToken: "t"}, nil } -func (fakeOAuth2) Token(_ context.Context, _ *http.Request) (*inta.TokenResponse, error) { return &inta.TokenResponse{AccessToken: "t"}, nil } func (fakeOAuth2) Register(_ context.Context, _ *http.Request) (*inta.ClientRegistrationResponse, error) { - return &inta.ClientRegistrationResponse{ClientID: "id"}, nil + return &inta.ClientRegistrationResponse{ClientID: "id"}, nil } func (fakeOAuth2) Revoke(_ context.Context, _ *http.Request) error { return nil } func (fakeOAuth2) ValidateToken(_ context.Context, _ string) error { return nil } func TestHandleOAuthAuthorize_GET_RendersPage(t *testing.T) { - // Ensure template exists for rendering - _ = os.MkdirAll("assets/templates", 0o755) - tplPath := filepath.Join("assets", "templates", "authorize.html") - _ = os.WriteFile(tplPath, []byte("authorize page"), 0o644) + // Ensure template exists for rendering + _ = os.MkdirAll("assets/templates", 0o755) + tplPath := filepath.Join("assets", "templates", "authorize.html") + _ = os.WriteFile(tplPath, []byte("authorize page"), 0o644) - logger := zap.NewNop() - s, err := NewServer(logger, 0, nil, nil, nil) - if err != nil { t.Fatalf("new server: %v", err) } + logger := zap.NewNop() + s, err := NewServer(logger, 0, nil, nil, nil) + if err != nil { + t.Fatalf("new server: %v", err) + } - // Build gin context bound to server router with HTML renderer loaded - w := httptest.NewRecorder() - c, eng := gin.CreateTestContext(w) - eng.LoadHTMLGlob("assets/templates/*") - c.Request = httptest.NewRequest(http.MethodGet, "/authorize?client_id=c&redirect_uri=http://x&state=s", nil) - c.Request.Header.Set("User-Agent", "ut") - c.Request.RemoteAddr = "127.0.0.1:12345" + // Build gin context bound to server router with HTML renderer loaded + w := httptest.NewRecorder() + c, eng := gin.CreateTestContext(w) + eng.LoadHTMLGlob("assets/templates/*") + c.Request = httptest.NewRequest(http.MethodGet, "/authorize?client_id=c&redirect_uri=http://x&state=s", nil) + c.Request.Header.Set("User-Agent", "ut") + c.Request.RemoteAddr = "127.0.0.1:12345" - s.renderAuthorizationPage(c, "client", "http://x", "s") - if w.Code != http.StatusOK { - t.Fatalf("expected 200, got %d", w.Code) - } - if !strings.Contains(w.Body.String(), "authorize") { - t.Fatalf("expected body to contain template content, got %q", w.Body.String()) - } + s.renderAuthorizationPage(c, "client", "http://x", "s") + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d", w.Code) + } + if !strings.Contains(w.Body.String(), "authorize") { + t.Fatalf("expected body to contain template content, got %q", w.Body.String()) + } } func TestHandleOAuthAuthorize_POST_Redirects(t *testing.T) { - logger := zap.NewNop() - s, err := NewServer(logger, 0, nil, nil, nil) - if err != nil { t.Fatalf("new server: %v", err) } - s.auth = &fakeAuth{OAuth2: fakeOAuth2{}} + logger := zap.NewNop() + s, err := NewServer(logger, 0, nil, nil, nil) + if err != nil { + t.Fatalf("new server: %v", err) + } + s.auth = &fakeAuth{OAuth2: fakeOAuth2{}} - form := url.Values{} - form.Set("client_id", "cid") - form.Set("redirect_uri", "http://example.com/cb") - form.Set("state", "s1") - form.Set("response_type", "code") - form.Set("scope", "openid") + form := url.Values{} + form.Set("client_id", "cid") + form.Set("redirect_uri", "http://example.com/cb") + form.Set("state", "s1") + form.Set("response_type", "code") + form.Set("scope", "openid") - w := httptest.NewRecorder() - c, _ := gin.CreateTestContext(w) - req := httptest.NewRequest(http.MethodPost, "/authorize", strings.NewReader(form.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - c.Request = req + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + req := httptest.NewRequest(http.MethodPost, "/authorize", strings.NewReader(form.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + c.Request = req - s.handleOAuthAuthorize(c) + s.handleOAuthAuthorize(c) - loc := w.Header().Get("Location") - if loc == "" || !strings.Contains(loc, "code=") || !strings.Contains(loc, "state=") { - t.Fatalf("expected redirect to include code and state, got %s", loc) - } + loc := w.Header().Get("Location") + if loc == "" || !strings.Contains(loc, "code=") || !strings.Contains(loc, "state=") { + t.Fatalf("expected redirect to include code and state, got %s", loc) + } } diff --git a/internal/core/handler_chain_test.go b/internal/core/handler_chain_test.go index 2197e51c..76ec92aa 100644 --- a/internal/core/handler_chain_test.go +++ b/internal/core/handler_chain_test.go @@ -214,4 +214,4 @@ func TestTextHandler_HandleText(t *testing.T) { assert.True(t, ok) assert.Equal(t, "text", textContent1.Type) assert.Contains(t, textContent1.Text, textContent) -} \ No newline at end of file +} diff --git a/internal/core/handler_test.go b/internal/core/handler_test.go index 37bbf389..772db737 100644 --- a/internal/core/handler_test.go +++ b/internal/core/handler_test.go @@ -314,4 +314,4 @@ func TestHandlerChain_Integration(t *testing.T) { } }) } -} \ No newline at end of file +} diff --git a/internal/core/mcpproxy/transports_early_test.go b/internal/core/mcpproxy/transports_early_test.go index 161f3c1f..3f2ebdd4 100644 --- a/internal/core/mcpproxy/transports_early_test.go +++ b/internal/core/mcpproxy/transports_early_test.go @@ -1,44 +1,68 @@ package mcpproxy import ( - "context" - "encoding/json" - "testing" + "context" + "encoding/json" + "testing" - "github.com/amoylab/unla/internal/common/config" - "github.com/amoylab/unla/internal/template" - "github.com/amoylab/unla/pkg/mcp" + "github.com/amoylab/unla/internal/common/config" + "github.com/amoylab/unla/internal/template" + "github.com/amoylab/unla/pkg/mcp" ) func TestSSETransport_EarlyFailures(t *testing.T) { - tr := &SSETransport{cfg: config.MCPServerConfig{URL: "://bad"}} - if tr.IsRunning() { t.Fatalf("unexpected running state") } - if err := tr.Stop(context.Background()); err != nil { t.Fatalf("stop idle: %v", err) } - if _, err := tr.FetchTools(context.Background()); err == nil { t.Fatalf("expected fetch error") } - - args, _ := json.Marshal(map[string]any{"k": "v"}) - _, err := tr.CallTool(context.Background(), mcp.CallToolParams{Name: "t", Arguments: args}, &template.RequestWrapper{}) - if err == nil { t.Fatalf("expected call tool error") } + tr := &SSETransport{cfg: config.MCPServerConfig{URL: "://bad"}} + if tr.IsRunning() { + t.Fatalf("unexpected running state") + } + if err := tr.Stop(context.Background()); err != nil { + t.Fatalf("stop idle: %v", err) + } + if _, err := tr.FetchTools(context.Background()); err == nil { + t.Fatalf("expected fetch error") + } + + args, _ := json.Marshal(map[string]any{"k": "v"}) + _, err := tr.CallTool(context.Background(), mcp.CallToolParams{Name: "t", Arguments: args}, &template.RequestWrapper{}) + if err == nil { + t.Fatalf("expected call tool error") + } } func TestStdioTransport_EarlyFailures(t *testing.T) { - tr := &StdioTransport{cfg: config.MCPServerConfig{Command: "__nonexistent_command__"}} - if tr.IsRunning() { t.Fatalf("unexpected running state") } - if err := tr.Stop(context.Background()); err != nil { t.Fatalf("stop idle: %v", err) } - if _, err := tr.FetchTools(context.Background()); err == nil { t.Fatalf("expected fetch error") } - - args, _ := json.Marshal(map[string]any{}) - _, err := tr.CallTool(context.Background(), mcp.CallToolParams{Name: "t", Arguments: args}, &template.RequestWrapper{}) - if err == nil { t.Fatalf("expected call tool error") } + tr := &StdioTransport{cfg: config.MCPServerConfig{Command: "__nonexistent_command__"}} + if tr.IsRunning() { + t.Fatalf("unexpected running state") + } + if err := tr.Stop(context.Background()); err != nil { + t.Fatalf("stop idle: %v", err) + } + if _, err := tr.FetchTools(context.Background()); err == nil { + t.Fatalf("expected fetch error") + } + + args, _ := json.Marshal(map[string]any{}) + _, err := tr.CallTool(context.Background(), mcp.CallToolParams{Name: "t", Arguments: args}, &template.RequestWrapper{}) + if err == nil { + t.Fatalf("expected call tool error") + } } func TestStreamableTransport_EarlyFailures(t *testing.T) { - tr := &StreamableTransport{cfg: config.MCPServerConfig{URL: "://bad"}} - if tr.IsRunning() { t.Fatalf("unexpected running state") } - if err := tr.Stop(context.Background()); err != nil { t.Fatalf("stop idle: %v", err) } - if _, err := tr.FetchTools(context.Background()); err == nil { t.Fatalf("expected fetch error") } - - args, _ := json.Marshal(map[string]any{"k": 1}) - _, err := tr.CallTool(context.Background(), mcp.CallToolParams{Name: "t", Arguments: args}, &template.RequestWrapper{}) - if err == nil { t.Fatalf("expected call tool error") } + tr := &StreamableTransport{cfg: config.MCPServerConfig{URL: "://bad"}} + if tr.IsRunning() { + t.Fatalf("unexpected running state") + } + if err := tr.Stop(context.Background()); err != nil { + t.Fatalf("stop idle: %v", err) + } + if _, err := tr.FetchTools(context.Background()); err == nil { + t.Fatalf("expected fetch error") + } + + args, _ := json.Marshal(map[string]any{"k": 1}) + _, err := tr.CallTool(context.Background(), mcp.CallToolParams{Name: "t", Arguments: args}, &template.RequestWrapper{}) + if err == nil { + t.Fatalf("expected call tool error") + } } diff --git a/internal/core/server_options_shutdown_test.go b/internal/core/server_options_shutdown_test.go index a532b8c8..2a3489ab 100644 --- a/internal/core/server_options_shutdown_test.go +++ b/internal/core/server_options_shutdown_test.go @@ -1,30 +1,33 @@ package core import ( - "context" - "testing" + "context" + "testing" - "github.com/amoylab/unla/pkg/trace" - "go.uber.org/zap" + "github.com/amoylab/unla/pkg/trace" + "go.uber.org/zap" ) func TestWithTraceCaptureOption(t *testing.T) { - logger := zap.NewNop() - cap := trace.CaptureConfig{} - cap.DownstreamRequest.Enabled = true - s, err := NewServer(logger, 0, nil, nil, nil, WithTraceCapture(cap)) - if err != nil { t.Fatalf("new server: %v", err) } - if !s.traceCapture.DownstreamRequest.Enabled { - t.Fatalf("expected trace capture enabled") - } + logger := zap.NewNop() + cap := trace.CaptureConfig{} + cap.DownstreamRequest.Enabled = true + s, err := NewServer(logger, 0, nil, nil, nil, WithTraceCapture(cap)) + if err != nil { + t.Fatalf("new server: %v", err) + } + if !s.traceCapture.DownstreamRequest.Enabled { + t.Fatalf("expected trace capture enabled") + } } func TestServerShutdown_NoTransports(t *testing.T) { - logger := zap.NewNop() - s, err := NewServer(logger, 0, nil, nil, nil) - if err != nil { t.Fatalf("new server: %v", err) } - if err := s.Shutdown(context.Background()); err != nil { - t.Fatalf("shutdown: %v", err) - } + logger := zap.NewNop() + s, err := NewServer(logger, 0, nil, nil, nil) + if err != nil { + t.Fatalf("new server: %v", err) + } + if err := s.Shutdown(context.Background()); err != nil { + t.Fatalf("shutdown: %v", err) + } } - diff --git a/internal/core/sse_message_post_test.go b/internal/core/sse_message_post_test.go index 6580dc96..cf9ae18c 100644 --- a/internal/core/sse_message_post_test.go +++ b/internal/core/sse_message_post_test.go @@ -1,103 +1,111 @@ package core import ( - "bytes" - "context" - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "github.com/amoylab/unla/internal/mcp/session" - "github.com/amoylab/unla/pkg/mcp" - "github.com/gin-gonic/gin" - "go.uber.org/zap" + "bytes" + "context" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/amoylab/unla/internal/mcp/session" + "github.com/amoylab/unla/pkg/mcp" + "github.com/gin-gonic/gin" + "go.uber.org/zap" ) type testFakeConn struct{ meta *session.Meta } -func (f *testFakeConn) EventQueue() <-chan *session.Message { return make(chan *session.Message) } +func (f *testFakeConn) EventQueue() <-chan *session.Message { return make(chan *session.Message) } func (f *testFakeConn) Send(ctx context.Context, msg *session.Message) error { return nil } -func (f *testFakeConn) Close(ctx context.Context) error { return nil } -func (f *testFakeConn) Meta() *session.Meta { return f.meta } +func (f *testFakeConn) Close(ctx context.Context) error { return nil } +func (f *testFakeConn) Meta() *session.Meta { return f.meta } type testFakeConnErr struct{ meta *session.Meta } -func (f *testFakeConnErr) EventQueue() <-chan *session.Message { return make(chan *session.Message) } -func (f *testFakeConnErr) Send(ctx context.Context, msg *session.Message) error { return fmt.Errorf("send error") } -func (f *testFakeConnErr) Close(ctx context.Context) error { return nil } -func (f *testFakeConnErr) Meta() *session.Meta { return f.meta } +func (f *testFakeConnErr) EventQueue() <-chan *session.Message { return make(chan *session.Message) } +func (f *testFakeConnErr) Send(ctx context.Context, msg *session.Message) error { + return fmt.Errorf("send error") +} +func (f *testFakeConnErr) Close(ctx context.Context) error { return nil } +func (f *testFakeConnErr) Meta() *session.Meta { return f.meta } func TestHandleMessage_MissingSessionID(t *testing.T) { - logger := zap.NewNop() - s, err := NewServer(logger, 0, nil, nil, nil) - if err != nil { t.Fatalf("new server: %v", err) } - - w := httptest.NewRecorder() - c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest(http.MethodPost, "/x/message", nil) - - s.handleMessage(c) - if w.Code != http.StatusNotFound { - t.Fatalf("expected 404, got %d", w.Code) - } + logger := zap.NewNop() + s, err := NewServer(logger, 0, nil, nil, nil) + if err != nil { + t.Fatalf("new server: %v", err) + } + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest(http.MethodPost, "/x/message", nil) + + s.handleMessage(c) + if w.Code != http.StatusNotFound { + t.Fatalf("expected 404, got %d", w.Code) + } } func TestHandlePostMessage_BasicValidation(t *testing.T) { - logger := zap.NewNop() - s, err := NewServer(logger, 0, nil, nil, nil) - if err != nil { t.Fatalf("new server: %v", err) } - - // nil connection - { - w := httptest.NewRecorder() - c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest(http.MethodPost, "/x/message", nil) - s.handlePostMessage(c, nil) - if w.Code != http.StatusInternalServerError { - t.Fatalf("expected 500, got %d", w.Code) - } - } - - // invalid content type - { - w := httptest.NewRecorder() - c, _ := gin.CreateTestContext(w) - req := httptest.NewRequest(http.MethodPost, "/x/message", bytes.NewBufferString("{}")) - req.Header.Set("Content-Type", "text/plain") - c.Request = req - s.handlePostMessage(c, &testFakeConn{meta: &session.Meta{ID: "sid"}}) - if w.Code != http.StatusNotAcceptable { - t.Fatalf("expected 406, got %d", w.Code) - } - } - - // invalid json - { - w := httptest.NewRecorder() - c, _ := gin.CreateTestContext(w) - req := httptest.NewRequest(http.MethodPost, "/x/message", bytes.NewBufferString("{")) - req.Header.Set("Content-Type", "application/json") - c.Request = req - s.handlePostMessage(c, &testFakeConn{meta: &session.Meta{ID: "sid"}}) - if w.Code != http.StatusBadRequest { - t.Fatalf("expected 400, got %d", w.Code) - } - } + logger := zap.NewNop() + s, err := NewServer(logger, 0, nil, nil, nil) + if err != nil { + t.Fatalf("new server: %v", err) + } + + // nil connection + { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest(http.MethodPost, "/x/message", nil) + s.handlePostMessage(c, nil) + if w.Code != http.StatusInternalServerError { + t.Fatalf("expected 500, got %d", w.Code) + } + } + + // invalid content type + { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + req := httptest.NewRequest(http.MethodPost, "/x/message", bytes.NewBufferString("{}")) + req.Header.Set("Content-Type", "text/plain") + c.Request = req + s.handlePostMessage(c, &testFakeConn{meta: &session.Meta{ID: "sid"}}) + if w.Code != http.StatusNotAcceptable { + t.Fatalf("expected 406, got %d", w.Code) + } + } + + // invalid json + { + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + req := httptest.NewRequest(http.MethodPost, "/x/message", bytes.NewBufferString("{")) + req.Header.Set("Content-Type", "application/json") + c.Request = req + s.handlePostMessage(c, &testFakeConn{meta: &session.Meta{ID: "sid"}}) + if w.Code != http.StatusBadRequest { + t.Fatalf("expected 400, got %d", w.Code) + } + } } func TestSendErrorResponse_SendFailureAccepted(t *testing.T) { - logger := zap.NewNop() - s, err := NewServer(logger, 0, nil, nil, nil) - if err != nil { t.Fatalf("new server: %v", err) } - - w := httptest.NewRecorder() - c, _ := gin.CreateTestContext(w) - c.Request = httptest.NewRequest(http.MethodPost, "/x/message", nil) - - conn := &testFakeConnErr{meta: &session.Meta{ID: "sid"}} - s.sendErrorResponse(c, conn, mcp.JSONRPCRequest{Id: "1", Method: "demo"}, "oops") - if w.Code != http.StatusAccepted { - t.Fatalf("expected 202, got %d", w.Code) - } + logger := zap.NewNop() + s, err := NewServer(logger, 0, nil, nil, nil) + if err != nil { + t.Fatalf("new server: %v", err) + } + + w := httptest.NewRecorder() + c, _ := gin.CreateTestContext(w) + c.Request = httptest.NewRequest(http.MethodPost, "/x/message", nil) + + conn := &testFakeConnErr{meta: &session.Meta{ID: "sid"}} + s.sendErrorResponse(c, conn, mcp.JSONRPCRequest{Id: "1", Method: "demo"}, "oops") + if w.Code != http.StatusAccepted { + t.Fatalf("expected 202, got %d", w.Code) + } } diff --git a/internal/core/state/state_build_test.go b/internal/core/state/state_build_test.go index b12acefe..bcc8b43d 100644 --- a/internal/core/state/state_build_test.go +++ b/internal/core/state/state_build_test.go @@ -1,58 +1,57 @@ package state import ( - "context" - "testing" + "context" + "testing" - "github.com/amoylab/unla/internal/common/cnst" - "github.com/amoylab/unla/internal/common/config" - "go.uber.org/zap" + "github.com/amoylab/unla/internal/common/cnst" + "github.com/amoylab/unla/internal/common/config" + "go.uber.org/zap" ) func TestBuildStateFromConfig_MinimalHTTPAndMCP(t *testing.T) { - logger := zap.NewNop() - ctx := context.Background() + logger := zap.NewNop() + ctx := context.Background() - cfg := &config.MCPConfig{ - Name: "c1", - Tenant: "t1", - Tools: []config.ToolConfig{{ - Name: "tool1", - Method: "GET", - Endpoint: "/x", - }}, - Servers: []config.ServerConfig{{ - Name: "srv1", - AllowedTools: []string{"tool1"}, - }}, - Routers: []config.RouterConfig{{ - Server: "srv1", - Prefix: "/h", - }, { - Server: "ms1", - Prefix: "/m", - }}, - McpServers: []config.MCPServerConfig{{ - Type: cnst.BackendProtoSSE.String(), - Name: "ms1", - URL: "http://127.0.0.1:9/", // invalid, but Start is not called - Policy: cnst.PolicyOnDemand, - }}, - Prompts: []config.PromptConfig{{ - Name: "p1", - Description: "d", - }}, - } + cfg := &config.MCPConfig{ + Name: "c1", + Tenant: "t1", + Tools: []config.ToolConfig{{ + Name: "tool1", + Method: "GET", + Endpoint: "/x", + }}, + Servers: []config.ServerConfig{{ + Name: "srv1", + AllowedTools: []string{"tool1"}, + }}, + Routers: []config.RouterConfig{{ + Server: "srv1", + Prefix: "/h", + }, { + Server: "ms1", + Prefix: "/m", + }}, + McpServers: []config.MCPServerConfig{{ + Type: cnst.BackendProtoSSE.String(), + Name: "ms1", + URL: "http://127.0.0.1:9/", // invalid, but Start is not called + Policy: cnst.PolicyOnDemand, + }}, + Prompts: []config.PromptConfig{{ + Name: "p1", + Description: "d", + }}, + } - ns, err := BuildStateFromConfig(ctx, []*config.MCPConfig{cfg}, nil, logger) - if err != nil { - t.Fatalf("BuildStateFromConfig: %v", err) - } - if ns.GetProtoType("/h") != cnst.BackendProtoHttp { - t.Fatalf("expected http proto for /h") - } - if ns.GetProtoType("/m") != cnst.BackendProtoSSE { - t.Fatalf("expected sse proto for /m") - } + ns, err := BuildStateFromConfig(ctx, []*config.MCPConfig{cfg}, nil, logger) + if err != nil { + t.Fatalf("BuildStateFromConfig: %v", err) + } + if ns.GetProtoType("/h") != cnst.BackendProtoHttp { + t.Fatalf("expected http proto for /h") + } + if ns.GetProtoType("/m") != cnst.BackendProtoSSE { + t.Fatalf("expected sse proto for /m") + } } - diff --git a/internal/core/tool_test.go b/internal/core/tool_test.go index a34a5795..6b97b39c 100644 --- a/internal/core/tool_test.go +++ b/internal/core/tool_test.go @@ -170,4 +170,3 @@ func TestPrepareRequest(t *testing.T) { // rendered body is returned for tracing capture assert.Equal(t, `{"a": "V"}`, rendered) } - diff --git a/internal/mcp/storage/db_list_deleteversion_test.go b/internal/mcp/storage/db_list_deleteversion_test.go index ec94a3c0..0f7fb272 100644 --- a/internal/mcp/storage/db_list_deleteversion_test.go +++ b/internal/mcp/storage/db_list_deleteversion_test.go @@ -1,79 +1,78 @@ package storage import ( - "context" - "testing" + "context" + "testing" - "github.com/amoylab/unla/internal/common/config" - "github.com/stretchr/testify/assert" + "github.com/amoylab/unla/internal/common/config" + "github.com/stretchr/testify/assert" ) // Covers DBStore.List (with/without deleted) and DeleteVersion (non-active). func TestDBStore_List_And_DeleteVersion(t *testing.T) { - s := newSQLiteStore(t) - ctx := context.Background() + s := newSQLiteStore(t) + ctx := context.Background() - cfg1 := sampleConfig() - cfg1.Name = "cfg1" - cfg2 := sampleConfig() - cfg2.Name = "cfg2" + cfg1 := sampleConfig() + cfg1.Name = "cfg1" + cfg2 := sampleConfig() + cfg2.Name = "cfg2" - // Create two configs - assert.NoError(t, s.Create(ctx, cfg1)) - assert.NoError(t, s.Create(ctx, cfg2)) + // Create two configs + assert.NoError(t, s.Create(ctx, cfg1)) + assert.NoError(t, s.Create(ctx, cfg2)) - // List without deleted - lst, err := s.List(ctx) - assert.NoError(t, err) - if assert.GreaterOrEqual(t, len(lst), 2) { - names := []string{lst[0].Name, lst[1].Name} - assert.Contains(t, names, "cfg1") - assert.Contains(t, names, "cfg2") - } + // List without deleted + lst, err := s.List(ctx) + assert.NoError(t, err) + if assert.GreaterOrEqual(t, len(lst), 2) { + names := []string{lst[0].Name, lst[1].Name} + assert.Contains(t, names, "cfg1") + assert.Contains(t, names, "cfg2") + } - // Create additional versions for cfg1 so we can delete a non-active one - cfg1.Tools = append(cfg1.Tools, config.ToolConfig{Name: "tool-x"}) - assert.NoError(t, s.Update(ctx, cfg1)) // creates v2 - cfg1.Tools = append(cfg1.Tools, config.ToolConfig{Name: "tool-y"}) - assert.NoError(t, s.Update(ctx, cfg1)) // creates v3 (active) + // Create additional versions for cfg1 so we can delete a non-active one + cfg1.Tools = append(cfg1.Tools, config.ToolConfig{Name: "tool-x"}) + assert.NoError(t, s.Update(ctx, cfg1)) // creates v2 + cfg1.Tools = append(cfg1.Tools, config.ToolConfig{Name: "tool-y"}) + assert.NoError(t, s.Update(ctx, cfg1)) // creates v3 (active) - vers, err := s.ListVersions(ctx, cfg1.Tenant, cfg1.Name) - assert.NoError(t, err) - // Find a non-active version (prefer v2) - nonActive := -1 - for _, v := range vers { - if !v.IsActive { - nonActive = v.Version - break - } - } - if nonActive == -1 { - t.Fatalf("expected a non-active version to exist") - } + vers, err := s.ListVersions(ctx, cfg1.Tenant, cfg1.Name) + assert.NoError(t, err) + // Find a non-active version (prefer v2) + nonActive := -1 + for _, v := range vers { + if !v.IsActive { + nonActive = v.Version + break + } + } + if nonActive == -1 { + t.Fatalf("expected a non-active version to exist") + } - // Delete non-active version should succeed - assert.NoError(t, s.DeleteVersion(ctx, cfg1.Tenant, cfg1.Name, nonActive)) + // Delete non-active version should succeed + assert.NoError(t, s.DeleteVersion(ctx, cfg1.Tenant, cfg1.Name, nonActive)) - // Ensure it is gone from versions list - vers2, err := s.ListVersions(ctx, cfg1.Tenant, cfg1.Name) - assert.NoError(t, err) - for _, v := range vers2 { - if v.Version == nonActive { - t.Fatalf("version %d should have been deleted", nonActive) - } - } + // Ensure it is gone from versions list + vers2, err := s.ListVersions(ctx, cfg1.Tenant, cfg1.Name) + assert.NoError(t, err) + for _, v := range vers2 { + if v.Version == nonActive { + t.Fatalf("version %d should have been deleted", nonActive) + } + } - // Soft delete cfg2 and verify List(includeDeleted=true) still returns it convertible - assert.NoError(t, s.Delete(ctx, cfg2.Tenant, cfg2.Name)) - lstAll, err := s.List(ctx, true) - assert.NoError(t, err) - foundDeleted := false - for _, it := range lstAll { - if it.Name == cfg2.Name { - foundDeleted = true - break - } - } - assert.True(t, foundDeleted, "expected deleted config present when includeDeleted=true") + // Soft delete cfg2 and verify List(includeDeleted=true) still returns it convertible + assert.NoError(t, s.Delete(ctx, cfg2.Tenant, cfg2.Name)) + lstAll, err := s.List(ctx, true) + assert.NoError(t, err) + foundDeleted := false + for _, it := range lstAll { + if it.Name == cfg2.Name { + foundDeleted = true + break + } + } + assert.True(t, foundDeleted, "expected deleted config present when includeDeleted=true") } - diff --git a/internal/mcp/storage/factory_test.go b/internal/mcp/storage/factory_test.go index 49ae3826..51497a0a 100644 --- a/internal/mcp/storage/factory_test.go +++ b/internal/mcp/storage/factory_test.go @@ -1,33 +1,33 @@ package storage import ( - "testing" - "time" + "testing" + "time" - "github.com/amoylab/unla/internal/common/config" - "github.com/stretchr/testify/assert" - "go.uber.org/zap" + "github.com/amoylab/unla/internal/common/config" + "github.com/stretchr/testify/assert" + "go.uber.org/zap" ) func TestNewStore_DB_And_API_And_Unsupported(t *testing.T) { - logger := zap.NewNop() + logger := zap.NewNop() - // DB store using sqlite memory temp file via helper - // Leverage the same config shape as newSQLiteStore - tmp := t.TempDir() - cfgDB := &config.StorageConfig{Type: "db", Database: config.DatabaseConfig{Type: "sqlite", DBName: tmp + "/store.db"}} - stDB, err := NewStore(logger, cfgDB) - assert.NoError(t, err) - assert.NotNil(t, stDB) + // DB store using sqlite memory temp file via helper + // Leverage the same config shape as newSQLiteStore + tmp := t.TempDir() + cfgDB := &config.StorageConfig{Type: "db", Database: config.DatabaseConfig{Type: "sqlite", DBName: tmp + "/store.db"}} + stDB, err := NewStore(logger, cfgDB) + assert.NoError(t, err) + assert.NotNil(t, stDB) - // API store - cfgAPI := &config.StorageConfig{Type: "api", API: config.APIStorageConfig{Url: "http://127.0.0.1:1", Timeout: time.Second}} - stAPI, err := NewStore(logger, cfgAPI) - assert.NoError(t, err) - assert.NotNil(t, stAPI) + // API store + cfgAPI := &config.StorageConfig{Type: "api", API: config.APIStorageConfig{Url: "http://127.0.0.1:1", Timeout: time.Second}} + stAPI, err := NewStore(logger, cfgAPI) + assert.NoError(t, err) + assert.NotNil(t, stAPI) - // Unsupported - stX, err := NewStore(logger, &config.StorageConfig{Type: "nope"}) - assert.Error(t, err) - assert.Nil(t, stX) + // Unsupported + stX, err := NewStore(logger, &config.StorageConfig{Type: "nope"}) + assert.Error(t, err) + assert.Nil(t, stX) } diff --git a/internal/mcp/storage/notifier/factory_test.go b/internal/mcp/storage/notifier/factory_test.go index 41e08259..d56dc174 100644 --- a/internal/mcp/storage/notifier/factory_test.go +++ b/internal/mcp/storage/notifier/factory_test.go @@ -1,48 +1,48 @@ package notifier import ( - "context" - "fmt" - "strings" - "testing" - - "github.com/amoylab/unla/internal/common/config" - "github.com/stretchr/testify/assert" - "go.uber.org/zap" + "context" + "fmt" + "strings" + "testing" + + "github.com/amoylab/unla/internal/common/config" + "github.com/stretchr/testify/assert" + "go.uber.org/zap" ) func TestNewNotifier_UnknownType(t *testing.T) { - _, err := NewNotifier(context.Background(), zap.NewNop(), &config.NotifierConfig{Type: "unknown"}) - assert.Error(t, err) + _, err := NewNotifier(context.Background(), zap.NewNop(), &config.NotifierConfig{Type: "unknown"}) + assert.Error(t, err) } func TestNewNotifier_KnownTypes(t *testing.T) { - ctx := context.Background() - logger := zap.NewNop() - - // signal (provide dummy pid path so constructor doesn't panic) - n1, err := NewNotifier(ctx, logger, &config.NotifierConfig{Type: string(TypeSignal), Role: string(config.RoleBoth), Signal: config.SignalConfig{PID: "/tmp/unla-test.pid"}}) - assert.NoError(t, err) - assert.True(t, n1.CanReceive()) - - // api sender - n2, err := NewNotifier(ctx, logger, &config.NotifierConfig{Type: string(TypeAPI), Role: string(config.RoleSender)}) - assert.NoError(t, err) - assert.True(t, n2.CanSend()) - - // redis using invalid address should return error - n3, err := NewNotifier(ctx, logger, &config.NotifierConfig{ - Type: string(TypeRedis), - Role: string(config.RoleBoth), - Redis: config.RedisConfig{Addr: "127.0.0.1:0"}, - }) - assert.Nil(t, n3) - assert.Error(t, err) - - // composite without redis addr should still construct (provide dummy pid) - n4, err := NewNotifier(ctx, logger, &config.NotifierConfig{Type: string(TypeComposite), Role: string(config.RoleBoth), Signal: config.SignalConfig{PID: "/tmp/unla-test.pid"}}) - assert.NoError(t, err) - // type string contains Composite for sanity - typeStr := fmt.Sprintf("%T", n4) - assert.True(t, strings.Contains(typeStr, "Composite")) + ctx := context.Background() + logger := zap.NewNop() + + // signal (provide dummy pid path so constructor doesn't panic) + n1, err := NewNotifier(ctx, logger, &config.NotifierConfig{Type: string(TypeSignal), Role: string(config.RoleBoth), Signal: config.SignalConfig{PID: "/tmp/unla-test.pid"}}) + assert.NoError(t, err) + assert.True(t, n1.CanReceive()) + + // api sender + n2, err := NewNotifier(ctx, logger, &config.NotifierConfig{Type: string(TypeAPI), Role: string(config.RoleSender)}) + assert.NoError(t, err) + assert.True(t, n2.CanSend()) + + // redis using invalid address should return error + n3, err := NewNotifier(ctx, logger, &config.NotifierConfig{ + Type: string(TypeRedis), + Role: string(config.RoleBoth), + Redis: config.RedisConfig{Addr: "127.0.0.1:0"}, + }) + assert.Nil(t, n3) + assert.Error(t, err) + + // composite without redis addr should still construct (provide dummy pid) + n4, err := NewNotifier(ctx, logger, &config.NotifierConfig{Type: string(TypeComposite), Role: string(config.RoleBoth), Signal: config.SignalConfig{PID: "/tmp/unla-test.pid"}}) + assert.NoError(t, err) + // type string contains Composite for sanity + typeStr := fmt.Sprintf("%T", n4) + assert.True(t, strings.Contains(typeStr, "Composite")) } diff --git a/pkg/trace/trace.go b/pkg/trace/trace.go index a735518e..a7f2f1df 100644 --- a/pkg/trace/trace.go +++ b/pkg/trace/trace.go @@ -22,9 +22,9 @@ import ( // testable indirections for constructors var ( - newResource = resource.New - newOTLPTraceHTTP = otlptracehttp.New - newOTLPTraceGRPC = otlptracegrpc.New + newResource = resource.New + newOTLPTraceHTTP = otlptracehttp.New + newOTLPTraceGRPC = otlptracegrpc.New ) // Config represents OpenTelemetry/Jaeger tracing configuration @@ -135,41 +135,41 @@ func InitTracing(ctx context.Context, cfg *Config, lg *zap.Logger) (func(context } // Resource with service metadata - res, err := newResource(ctx, - resource.WithFromEnv(), - resource.WithProcess(), - resource.WithTelemetrySDK(), - resource.WithAttributes( - semconv.ServiceName(serviceName), - semconv.DeploymentEnvironment(cfg.Environment), - ), - ) + res, err := newResource(ctx, + resource.WithFromEnv(), + resource.WithProcess(), + resource.WithTelemetrySDK(), + resource.WithAttributes( + semconv.ServiceName(serviceName), + semconv.DeploymentEnvironment(cfg.Environment), + ), + ) if err != nil { return nil, fmt.Errorf("create resource: %w", err) } // Exporter var exp *otlptrace.Exporter - switch protocol { - case "http": - opts := []otlptracehttp.Option{otlptracehttp.WithEndpoint(endpoint)} - if cfg.Insecure { - opts = append(opts, otlptracehttp.WithInsecure()) - } - if len(cfg.Headers) > 0 { - opts = append(opts, otlptracehttp.WithHeaders(cfg.Headers)) - } - exp, err = newOTLPTraceHTTP(ctx, opts...) - default: // grpc - opts := []otlptracegrpc.Option{otlptracegrpc.WithEndpoint(endpoint)} - if cfg.Insecure { - opts = append(opts, otlptracegrpc.WithInsecure()) - } - if len(cfg.Headers) > 0 { - opts = append(opts, otlptracegrpc.WithHeaders(cfg.Headers)) - } - exp, err = newOTLPTraceGRPC(ctx, opts...) - } + switch protocol { + case "http": + opts := []otlptracehttp.Option{otlptracehttp.WithEndpoint(endpoint)} + if cfg.Insecure { + opts = append(opts, otlptracehttp.WithInsecure()) + } + if len(cfg.Headers) > 0 { + opts = append(opts, otlptracehttp.WithHeaders(cfg.Headers)) + } + exp, err = newOTLPTraceHTTP(ctx, opts...) + default: // grpc + opts := []otlptracegrpc.Option{otlptracegrpc.WithEndpoint(endpoint)} + if cfg.Insecure { + opts = append(opts, otlptracegrpc.WithInsecure()) + } + if len(cfg.Headers) > 0 { + opts = append(opts, otlptracegrpc.WithHeaders(cfg.Headers)) + } + exp, err = newOTLPTraceGRPC(ctx, opts...) + } if err != nil { return nil, fmt.Errorf("create exporter: %w", err) } diff --git a/pkg/trace/trace_additional_test.go b/pkg/trace/trace_additional_test.go index 43b71399..009b6aa3 100644 --- a/pkg/trace/trace_additional_test.go +++ b/pkg/trace/trace_additional_test.go @@ -258,4 +258,4 @@ func TestSpanScope_WithAttrs_Chaining(t *testing.T) { result := scope.WithAttrs(attribute.String("key1", "value1")).WithAttrs(attribute.String("key2", "value2")) assert.Equal(t, scope, result) scope.End() -} \ No newline at end of file +} diff --git a/pkg/trace/trace_test.go b/pkg/trace/trace_test.go index 0ea97eda..5229f23b 100644 --- a/pkg/trace/trace_test.go +++ b/pkg/trace/trace_test.go @@ -1,20 +1,20 @@ package trace import ( - "context" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/exporters/otlp/otlptrace" - "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" - "go.opentelemetry.io/otel/sdk/resource" - sdktrace "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/sdk/trace/tracetest" - "go.uber.org/zap" - "gopkg.in/yaml.v3" + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" + "go.uber.org/zap" + "gopkg.in/yaml.v3" ) // helper structure for YAML decoding of StringMap @@ -105,7 +105,7 @@ func TestInitTracing_GRPC_Normalization_And_Shutdown(t *testing.T) { Protocol: "grpc", Endpoint: "http://localhost:4317/", // will be trimmed to localhost:4317 Insecure: true, - SamplerRate: -1, // clamp to 0 + SamplerRate: -1, // clamp to 0 Headers: map[string]string{"auth": "t"}, // cover grpc headers path } @@ -150,31 +150,31 @@ func TestInitTracing_DefaultProtocol_Empty_UsesGRPC(t *testing.T) { } func TestInitTracing_ResourceError_IsReturned(t *testing.T) { - // Override newResource to simulate a construction error - prev := newResource - newResource = func(ctx context.Context, opts ...resource.Option) (*resource.Resource, error) { - return nil, fmt.Errorf("boom") - } - t.Cleanup(func() { newResource = prev }) - - cfg := &Config{ServiceName: "svc", Protocol: "http"} - shutdown, err := InitTracing(context.Background(), cfg, zap.NewNop()) - require.Error(t, err) - require.Nil(t, shutdown) + // Override newResource to simulate a construction error + prev := newResource + newResource = func(ctx context.Context, opts ...resource.Option) (*resource.Resource, error) { + return nil, fmt.Errorf("boom") + } + t.Cleanup(func() { newResource = prev }) + + cfg := &Config{ServiceName: "svc", Protocol: "http"} + shutdown, err := InitTracing(context.Background(), cfg, zap.NewNop()) + require.Error(t, err) + require.Nil(t, shutdown) } func TestInitTracing_ExporterError_IsReturned(t *testing.T) { - // Override HTTP exporter constructor to simulate an error - prev := newOTLPTraceHTTP - newOTLPTraceHTTP = func(ctx context.Context, options ...otlptracehttp.Option) (*otlptrace.Exporter, error) { - return nil, fmt.Errorf("no exporter") - } - t.Cleanup(func() { newOTLPTraceHTTP = prev }) - - cfg := &Config{ServiceName: "svc", Protocol: "http"} - shutdown, err := InitTracing(context.Background(), cfg, zap.NewNop()) - require.Error(t, err) - require.Nil(t, shutdown) + // Override HTTP exporter constructor to simulate an error + prev := newOTLPTraceHTTP + newOTLPTraceHTTP = func(ctx context.Context, options ...otlptracehttp.Option) (*otlptrace.Exporter, error) { + return nil, fmt.Errorf("no exporter") + } + t.Cleanup(func() { newOTLPTraceHTTP = prev }) + + cfg := &Config{ServiceName: "svc", Protocol: "http"} + shutdown, err := InitTracing(context.Background(), cfg, zap.NewNop()) + require.Error(t, err) + require.Nil(t, shutdown) } func TestBuilder_Start_WithAttrs_End_WithInMemoryProvider(t *testing.T) { From 30cac0f5810bbebc80fbfedcc7d3d40a7dcbdc94 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Mon, 3 Nov 2025 04:51:00 +0000 Subject: [PATCH 2/2] fix: upgrade react-i18next from 15.6.0 to 15.7.4 Snyk has created this PR to upgrade react-i18next from 15.6.0 to 15.7.4. See this package in npm: react-i18next See this project in Snyk: https://app.snyk.io/org/ifuryst/project/98f029b3-c19c-4d43-a11e-dabb5cc0c815?utm_source=github&utm_medium=referral&page=upgrade-pr --- web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/package.json b/web/package.json index 18a7fd97..47dd9c5b 100644 --- a/web/package.json +++ b/web/package.json @@ -67,7 +67,7 @@ "react-diff-viewer-continued": "^3.4.0", "react-dom": "^18.3.1", "react-dropzone": "^14.3.8", - "react-i18next": "^15.5.2", + "react-i18next": "^15.7.4", "react-markdown": "^10.1.0", "react-router-dom": "^6.30.1", "rehype-highlight": "^7.0.2",