zouter/tests/example.zig

127 lines
3.6 KiB
Zig

const std = @import("std");
const zap = @import("zap");
const zouter = @import("zouter");
/// Stop zap in 3s.
fn stopAfter3s() !void {
std.time.sleep(3 * std.time.ns_per_s);
zap.stop();
}
// Spawn a new thread to stop zap in 3s.
fn stopAfter3sThread() !std.Thread {
return try std.Thread.spawn(.{}, stopAfter3s, .{});
}
// Make an HTTP request to the given URL and put the result in the given pointed variable.
fn makeRequest(allocator: std.mem.Allocator, method: std.http.Method, url: []const u8, varPointer: *[]const u8) !void {
// Emit HTTP request to the server.
var http_client: std.http.Client = .{ .allocator = allocator };
defer http_client.deinit();
var response = std.ArrayList(u8).init(allocator);
defer response.deinit();
_ = try http_client.fetch(.{
.method = method,
.location = .{ .url = url },
.response_storage = .{ .dynamic = &response },
});
varPointer.* = try allocator.dupe(u8, response.items);
}
/// Make a request thread.
fn makeRequestThread(allocator: std.mem.Allocator, method: std.http.Method, url: []const u8, varPointer: *[]const u8) !std.Thread {
return try std.Thread.spawn(.{}, makeRequest, .{ allocator, method, url, varPointer });
}
/// GET /foo/:arg/bar request handler.
fn get(route: zouter.MatchedRoute, request: zap.Request) !void {
var bodyBuffer: [512]u8 = undefined;
const body = try std.fmt.bufPrint(&bodyBuffer, "get: {s}", .{route.params.get("arg").?});
try request.sendBody(body);
}
/// POST /foo/:arg/bar request handler.
fn post(route: zouter.MatchedRoute, request: zap.Request) !void {
var bodyBuffer: [512]u8 = undefined;
const body = try std.fmt.bufPrint(&bodyBuffer, "post: {s}", .{route.params.get("arg").?});
try request.sendBody(body);
}
/// Setup an example router.
fn setupExampleRouter(allocator: std.mem.Allocator) !zouter.Router {
// Initialize an example router.
var exampleRouter = try zouter.Router.init(allocator, .{});
// Add a route to the example router.
try exampleRouter.route(.{
.path = "foo",
.children = &[_]zouter.RouteDefinition{
.{
.path = ":arg",
.children = &[_]zouter.RouteDefinition{
.{
.path = "bar",
.handle = .{
.get = &get,
.post = &post,
},
}
},
}
},
});
return exampleRouter;
}
/// Run HTTP server with test router.
fn runHttp(allocator: std.mem.Allocator) !void {
// Setup test router.
var exampleRouter = try setupExampleRouter(allocator);
defer exampleRouter.deinit();
// Setup HTTP listener.
var listener = zap.HttpListener.init(.{
.interface = "127.0.0.1",
.port = 8112,
.log = true,
// Add zouter to the listener.
.on_request = zouter.Router.onRequest,
});
try listener.listen();
// Emit a GET HTTP request.
const getThread = try makeRequestThread(allocator, std.http.Method.GET, "http://127.0.0.1:8112/foo/any%20value/bar", &getResponse);
defer getThread.join();
// Emit a POST HTTP request.
const postThread = try makeRequestThread(allocator, std.http.Method.POST, "http://127.0.0.1:8112/foo/any%20value/bar", &postResponse);
defer postThread.join();
// Add zap stop in 3s.
const stopThread = try stopAfter3sThread();
defer stopThread.join();
// Start HTTP server workers.
zap.start(.{
.threads = 1,
.workers = 1,
});
}
var getResponse: []const u8 = undefined;
var postResponse: []const u8 = undefined;
test {
const allocator = std.testing.allocator;
try runHttp(allocator);
// Test that responses are correct.
try std.testing.expectEqualStrings("get: any value", getResponse);
try std.testing.expectEqualStrings("post: any value", postResponse);
// Free allocated responses.
allocator.free(getResponse);
allocator.free(postResponse);
}