add handler/server tests

This commit is contained in:
Emil Lerch 2026-01-08 12:04:30 -08:00
parent 0726cc0299
commit d20b49dc90
Signed by: lobo
GPG key ID: A7B62D657EF764F8
2 changed files with 265 additions and 2 deletions

View file

@ -76,7 +76,7 @@ fn handleWeather(ctx: *Context, req: *httpz.Request, res: *httpz.Response) !void
try handler.handleWeather(&ctx.options, req, res, client_ip); try handler.handleWeather(&ctx.options, req, res, client_ip);
} }
fn getClientIp(req: *httpz.Request, buf: []u8) ![]const u8 { pub fn getClientIp(req: *httpz.Request, buf: []u8) ![]const u8 {
// Check X-Forwarded-For header first (for proxies) // Check X-Forwarded-For header first (for proxies)
if (req.header("x-forwarded-for")) |xff| { if (req.header("x-forwarded-for")) |xff| {
return parseXForwardedFor(xff); return parseXForwardedFor(xff);
@ -114,7 +114,7 @@ pub fn deinit(self: *Server) void {
self.httpz_server.deinit(); self.httpz_server.deinit();
} }
const MockHarness = struct { pub const MockHarness = struct {
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
config: Config, config: Config,
geoip: *GeoIp, geoip: *GeoIp,
@ -241,6 +241,16 @@ test "handleWeather: default endpoint uses IP address" {
try handler.handleWeather(&harness.opts, ht.req, ht.res, client_ip); try handler.handleWeather(&harness.opts, ht.req, ht.res, client_ip);
try ht.expectStatus(200); try ht.expectStatus(200);
try ht.expectBody(
\\<pre>Weather report: 73.158.64.1
\\
\\<span style="color:#ffff00"> \ / </span> Clear
\\<span style="color:#ffff00"> .-. </span> <span style="color:#d7ff00">+68(+68)</span> °F
\\<span style="color:#ffff00"> ― ( ) ― </span> ↓ <span style="color:#6c6c6c">3</span> mph
\\<span style="color:#ffff00"> `-' </span> 6 mi
\\<span style="color:#ffff00"> / \ </span> 0.0 in
\\</pre>
);
} }
test "handleWeather: x-forwarded-for with multiple IPs" { test "handleWeather: x-forwarded-for with multiple IPs" {

View file

@ -198,3 +198,256 @@ fn determineFormat(params: QueryParams, user_agent: ?[]const u8) Formatted.Forma
return .ansi; return .ansi;
return .html; return .html;
} }
test "handler: help page" {
const allocator = std.testing.allocator;
const MockHarness = @import("Server.zig").MockHarness;
var harness = try MockHarness.init(allocator);
defer harness.deinit();
var ht = httpz.testing.init(.{});
defer ht.deinit();
ht.url("/:help");
ht.param("location", ":help");
try handleWeather(&harness.opts, ht.req, ht.res, "127.0.0.1");
try ht.expectStatus(200);
}
test "handler: translation page" {
const allocator = std.testing.allocator;
const MockHarness = @import("Server.zig").MockHarness;
var harness = try MockHarness.init(allocator);
defer harness.deinit();
var ht = httpz.testing.init(.{});
defer ht.deinit();
ht.url("/:translation");
ht.param("location", ":translation");
try handleWeather(&harness.opts, ht.req, ht.res, "127.0.0.1");
try ht.expectStatus(200);
}
test "handler: favicon" {
const allocator = std.testing.allocator;
const MockHarness = @import("Server.zig").MockHarness;
var harness = try MockHarness.init(allocator);
defer harness.deinit();
var ht = httpz.testing.init(.{});
defer ht.deinit();
ht.url("/favicon.ico");
ht.param("location", "favicon.ico");
try handleWeather(&harness.opts, ht.req, ht.res, "127.0.0.1");
try ht.expectStatus(200);
}
test "handler: format j1 (json)" {
const allocator = std.testing.allocator;
const MockHarness = @import("Server.zig").MockHarness;
var harness = try MockHarness.init(allocator);
defer harness.deinit();
var ht = httpz.testing.init(.{});
defer ht.deinit();
ht.url("/73.158.64.1?format=j1");
ht.param("location", "73.158.64.1");
var client_ip_buf: [47]u8 = undefined;
const client_ip = try @import("Server.zig").getClientIp(ht.req, &client_ip_buf);
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
try ht.expectStatus(200);
try ht.expectHeader("Content-Type", "application/json; charset=UTF-8");
try ht.expectBody(
\\{"current_condition":{"temp_C":20,"weatherCode":"clear","weatherDesc":[{"value":"Clear"}],"humidity":50,"windspeedKmph":5,"winddirDegree":0,"pressure":1013,"precipMM":0},"weather":[]}
);
}
test "handler: format p1 (prometheus)" {
const allocator = std.testing.allocator;
const MockHarness = @import("Server.zig").MockHarness;
var harness = try MockHarness.init(allocator);
defer harness.deinit();
var ht = httpz.testing.init(.{});
defer ht.deinit();
ht.url("/73.158.64.1?format=p1");
ht.param("location", "73.158.64.1");
var client_ip_buf: [47]u8 = undefined;
const client_ip = try @import("Server.zig").getClientIp(ht.req, &client_ip_buf);
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
try ht.expectStatus(200);
try ht.expectBody(
\\# HELP temperature_feels_like_celsius Feels Like Temperature in Celsius
\\temperature_feels_like_celsius{forecast="current"} 20
\\# HELP temperature_feels_like_fahrenheit Feels Like Temperature in Fahrenheit
\\temperature_feels_like_fahrenheit{forecast="current"} 68
\\# HELP cloudcover_percentage Cloud Coverage in Percent
\\cloudcover_percentage{forecast="current"} 0
\\# HELP humidity_percentage Humidity in Percent
\\humidity_percentage{forecast="current"} 50
\\# HELP precipitation_mm Precipitation (Rainfall) in mm
\\precipitation_mm{forecast="current"} 0.0
\\# HELP pressure_hpa Air pressure in hPa
\\pressure_hpa{forecast="current"} 1013
\\# HELP temperature_celsius Temperature in Celsius
\\temperature_celsius{forecast="current"} 20
\\# HELP temperature_fahrenheit Temperature in Fahrenheit
\\temperature_fahrenheit{forecast="current"} 68
\\# HELP uv_index Ultraviolet Radiation Index
\\uv_index{forecast="current"} 0
\\# HELP visibility Visible Distance in Kilometres
\\visibility{forecast="current"} 10
\\# HELP weather_code Code to describe Weather Condition
\\weather_code{forecast="current"} 800
\\# HELP winddir_degree Wind Direction in Degree
\\winddir_degree{forecast="current"} 0
\\# HELP windspeed_kmph Wind Speed in Kilometres per Hour
\\windspeed_kmph{forecast="current"} 5
\\# HELP windspeed_mph Wind Speed in Miles per Hour
\\windspeed_mph{forecast="current"} 3.106856
\\# HELP observation_time Minutes since start of the day the observation happened
\\observation_time{forecast="current"} 0
\\# HELP weather_desc Weather Description
\\weather_desc{forecast="current", description="Clear"} 1
\\# HELP winddir_16_point Wind Direction on a 16-wind compass rose
\\winddir_16_point{forecast="current", description="N"} 1
\\
);
}
test "handler: format v2" {
const allocator = std.testing.allocator;
const MockHarness = @import("Server.zig").MockHarness;
var harness = try MockHarness.init(allocator);
defer harness.deinit();
var ht = httpz.testing.init(.{});
defer ht.deinit();
ht.url("/73.158.64.1?format=v2");
ht.param("location", "73.158.64.1");
var client_ip_buf: [47]u8 = undefined;
const client_ip = try @import("Server.zig").getClientIp(ht.req, &client_ip_buf);
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
try ht.expectStatus(200);
// Should we have 2 empty lines?
try ht.expectBody(
\\Weather report: 73.158.64.1
\\
\\ Current conditions
\\ Clear
\\ 🌡️ 20.0°C (68.0°F)
\\ 💧 50%
\\ 🌬️ 5.0 km/h N
\\ 🔽 1013.0 hPa
\\ 💦 0.0 mm
\\
\\
);
}
test "handler: format custom (%c)" {
const allocator = std.testing.allocator;
const MockHarness = @import("Server.zig").MockHarness;
var harness = try MockHarness.init(allocator);
defer harness.deinit();
var ht = httpz.testing.init(.{});
defer ht.deinit();
ht.url("/73.158.64.1?format=%c");
ht.param("location", "73.158.64.1");
var client_ip_buf: [47]u8 = undefined;
const client_ip = try @import("Server.zig").getClientIp(ht.req, &client_ip_buf);
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
try ht.expectStatus(200);
try ht.expectBody("☀️");
}
test "handler: format line 1" {
const allocator = std.testing.allocator;
const MockHarness = @import("Server.zig").MockHarness;
var harness = try MockHarness.init(allocator);
defer harness.deinit();
var ht = httpz.testing.init(.{});
defer ht.deinit();
ht.url("/73.158.64.1?format=1");
ht.param("location", "73.158.64.1");
var client_ip_buf: [47]u8 = undefined;
const client_ip = try @import("Server.zig").getClientIp(ht.req, &client_ip_buf);
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
try ht.expectStatus(200);
try ht.expectBody("Test: ☀️ 20°C");
}
test "handler: format line 2" {
const allocator = std.testing.allocator;
const MockHarness = @import("Server.zig").MockHarness;
var harness = try MockHarness.init(allocator);
defer harness.deinit();
var ht = httpz.testing.init(.{});
defer ht.deinit();
ht.url("/73.158.64.1?format=2");
ht.param("location", "73.158.64.1");
var client_ip_buf: [47]u8 = undefined;
const client_ip = try @import("Server.zig").getClientIp(ht.req, &client_ip_buf);
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
try ht.expectStatus(200);
try ht.expectBody("Test: ☀️ 20°C 🌬N5km/h");
}
test "handler: format line 3" {
const allocator = std.testing.allocator;
const MockHarness = @import("Server.zig").MockHarness;
var harness = try MockHarness.init(allocator);
defer harness.deinit();
var ht = httpz.testing.init(.{});
defer ht.deinit();
ht.url("/73.158.64.1?format=3");
ht.param("location", "73.158.64.1");
var client_ip_buf: [47]u8 = undefined;
const client_ip = try @import("Server.zig").getClientIp(ht.req, &client_ip_buf);
try handleWeather(&harness.opts, ht.req, ht.res, client_ip);
try ht.expectStatus(200);
try ht.expectBody("Test: ☀️ 20°C 🌬N5km/h 💧50%%");
}