class Tina4::TestClient
def delete(path, headers: nil)
def delete(path, headers: nil) request("DELETE", path, headers: headers) end
def get(path, headers: nil)
def get(path, headers: nil) request("GET", path, headers: headers) end
def patch(path, json: nil, body: nil, headers: nil)
def patch(path, json: nil, body: nil, headers: nil) request("PATCH", path, json: json, body: body, headers: headers) end
def post(path, json: nil, body: nil, headers: nil)
def post(path, json: nil, body: nil, headers: nil) request("POST", path, json: json, body: body, headers: headers) end
def put(path, json: nil, body: nil, headers: nil)
def put(path, json: nil, body: nil, headers: nil) request("PUT", path, json: json, body: body, headers: headers) end
def request(method, path, json: nil, body: nil, headers: nil)
def request(method, path, json: nil, body: nil, headers: nil) # Build raw body raw_body = "" content_type = "" if json raw_body = JSON.generate(json) content_type = "application/json" elsif body raw_body = body.to_s end # Split path and query string clean_path, query_string = path.include?("?") ? path.split("?", 2) : [path, ""] # Build Rack env hash env = { "REQUEST_METHOD" => method.upcase, "PATH_INFO" => clean_path, "QUERY_STRING" => query_string || "", "SERVER_NAME" => "localhost", "SERVER_PORT" => "7145", "HTTP_HOST" => "localhost:7145", "REMOTE_ADDR" => "127.0.0.1", "rack.input" => StringIO.new(raw_body), "rack.url_scheme" => "http" } # Add content type env["CONTENT_TYPE"] = content_type unless content_type.empty? env["CONTENT_LENGTH"] = raw_body.bytesize.to_s unless raw_body.empty? # Add custom headers (convert to Rack format: X-Custom → HTTP_X_CUSTOM) if headers headers.each do |key, value| rack_key = "HTTP_#{key.upcase.tr('-', '_')}" env[rack_key] = value end end # Match route result = Tina4::Router.match(method.upcase, clean_path) unless result return TestResponse.new([404, { "content-type" => "application/json" }, ['{"error":"Not found"}']]) end route, path_params = result # Create request and response req = Tina4::Request.new(env, path_params || {}) res = Tina4::Response.new # Build handler args (same logic as RackApp.handle_route) handler_params = route.handler.parameters.map(&:last) route_params = path_params || {} args = handler_params.map do |name| if route_params.key?(name) route_params[name] elsif name == :request || name == :req req else res end end # Execute handler handler_result = args.empty? ? route.handler.call : route.handler.call(*args) # Auto-detect response type if handler_result.is_a?(Tina4::Response) final = handler_result elsif route.respond_to?(:template) && route.template && handler_result.is_a?(Hash) html = Tina4::Template.render(route.template, handler_result) res.html(html) final = res else final = Tina4::Response.auto_detect(handler_result, res) end TestResponse.new(final.to_rack) end