## Test suite for UTCP Protocol implementation import unittest import std/[tables, json, options] import ../src/nimpak/utcp_protocol suite "UTCP Address Assignment": test "Assign UTCP address to Nippel": let res = assignUTCPAddress(Nippel, "dev-env", "localhost") check res.isOk if res.isOk: let address = res.okValue check address.scheme == UtcpPlain check address.host == "localhost" check address.resourceType == Nippel check address.resourceName == "dev-env" test "Assign UTCP address with default host": let res = assignUTCPAddress(Nexter, "web-server") check res.isOk let address = res.okValue check address.host.len > 0 # Should use hostname check address.resourceType == Nexter check address.resourceName == "web-server" test "Create agent endpoint": let res = createAgentEndpoint("n8n-workflow", "localhost") check res.isOk let address = res.okValue check address.resourceType == Agent check address.resourceName == "n8n-workflow" test "Create tool endpoint": let res = createToolEndpoint("nexus", "localhost") check res.isOk let address = res.okValue check address.resourceType == Tool check address.resourceName == "nexus" test "Create LLM endpoint": let res = createLLMEndpoint("llama2", "localhost") check res.isOk let address = res.okValue check address.resourceType == LLM check address.resourceName == "llama2" suite "UTCP Address Parsing": test "Parse simple UTCP address": let res = parseUTCPAddress("utcp://localhost/nippel/dev-env") check res.isOk let address = res.okValue check address.scheme == UtcpPlain check address.host == "localhost" check address.resourceType == Nippel check address.resourceName == "dev-env" test "Parse UTCP address with port": let res = parseUTCPAddress("utcp://localhost:8080/nexter/web-server") check res.isOk let address = res.okValue check address.port.isSome check address.port.get() == 8080 test "Parse UTCP address with path": let res = parseUTCPAddress("utcp://localhost/nippel/dev-env/state") check res.isOk let address = res.okValue check address.path == "/state" test "Parse UTCP address with query": let res = parseUTCPAddress("utcp://localhost/nippel/dev-env?cmd=vim") check res.isOk let address = res.okValue check address.query.hasKey("cmd") check address.query["cmd"] == "vim" test "Parse secure UTCP address": let res = parseUTCPAddress("utcps://localhost/agent/n8n") check res.isOk let address = res.okValue check address.scheme == UtcpSecure test "Parse invalid scheme": let res = parseUTCPAddress("http://localhost/nippel/dev-env") check not res.isOk test "Parse invalid path": let res = parseUTCPAddress("utcp://localhost/invalid") check not res.isOk suite "UTCP Address Formatting": test "Format simple address": let address = newUTCPAddress("localhost", Nippel, "dev-env") let formatted = formatUTCPAddress(address) check formatted == "utcp://localhost/nippel/dev-env" test "Format address with port": let address = newUTCPAddress("localhost", Nexter, "web-server", port = some(8080)) let formatted = formatUTCPAddress(address) check formatted == "utcp://localhost:8080/nexter/web-server" test "Format address with path": let address = newUTCPAddress("localhost", Nippel, "dev-env", path = "/state") let formatted = formatUTCPAddress(address) check formatted == "utcp://localhost/nippel/dev-env/state" test "Format address with query": var query = initTable[string, string]() query["cmd"] = "vim" let address = newUTCPAddress("localhost", Nippel, "dev-env", query = query) let formatted = formatUTCPAddress(address) check formatted.contains("?cmd=vim") test "Format secure address": let address = newUTCPAddress("localhost", Agent, "n8n", scheme = UtcpSecure) let formatted = formatUTCPAddress(address) check formatted.startsWith("utcps://") suite "UTCP Request Creation": test "Create GET request": let address = newUTCPAddress("localhost", Nippel, "dev-env") let request = newUTCPRequest(address, GET) check request.meth == GET check request.address.resourceName == "dev-env" check request.headers.hasKey("User-Agent") check request.headers.hasKey("UTCP-Version") test "Create POST request with payload": let address = newUTCPAddress("localhost", Nippel, "dev-env") let payload = %*{"command": "vim"} let request = newUTCPRequest(address, POST, payload) check request.meth == POST check request.payload.hasKey("command") check request.payload["command"].getStr() == "vim" suite "UTCP Response Creation": test "Create success response": let response = newUTCPResponse(Ok, %*{"status": "active"}, "req-123") check response.status == Ok check response.requestId == "req-123" check response.data.hasKey("status") test "Create error response": let response = newUTCPResponse(NotFound, %*{"error": "Not found"}) check response.status == NotFound check response.data.hasKey("error") suite "UTCP Method Handlers": test "Handle GET state": let res = handleGetState(Nippel, "dev-env") check res.isOk let state = res.okValue check state.hasKey("resourceType") check state.hasKey("resourceName") check state.hasKey("status") test "Handle activate": let res = handleActivate(Nippel, "dev-env") check res.isOk let response = res.okValue check response.hasKey("action") check response["action"].getStr() == "activate" test "Handle deactivate": let res = handleDeactivate(Nippel, "dev-env") check res.isOk let response = res.okValue check response.hasKey("action") check response["action"].getStr() == "deactivate" test "Handle get merkle": let res = handleGetMerkle(Nippel, "dev-env") check res.isOk let merkle = res.okValue check merkle.hasKey("merkleRoot") test "Handle exec": let res = handleExec(Nippel, "dev-env", "vim") check res.isOk let execResult = res.okValue check execResult.hasKey("command") check execResult["command"].getStr() == "vim" suite "UTCP Request Routing": test "Route GET state request": let address = newUTCPAddress("localhost", Nippel, "dev-env", path = "/state") let request = newUTCPRequest(address, GET) let res = routeUTCPRequest(request) check res.isOk let response = res.okValue check response.status == Ok check response.data.hasKey("resourceType") test "Route POST activate request": let address = newUTCPAddress("localhost", Nippel, "dev-env", path = "/activate") let request = newUTCPRequest(address, POST) let res = routeUTCPRequest(request) check res.isOk let response = res.okValue check response.status == Ok check response.data["action"].getStr() == "activate" test "Route POST deactivate request": let address = newUTCPAddress("localhost", Nippel, "dev-env", path = "/deactivate") let request = newUTCPRequest(address, POST) let res = routeUTCPRequest(request) check res.isOk let response = res.okValue check response.status == Ok check response.data["action"].getStr() == "deactivate" test "Route GET merkle request": let address = newUTCPAddress("localhost", Nippel, "dev-env", path = "/merkle") let request = newUTCPRequest(address, GET) let res = routeUTCPRequest(request) check res.isOk let response = res.okValue check response.status == Ok check response.data.hasKey("merkleRoot") test "Route POST exec request": let address = newUTCPAddress("localhost", Nippel, "dev-env", path = "/exec") let payload = %*{"command": "vim"} let request = newUTCPRequest(address, POST, payload) let res = routeUTCPRequest(request) check res.isOk let response = res.okValue check response.status == Ok check response.data["command"].getStr() == "vim" test "Route invalid path": let address = newUTCPAddress("localhost", Nippel, "dev-env", path = "/invalid") let request = newUTCPRequest(address, GET) let res = routeUTCPRequest(request) check res.isOk let response = res.okValue check response.status == NotFound test "Route unsupported method": let address = newUTCPAddress("localhost", Nippel, "dev-env") let request = newUTCPRequest(address, DELETE) let res = routeUTCPRequest(request) check res.isOk let response = res.okValue check response.status == MethodNotAllowed suite "UTCP Utility Functions": test "Check local address - localhost": let address = newUTCPAddress("localhost", Nippel, "dev-env") check isLocalAddress(address) test "Check local address - 127.0.0.1": let address = newUTCPAddress("127.0.0.1", Nippel, "dev-env") check isLocalAddress(address) test "Check remote address": let address = newUTCPAddress("remote.example.com", Nippel, "dev-env") check not isLocalAddress(address) test "Get default port for plain UTCP": let port = getDefaultPort(UtcpPlain) check port == UTCP_DEFAULT_PORT test "Get default port for secure UTCP": let port = getDefaultPort(UtcpSecure) check port == UTCP_DEFAULT_PORT + 1 test "Validate valid address": let address = newUTCPAddress("localhost", Nippel, "dev-env") let res = validateAddress(address) check res.isOk test "Validate address with empty host": let address = UTCPAddress( scheme: UtcpPlain, host: "", resourceType: Nippel, resourceName: "dev-env" ) let res = validateAddress(address) check not res.isOk test "Validate address with empty resource name": let address = UTCPAddress( scheme: UtcpPlain, host: "localhost", resourceType: Nippel, resourceName: "" ) let res = validateAddress(address) check not res.isOk suite "UTCP Resource Types": test "Create Nippel address": let address = newUTCPAddress("localhost", Nippel, "dev-env") check address.resourceType == Nippel test "Create Nexter address": let address = newUTCPAddress("localhost", Nexter, "web-server") check address.resourceType == Nexter test "Create Package address": let address = newUTCPAddress("localhost", Package, "nginx") check address.resourceType == Package test "Create System address": let address = newUTCPAddress("localhost", System, "config") check address.resourceType == System test "Create Tool address": let address = newUTCPAddress("localhost", Tool, "nexus") check address.resourceType == Tool test "Create Agent address": let address = newUTCPAddress("localhost", Agent, "n8n-workflow") check address.resourceType == Agent test "Create LLM address": let address = newUTCPAddress("localhost", LLM, "llama2") check address.resourceType == LLM