From 646ca2cdbf84170a595c13df01f710a3aacfc690 Mon Sep 17 00:00:00 2001 From: Joao Grassi <5938087+joaopgrassi@users.noreply.github.com> Date: Tue, 26 Mar 2024 23:20:59 +0100 Subject: [PATCH] Add tests for csharp metrics --- internal/common/go.mod | 2 + internal/common/go.sum | 6 +- internal/common/testutils/metrics.go | 6 +- internal/common/testutils/types.go | 62 ++++--------------- src/csharp/metrics/console/App.cs | 13 ++-- src/csharp/metrics/console/Console.csproj | 2 +- src/csharp/metrics/console/Dockerfile | 4 +- .../metrics/console/collector-config.yaml | 17 +++++ src/csharp/metrics/console/docker-compose.yml | 37 +++++++++++ src/csharp/metrics/console/test/go.mod | 16 +++++ src/csharp/metrics/console/test/go.sum | 18 ++++++ .../metrics/console/test/metrics_test.go | 20 ++++++ 12 files changed, 140 insertions(+), 63 deletions(-) create mode 100644 src/csharp/metrics/console/collector-config.yaml create mode 100644 src/csharp/metrics/console/docker-compose.yml create mode 100644 src/csharp/metrics/console/test/go.mod create mode 100644 src/csharp/metrics/console/test/go.sum create mode 100644 src/csharp/metrics/console/test/metrics_test.go diff --git a/internal/common/go.mod b/internal/common/go.mod index 28f4ac3..8c4a7b2 100644 --- a/internal/common/go.mod +++ b/internal/common/go.mod @@ -6,6 +6,8 @@ require go.opentelemetry.io/proto/otlp v1.1.0 require google.golang.org/protobuf v1.33.0 +require github.com/google/go-cmp v0.5.8 // indirect + require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/internal/common/go.sum b/internal/common/go.sum index aef0f70..b715031 100644 --- a/internal/common/go.sum +++ b/internal/common/go.sum @@ -1,15 +1,13 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/internal/common/testutils/metrics.go b/internal/common/testutils/metrics.go index 398bc25..7576000 100644 --- a/internal/common/testutils/metrics.go +++ b/internal/common/testutils/metrics.go @@ -13,7 +13,7 @@ import ( "google.golang.org/protobuf/proto" ) -func AssertCounter(t *testing.T, tc *MetricTestCase, actualMetrics []*otlpmetrics.Metric) { +func AssertCounter[T Number](t *testing.T, tc *MetricTestCase[T], actualMetrics []*otlpmetrics.Metric) { // find metric by name m := findMetric(t, actualMetrics, tc.metricName) @@ -22,14 +22,14 @@ func AssertCounter(t *testing.T, tc *MetricTestCase, actualMetrics []*otlpmetric assert.Equal(t, tc.unit, m.GetUnit()) s := m.GetData().(*v1.Metric_Sum) dp := s.Sum.DataPoints[0] - assert.Equal(t, tc.value, dp.GetAsDouble()) + assert.Equal(t, tc.value, dp.GetAsInt()) for _, exp := range tc.attributes { assert.Contains(t, dp.Attributes, exp) } } -func AssertGauge(t *testing.T, tc *MetricTestCase, actualMetrics []*otlpmetrics.Metric) { +func AssertGauge[T Number](t *testing.T, tc *MetricTestCase[T], actualMetrics []*otlpmetrics.Metric) { // find metric by name m := findMetric(t, actualMetrics, tc.metricName) diff --git a/internal/common/testutils/types.go b/internal/common/testutils/types.go index 1d1d0bb..5f6005b 100644 --- a/internal/common/testutils/types.go +++ b/internal/common/testutils/types.go @@ -49,60 +49,24 @@ func NewSpanTest(options ...SpanTestOption) *SpanTest { } } -type MetricTestCase struct { +type Number interface { + int | int64 | float64 +} + +type MetricTestCase[T Number] struct { metricName string description string unit string - value float64 + value T attributes []*otlpcommon.KeyValue } -type MetricTestCaseOptions struct { - MetricName string - Description string - Unit string - Value float64 - Attributes []*otlpcommon.KeyValue -} - -type MetricTestCaseOption func(*MetricTestCaseOptions) - -func WithMetricName(name string) MetricTestCaseOption { - return func(s *MetricTestCaseOptions) { - s.MetricName = name - } -} -func WithMetricDescription(description string) MetricTestCaseOption { - return func(s *MetricTestCaseOptions) { - s.Description = description - } -} -func WithMetricUnit(unit string) MetricTestCaseOption { - return func(s *MetricTestCaseOptions) { - s.Unit = unit - } -} -func WithMetricValue(value float64) MetricTestCaseOption { - return func(s *MetricTestCaseOptions) { - s.Value = value - } -} -func WithMetricAttributes(attributes ...*otlpcommon.KeyValue) MetricTestCaseOption { - return func(s *MetricTestCaseOptions) { - s.Attributes = attributes - } -} -func NewMetricTestCase(options ...MetricTestCaseOption) *MetricTestCase { - opts := &MetricTestCaseOptions{} - for _, option := range options { - option(opts) - } - - return &MetricTestCase{ - metricName: opts.MetricName, - description: opts.Description, - unit: opts.Unit, - value: opts.Value, - attributes: opts.Attributes, +func NewMetricTestCase[T Number](name, description, unit string, value T, attributes ...*otlpcommon.KeyValue) *MetricTestCase[T] { + return &MetricTestCase[T]{ + metricName: name, + description: description, + unit: unit, + value: value, + attributes: attributes, } } diff --git a/src/csharp/metrics/console/App.cs b/src/csharp/metrics/console/App.cs index 11ed441..9de7dc0 100644 --- a/src/csharp/metrics/console/App.cs +++ b/src/csharp/metrics/console/App.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics.Metrics; using OpenTelemetry; using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; namespace Console { @@ -9,10 +11,10 @@ public class App { // Creates the Meter private static readonly Meter Meter = new("csharp.console.app", "1.0"); - + // Creates the Counter instrument private static readonly Counter MyCounter = Meter.CreateCounter("myCounter", "1", "I count things"); - + // Creates the Gauge instrument passing the callback that will produce the metric values static App() { @@ -24,7 +26,10 @@ public static void Main(string[] args) // Configures the SDK, exporting to a local running Collector using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter("csharp.console.app") - .AddOtlpExporter() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("csharp.console.app")) + .AddOtlpExporter(opts => { + opts.Endpoint = new Uri("http://collector-otel-recipes:4317"); + }) .Build(); // Add to our counter with an attribute diff --git a/src/csharp/metrics/console/Console.csproj b/src/csharp/metrics/console/Console.csproj index 77b53c9..7790488 100644 --- a/src/csharp/metrics/console/Console.csproj +++ b/src/csharp/metrics/console/Console.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 Console diff --git a/src/csharp/metrics/console/Dockerfile b/src/csharp/metrics/console/Dockerfile index f375a14..05c60ed 100644 --- a/src/csharp/metrics/console/Dockerfile +++ b/src/csharp/metrics/console/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /source # Copy csproj and restore as distinct layers @@ -9,7 +9,7 @@ COPY . ./ RUN dotnet publish -c Release -o /app --no-cache # final stage/image -FROM mcr.microsoft.com/dotnet/runtime:7.0 +FROM mcr.microsoft.com/dotnet/runtime:8.0 WORKDIR /app COPY --from=build /app ./ ENTRYPOINT ["dotnet", "Console.dll"] diff --git a/src/csharp/metrics/console/collector-config.yaml b/src/csharp/metrics/console/collector-config.yaml new file mode 100644 index 0000000..1ae6617 --- /dev/null +++ b/src/csharp/metrics/console/collector-config.yaml @@ -0,0 +1,17 @@ +receivers: + otlp: + protocols: + grpc: + http: +processors: +exporters: + debug: + verbosity: detailed + otlphttp: + endpoint: http://otlp-backend:4319 + compression: none +service: + pipelines: + metrics: + receivers: [otlp] + exporters: [otlphttp, debug] diff --git a/src/csharp/metrics/console/docker-compose.yml b/src/csharp/metrics/console/docker-compose.yml new file mode 100644 index 0000000..8f7142b --- /dev/null +++ b/src/csharp/metrics/console/docker-compose.yml @@ -0,0 +1,37 @@ +version: "2.4" +services: + + app: + build: + context: . + dockerfile: Dockerfile + depends_on: + - otlp-backend + - collector-otel-recipes + networks: + - otel-recipes + + otlp-backend: + build: + context: ../../../../internal/otlp_backend + dockerfile: Dockerfile + ports: + - "4319:4319" # OTLP HTTP receiver + networks: + - otel-recipes + + collector-otel-recipes: + image: otel/opentelemetry-collector-contrib:0.96.0 + command: ["--config=/etc/collector-config.yaml", "${OTELCOL_ARGS}"] + volumes: + - ./collector-config.yaml:/etc/collector-config.yaml + ports: + - "13133:13133" # health_check extension + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver + depends_on: + - otlp-backend + networks: + - otel-recipes +networks: + otel-recipes: diff --git a/src/csharp/metrics/console/test/go.mod b/src/csharp/metrics/console/test/go.mod new file mode 100644 index 0000000..d7af705 --- /dev/null +++ b/src/csharp/metrics/console/test/go.mod @@ -0,0 +1,16 @@ +module github.com/joaopgrassi/otel-recipes/csharp/metrics/console + +go 1.22.1 + +require github.com/joaopgrassi/otel-recipes/internal/common v0.0.0 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.9.0 // indirect + go.opentelemetry.io/proto/otlp v1.1.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/joaopgrassi/otel-recipes/internal/common v0.0.0 => ../../../../../internal/common diff --git a/src/csharp/metrics/console/test/go.sum b/src/csharp/metrics/console/test/go.sum new file mode 100644 index 0000000..aef0f70 --- /dev/null +++ b/src/csharp/metrics/console/test/go.sum @@ -0,0 +1,18 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/csharp/metrics/console/test/metrics_test.go b/src/csharp/metrics/console/test/metrics_test.go new file mode 100644 index 0000000..1a5098e --- /dev/null +++ b/src/csharp/metrics/console/test/metrics_test.go @@ -0,0 +1,20 @@ +package test + +import ( + "testing" + + tu "github.com/joaopgrassi/otel-recipes/internal/common/testutils" +) + +func TestMetricsGeneratedFromSample(t *testing.T) { + rm := tu.GetMetricsWithRetry(t, "csharp.console.app") + m := rm.GetScopeMetrics()[0].Metrics + + // Counter metric + ctc := tu.NewMetricTestCase("myCounter", "I count things", "1", int64(3), tu.StringAttribute("foo", "bar")) + tu.AssertCounter(t, ctc, m) + + // Gauge metric + ctg := tu.NewMetricTestCase("myGauge", "I gauge things", "1", float64(3.5), tu.StringAttribute("foo", "bar")) + tu.AssertGauge(t, ctg, m) +}