@@ -5,7 +5,9 @@ const otlp = @import("otlp.zig");
5
5
6
6
const ConfigOptions = otlp .ConfigOptions ;
7
7
8
+ const protobuf = @import ("protobuf" );
8
9
const pbcollector_metrics = @import ("opentelemetry/proto/collector/metrics/v1.pb.zig" );
10
+ const pbcommon = @import ("opentelemetry/proto/common/v1.pb.zig" );
9
11
const pbmetrics = @import ("opentelemetry/proto/metrics/v1.pb.zig" );
10
12
11
13
test "otlp HTTPClient send fails on non-retryable error" {
@@ -72,6 +74,27 @@ test "otlp HTTPClient send retries on retryable error" {
72
74
try std .testing .expectEqual (max_requests , req_counter .load (.acquire ));
73
75
}
74
76
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
+
75
98
fn emptyMetricsExportRequest (allocator : std.mem.Allocator ) ! pbcollector_metrics.ExportMetricsServiceRequest {
76
99
const rm = try allocator .alloc (pbmetrics .ResourceMetrics , 1 );
77
100
const rm0 = pbmetrics.ResourceMetrics {
@@ -86,9 +109,52 @@ fn emptyMetricsExportRequest(allocator: std.mem.Allocator) !pbcollector_metrics.
86
109
return dummy ;
87
110
}
88
111
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
+
89
149
// Type that defines the behavior of the mocked HTTP test server.
90
150
const serverBehavior = * const fn (request : * http.Server.Request ) anyerror ! void ;
91
151
152
+ const AssertionError = error {
153
+ EmptyBody ,
154
+ ProtobufBodyMismatch ,
155
+ CompressionMismatch ,
156
+ };
157
+
92
158
fn badRequest (request : * http.Server.Request ) anyerror ! void {
93
159
try request .respond ("" , .{ .status = .bad_request });
94
160
}
@@ -97,6 +163,28 @@ fn tooManyRequests(request: *http.Server.Request) anyerror!void {
97
163
try request .respond ("" , .{ .status = .too_many_requests });
98
164
}
99
165
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
+
100
188
const HTTPTestServer = struct {
101
189
const Self = @This ();
102
190
0 commit comments