Improve test App requestJob

Detect if provided params are a subset of actual params, provide
informative errors if not.
This commit is contained in:
Bob Farrell 2025-05-05 13:18:24 +01:00
parent acacd209bd
commit 789f123926
8 changed files with 68 additions and 12 deletions

View File

@ -25,8 +25,8 @@
.hash = "jetkv-0.0.0-zCv0fmCGAgCyYqwHjk0P5KrYVRew1MJAtbtAcIO-WPpT", .hash = "jetkv-0.0.0-zCv0fmCGAgCyYqwHjk0P5KrYVRew1MJAtbtAcIO-WPpT",
}, },
.zmpl = .{ .zmpl = .{
.url = "https://github.com/jetzig-framework/zmpl/archive/c57fc9b83027e8c1459d9625c3509f59f0fb89f3.tar.gz", .url = "https://github.com/jetzig-framework/zmpl/archive/03cc97b52e80ac368f0dfe2e19c2b3dcdbf9f6ed.tar.gz",
.hash = "zmpl-0.0.1-SYFGBgdqAwDeA6xm4KAhpKoNrWs5CMQK6x447zhWclCs", .hash = "zmpl-0.0.1-SYFGBsSZAwBaIVdcokCVyM2azwVO3OTfaTTPddWERpyq",
}, },
.httpz = .{ .httpz = .{
.url = "https://github.com/karlseguin/http.zig/archive/37d7cb9819b804ade5f4b974b82f8dd0622225ed.tar.gz", .url = "https://github.com/karlseguin/http.zig/archive/37d7cb9819b804ade5f4b974b82f8dd0622225ed.tar.gz",

View File

@ -15,3 +15,11 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
return request.render(.ok); return request.render(.ok);
} }
test "index" {
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
defer app.deinit();
const response = try app.request(.GET, "/background_jobs", .{});
try response.expectJob("example", .{ .foo = "bar" });
}

View File

@ -147,7 +147,7 @@ fn renderMarkdown(
if (zmpl.findPrefixed("views", prefixed_name)) |layout| { if (zmpl.findPrefixed("views", prefixed_name)) |layout| {
view.data.content = .{ .data = content }; view.data.content = .{ .data = content };
return try layout.render(view.data, jetzig.TemplateContext, .{}, .{}); return try layout.render(view.data, jetzig.TemplateContext, .{}, &.{}, .{});
} else { } else {
std.debug.print("Unknown layout: {s}\n", .{layout_name}); std.debug.print("Unknown layout: {s}\n", .{layout_name});
return content; return content;
@ -174,6 +174,7 @@ fn renderZmplTemplate(
view.data, view.data,
jetzig.TemplateContext, jetzig.TemplateContext,
.{}, .{},
&.{},
.{ .layout = layout }, .{ .layout = layout },
); );
} else { } else {
@ -181,7 +182,7 @@ fn renderZmplTemplate(
return try allocator.dupe(u8, ""); return try allocator.dupe(u8, "");
} }
} else { } else {
return try template.render(view.data, jetzig.TemplateContext, .{}, .{}); return try template.render(view.data, jetzig.TemplateContext, .{}, &.{}, .{});
} }
} else return null; } else return null;
} }

View File

@ -307,6 +307,7 @@ pub fn renderRedirect(self: *Request, state: RedirectState) !void {
self.response_data, self.response_data,
jetzig.TemplateContext, jetzig.TemplateContext,
.{ .request = self }, .{ .request = self },
&.{},
.{}, .{},
); );
} else try std.fmt.allocPrint(self.allocator, "Redirecting to {s}", .{state.location}), } else try std.fmt.allocPrint(self.allocator, "Redirecting to {s}", .{state.location}),

View File

@ -435,6 +435,7 @@ fn renderTemplateWithLayout(
view.data, view.data,
jetzig.TemplateContext, jetzig.TemplateContext,
template_context, template_context,
&.{},
.{ .layout = layout }, .{ .layout = layout },
); );
} else { } else {
@ -443,6 +444,7 @@ fn renderTemplateWithLayout(
view.data, view.data,
jetzig.TemplateContext, jetzig.TemplateContext,
template_context, template_context,
&.{},
.{}, .{},
); );
} }
@ -450,6 +452,7 @@ fn renderTemplateWithLayout(
view.data, view.data,
jetzig.TemplateContext, jetzig.TemplateContext,
template_context, template_context,
&.{},
.{}, .{},
); );
} }
@ -613,6 +616,7 @@ fn renderErrorView(
request.response_data, request.response_data,
jetzig.TemplateContext, jetzig.TemplateContext,
.{ .request = request }, .{ .request = request },
&.{},
.{}, .{},
), ),
}; };

