From 38e46da67353d3d1fa67dff10b5d623bd7977071 Mon Sep 17 00:00:00 2001 From: Vernon Stinebaker Date: Thu, 7 May 2026 10:40:22 +0800 Subject: [PATCH] test(integration): cover settings and config failure paths --- src/api/config.zig | 10 +++++++ src/integration_tests.zig | 55 +++++++++++++++++++++++++++++++++++++++ src/server.zig | 10 ++++++- 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/api/config.zig b/src/api/config.zig index 0e36827..4df209b 100644 --- a/src/api/config.zig +++ b/src/api/config.zig @@ -125,6 +125,16 @@ pub fn handlePatch(allocator: std.mem.Allocator, p: paths_mod.Paths, component: } fn writeConfig(allocator: std.mem.Allocator, p: paths_mod.Paths, component: []const u8, name: []const u8, body: []const u8) ApiResponse { + const parsed = std.json.parseFromSlice(std.json.Value, allocator, body, .{ + .allocate = .alloc_always, + .ignore_unknown_fields = true, + }) catch return .{ + .status = "400 Bad Request", + .content_type = "application/json", + .body = "{\"error\":\"invalid JSON body\"}", + }; + defer parsed.deinit(); + const config_path = p.instanceConfig(allocator, component, name) catch return .{ .status = "500 Internal Server Error", .content_type = "application/json", diff --git a/src/integration_tests.zig b/src/integration_tests.zig index 6f57b15..a635b47 100644 --- a/src/integration_tests.zig +++ b/src/integration_tests.zig @@ -308,6 +308,61 @@ test "integration harness covers settings and config round-trips" { } } +test "integration harness covers settings and config failure paths" { + var server = try IntegrationServer.startWithSeed(std.testing.allocator, struct { + fn call(srv: *IntegrationServer) !void { + try seedManagedInstance(srv, "nullboiler", "demo"); + } + }.call); + defer server.deinit(); + + { + const resp = try server.fetch(.{ + .path = "/api/settings", + .method = .PUT, + .body = "not json", + }); + defer resp.deinit(std.testing.allocator); + try std.testing.expectEqual(std.http.Status.bad_request, resp.status); + try std.testing.expect(std.mem.indexOf(u8, resp.body, "invalid JSON body") != null); + } + + { + const resp = try server.fetch(.{ + .path = "/api/instances/nullboiler/demo/config", + .method = .PUT, + .body = "{\"gateway\":{\"port\":43123},\"provider\":\"openrouter\"}", + }); + defer resp.deinit(std.testing.allocator); + try std.testing.expectEqual(std.http.Status.ok, resp.status); + } + + { + const resp = try server.fetch(.{ .path = "/api/instances/nullboiler/demo/config?path=missing.path" }); + defer resp.deinit(std.testing.allocator); + try std.testing.expectEqual(std.http.Status.not_found, resp.status); + try std.testing.expect(std.mem.indexOf(u8, resp.body, "config path not found") != null); + } + + { + const resp = try server.fetch(.{ + .path = "/api/instances/nullboiler/demo/config", + .method = .PUT, + .body = "not json", + }); + defer resp.deinit(std.testing.allocator); + try std.testing.expectEqual(std.http.Status.bad_request, resp.status); + try std.testing.expect(std.mem.indexOf(u8, resp.body, "invalid JSON body") != null); + } + + { + const resp = try server.fetch(.{ .path = "/api/instances/nullboiler/demo/config?path=gateway.port" }); + defer resp.deinit(std.testing.allocator); + try std.testing.expectEqual(std.http.Status.ok, resp.status); + try std.testing.expect(std.mem.indexOf(u8, resp.body, "\"value\":43123") != null); + } +} + test "integration harness covers orchestration proxy not configured" { var server = try IntegrationServer.start(std.testing.allocator); defer server.deinit(); diff --git a/src/server.zig b/src/server.zig index e88d029..cf2b1f2 100644 --- a/src/server.zig +++ b/src/server.zig @@ -701,7 +701,15 @@ pub const Server = struct { } if (std.mem.eql(u8, method, "PUT")) { if (settings_api.handlePutSettings(allocator, body)) |json| { - return jsonResponse(json); + const status = if (std.mem.indexOf(u8, json, "\"error\"") != null) + "400 Bad Request" + else + "200 OK"; + return .{ + .status = status, + .content_type = "application/json", + .body = json, + }; } else |_| { return .{ .status = "500 Internal Server Error",