Skip to content

Commit f841872

Browse files
committed
otlp: add test to validate body
Signed-off-by: inge4pres <[email protected]>
1 parent 0269237 commit f841872

File tree

2 files changed

+89
-2
lines changed

2 files changed

+89
-2
lines changed

src/otlp.zig

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,6 @@ const HTTPClient = struct {
378378
.allocator = allocator,
379379
.config = config,
380380
.client = http.Client{ .allocator = allocator },
381-
// .retry = try ExpBackoffRetry.init(allocator, config.retryConfig),
382381
};
383382

384383
return s;
@@ -670,7 +669,7 @@ pub fn Export(
670669
};
671670
}
672671

673-
// NOTE: This is **not used** in the current implementation, but it is here to show how it could be done.
672+
// NOTE: The following code **not used** in the current implementation, but it is here to show how it could be done.
674673

675674
// This is an attempt to implement a priority queue for the retryable requests.
676675
// The retryable requests are stored in a priority queue, sorted by the next attempt time.

src/otlp_test.zig

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ const otlp = @import("otlp.zig");
55

66
const ConfigOptions = otlp.ConfigOptions;
77

8+
const protobuf = @import("protobuf");
89
const pbcollector_metrics = @import("opentelemetry/proto/collector/metrics/v1.pb.zig");
10+
const pbcommon = @import("opentelemetry/proto/common/v1.pb.zig");
911
const pbmetrics = @import("opentelemetry/proto/metrics/v1.pb.zig");
1012

1113
test "otlp HTTPClient send fails on non-retryable error" {
@@ -72,6 +74,27 @@ test "otlp HTTPClient send retries on retryable error" {
7274
try std.testing.expectEqual(max_requests, req_counter.load(.acquire));
7375
}
7476

77+
test "otlp HTTPClient uncompressed protobuf metrics payload" {
78+
const allocator = std.testing.allocator;
79+
80+
var server = try HTTPTestServer.init(allocator, assertUncompressedProtobufMetricsBodyCanBeParsed);
81+
defer server.deinit();
82+
83+
const thread = try std.Thread.spawn(.{}, HTTPTestServer.processSingleRequest, .{server});
84+
defer thread.join();
85+
86+
const config = try ConfigOptions.init(allocator);
87+
defer config.deinit();
88+
const endpoint = try std.fmt.allocPrint(allocator, "127.0.0.1:{d}", .{server.port()});
89+
defer allocator.free(endpoint);
90+
config.endpoint = endpoint;
91+
92+
const req = try oneDataPointMetricsExportRequest(allocator);
93+
defer req.deinit();
94+
95+
try otlp.Export(allocator, config, otlp.Signal.Data{ .metrics = req });
96+
}
97+
7598
fn emptyMetricsExportRequest(allocator: std.mem.Allocator) !pbcollector_metrics.ExportMetricsServiceRequest {
7699
const rm = try allocator.alloc(pbmetrics.ResourceMetrics, 1);
77100
const rm0 = pbmetrics.ResourceMetrics{
@@ -86,9 +109,52 @@ fn emptyMetricsExportRequest(allocator: std.mem.Allocator) !pbcollector_metrics.
86109
return dummy;
87110
}
88111

112+
fn oneDataPointMetricsExportRequest(allocator: std.mem.Allocator) !pbcollector_metrics.ExportMetricsServiceRequest {
113+
var data_points = try allocator.alloc(pbmetrics.NumberDataPoint, 1);
114+
const data_points0 = pbmetrics.NumberDataPoint{
115+
.value = .{ .as_int = 42 },
116+
.start_time_unix_nano = @intCast(std.time.nanoTimestamp()),
117+
.attributes = std.ArrayList(pbcommon.KeyValue).init(allocator),
118+
.exemplars = std.ArrayList(pbmetrics.Exemplar).init(allocator),
119+
};
120+
data_points[0] = data_points0;
121+
var metrics = try allocator.alloc(pbmetrics.Metric, 1);
122+
const metrics0 = pbmetrics.Metric{
123+
.name = protobuf.ManagedString.managed("test_metric"),
124+
.data = .{ .gauge = .{ .data_points = std.ArrayList(pbmetrics.NumberDataPoint).fromOwnedSlice(allocator, data_points) } },
125+
.metadata = std.ArrayList(pbcommon.KeyValue).init(allocator),
126+
};
127+
metrics[0] = metrics0;
128+
129+
var scope_metrics = try allocator.alloc(pbmetrics.ScopeMetrics, 1);
130+
const scope_metrics0 = pbmetrics.ScopeMetrics{
131+
.scope = null,
132+
.metrics = std.ArrayList(pbmetrics.Metric).fromOwnedSlice(allocator, metrics),
133+
};
134+
scope_metrics[0] = scope_metrics0;
135+
136+
const rm = try allocator.alloc(pbmetrics.ResourceMetrics, 1);
137+
const rm0 = pbmetrics.ResourceMetrics{
138+
.resource = null,
139+
.scope_metrics = std.ArrayList(pbmetrics.ScopeMetrics).fromOwnedSlice(allocator, scope_metrics),
140+
};
141+
142+
rm[0] = rm0;
143+
const req = pbcollector_metrics.ExportMetricsServiceRequest{
144+
.resource_metrics = std.ArrayList(pbmetrics.ResourceMetrics).fromOwnedSlice(allocator, rm),
145+
};
146+
return req;
147+
}
148+
89149
// Type that defines the behavior of the mocked HTTP test server.
90150
const serverBehavior = *const fn (request: *http.Server.Request) anyerror!void;
91151

152+
const AssertionError = error{
153+
EmptyBody,
154+
ProtobufBodyMismatch,
155+
CompressionMismatch,
156+
};
157+
92158
fn badRequest(request: *http.Server.Request) anyerror!void {
93159
try request.respond("", .{ .status = .bad_request });
94160
}
@@ -97,6 +163,28 @@ fn tooManyRequests(request: *http.Server.Request) anyerror!void {
97163
try request.respond("", .{ .status = .too_many_requests });
98164
}
99165

166+
fn assertUncompressedProtobufMetricsBodyCanBeParsed(request: *http.Server.Request) anyerror!void {
167+
var allocator = std.testing.allocator;
168+
const reader = try request.reader();
169+
170+
const body = try reader.readAllAlloc(allocator, 8192);
171+
defer allocator.free(body);
172+
if (body.len == 0) {
173+
return AssertionError.EmptyBody;
174+
}
175+
176+
const proto = pbcollector_metrics.ExportMetricsServiceRequest.decode(body, allocator) catch |err| {
177+
std.debug.print("Error parsing proto: {}\n", .{err});
178+
return err;
179+
};
180+
defer proto.deinit();
181+
if (proto.resource_metrics.items.len != 1) {
182+
std.debug.print("otlp HTTP test - decoded protobuf: {}\n", proto);
183+
return AssertionError.ProtobufBodyMismatch;
184+
}
185+
try request.respond("", .{ .status = .ok });
186+
}
187+
100188
const HTTPTestServer = struct {
101189
const Self = @This();
102190

0 commit comments

Comments
 (0)