View File

@ -148,7 +148,7 @@ fn defaultHtml(
try data.addConst("jetzig_view", data.string("")); try data.addConst("jetzig_view", data.string(""));
try data.addConst("jetzig_action", data.string("")); try data.addConst("jetzig_action", data.string(""));
return if (jetzig.zmpl.findPrefixed("mailers", mailer.html_template)) |template| return if (jetzig.zmpl.findPrefixed("mailers", mailer.html_template)) |template|
try template.render(&data, jetzig.TemplateContext, .{}, .{}) try template.render(&data, jetzig.TemplateContext, .{}, &.{}, .{})
else else
null; null;
} }
@ -166,7 +166,7 @@ fn defaultText(
try data.addConst("jetzig_view", data.string("")); try data.addConst("jetzig_view", data.string(""));
try data.addConst("jetzig_action", data.string("")); try data.addConst("jetzig_action", data.string(""));
return if (jetzig.zmpl.findPrefixed("mailers", mailer.text_template)) |template| return if (jetzig.zmpl.findPrefixed("mailers", mailer.text_template)) |template|
try template.render(&data, jetzig.TemplateContext, .{}, .{}) try template.render(&data, jetzig.TemplateContext, .{}, &.{}, .{})
else else
null; null;
} }

View File

@ -66,7 +66,7 @@ pub const TestResponse = struct {
jobs: []Job, jobs: []Job,
pub const Header = struct { name: []const u8, value: []const u8 }; pub const Header = struct { name: []const u8, value: []const u8 };
pub const Job = struct { name: []const u8, params: ?[]const u8 = null }; pub const Job = struct { name: []const u8, params: ?*jetzig.data.Value = null };
pub fn expectStatus(self: TestResponse, comptime expected: jetzig.http.status_codes.StatusCode) !void { pub fn expectStatus(self: TestResponse, comptime expected: jetzig.http.status_codes.StatusCode) !void {
try testing.expectStatus(expected, self); try testing.expectStatus(expected, self);
@ -299,13 +299,54 @@ pub fn expectJson(expected_path: []const u8, expected_value: anytype, response:
} }
pub fn expectJob(job_name: []const u8, job_params: anytype, response: TestResponse) !void { pub fn expectJob(job_name: []const u8, job_params: anytype, response: TestResponse) !void {
var actual_params_buf = std.ArrayList([]const u8).init(response.allocator);
defer actual_params_buf.deinit();
const value: ?*jetzig.data.Value = if (@TypeOf(job_params) == @TypeOf(null))
null
else
try jetzig.Data.zmplValue(job_params, response.allocator);
for (response.jobs) |job| { for (response.jobs) |job| {
comptime var has_args = false; if (std.mem.eql(u8, job_name, job.name)) {
inline for (@typeInfo(@TypeOf(job_params)).Struct.fields) |field| { if (value != null and job.params != null) {
has_args = true; if (job.params.?.includes(value.?.*)) {
_ = field; return;
} else {
try actual_params_buf.append(try job.params.?.toJson());
} }
if (!has_args and std.mem.eql(u8, job_name, job.name)) return; } else {
return;
}
}
}
if (actual_params_buf.items.len == 0) {
logFailure(
"Expected job " ++
jetzig.colors.cyan("{s}") ++
" to have been scheduled but job was not found in the queue.",
.{job_name},
);
} else {
const actual_params_formatted = try std.mem.join(
response.allocator,
"\n",
actual_params_buf.items,
);
defer response.allocator.free(actual_params_formatted);
logFailure(
"Expected params for job " ++
jetzig.colors.cyan("{s}") ++
":\n" ++
jetzig.colors.red("{s}\n") ++
"Actual job params:\n" ++
jetzig.colors.green("{s}"),
.{
job_name,
if (value) |v| try v.toJson() else "null",
actual_params_formatted,
},
);
} }
return error.JetzigExpectJobError; return error.JetzigExpectJobError;
} }

View File

@ -208,6 +208,7 @@ pub fn request(
while (try self.job_queue.popFirst(&data, "__jetzig_jobs")) |value| { while (try self.job_queue.popFirst(&data, "__jetzig_jobs")) |value| {
if (value.getT(.string, "__jetzig_job_name")) |job_name| try jobs.append(.{ if (value.getT(.string, "__jetzig_job_name")) |job_name| try jobs.append(.{
.name = try allocator.dupe(u8, job_name), .name = try allocator.dupe(u8, job_name),
.params = value,
}); });
} }