| // Copyright The OpenTelemetry Authors | |
| // SPDX-License-Identifier: Apache-2.0 | |
| package logtest | |
| import ( | |
| "errors" | |
| "fmt" | |
| "testing" | |
| "time" | |
| "github.com/stretchr/testify/assert" | |
| "github.com/stretchr/testify/require" | |
| "go.opentelemetry.io/otel/attribute" | |
| ) | |
| var ( | |
| y2k = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) | |
| errBoom = errors.New("boom") | |
| ) | |
| // These compile-time checks ensure that the testing structs implement TestingT. | |
| var ( | |
| _ TestingT = (*testing.T)(nil) | |
| _ TestingT = (*testing.B)(nil) | |
| _ TestingT = (*testing.F)(nil) | |
| _ TestingT = (*mockTestingT)(nil) | |
| ) | |
| type mockTestingT struct { | |
| errors []string | |
| } | |
| func (m *mockTestingT) Errorf(format string, args ...any) { | |
| m.errors = append(m.errors, fmt.Sprintf(format, args...)) | |
| } | |
| func TestAssertEqual(t *testing.T) { | |
| a := Recording{ | |
| Scope{Name: t.Name()}: []Record{ | |
| { | |
| Body: attribute.StringValue("msg"), | |
| Attributes: []attribute.KeyValue{attribute.String("foo", "bar"), attribute.Int("n", 1)}, | |
| }, | |
| }, | |
| } | |
| b := Recording{ | |
| Scope{Name: t.Name()}: []Record{ | |
| { | |
| Body: attribute.StringValue("msg"), | |
| Attributes: []attribute.KeyValue{attribute.Int("n", 1), attribute.String("foo", "bar")}, | |
| }, | |
| }, | |
| } | |
| got := AssertEqual(t, a, b) | |
| assert.True(t, got, "expected recordings to be equal") | |
| } | |
| func TestAssertEqualRecording(t *testing.T) { | |
| tests := []struct { | |
| name string | |
| a Recording | |
| b Recording | |
| opts []AssertOption | |
| want bool | |
| }{ | |
| { | |
| name: "equal recordings", | |
| a: Recording{ | |
| Scope{Name: t.Name()}: []Record{ | |
| { | |
| Timestamp: y2k, | |
| Context: t.Context(), | |
| Error: errBoom, | |
| Attributes: []attribute.KeyValue{attribute.Int("n", 1), attribute.String("foo", "bar")}, | |
| }, | |
| }, | |
| }, | |
| b: Recording{ | |
| Scope{Name: t.Name()}: []Record{ | |
| { | |
| Timestamp: y2k, | |
| Context: t.Context(), | |
| Error: errBoom, | |
| Attributes: []attribute.KeyValue{attribute.String("foo", "bar"), attribute.Int("n", 1)}, | |
| }, | |
| }, | |
| }, | |
| want: true, | |
| }, | |
| { | |
| name: "different recordings", | |
| a: Recording{ | |
| Scope{Name: t.Name()}: []Record{ | |
| {Attributes: []attribute.KeyValue{attribute.String("foo", "bar")}}, | |
| }, | |
| }, | |
| b: Recording{ | |
| Scope{Name: t.Name()}: []Record{ | |
| {Attributes: []attribute.KeyValue{attribute.Int("n", 1)}}, | |
| }, | |
| }, | |
| want: false, | |
| }, | |
| { | |
| name: "equal empty scopes", | |
| a: Recording{ | |
| Scope{Name: t.Name()}: nil, | |
| }, | |
| b: Recording{ | |
| Scope{Name: t.Name()}: []Record{}, | |
| }, | |
| want: true, | |
| }, | |
| { | |
| name: "equal empty attributes", | |
| a: Recording{ | |
| Scope{Name: t.Name()}: []Record{ | |
| {Body: attribute.StringValue("msg"), Attributes: []attribute.KeyValue{}}, | |
| }, | |
| }, | |
| b: Recording{ | |
| Scope{Name: t.Name()}: []Record{ | |
| {Body: attribute.StringValue("msg"), Attributes: nil}, | |
| }, | |
| }, | |
| want: true, | |
| }, | |
| } | |
| for _, tc := range tests { | |
| t.Run(tc.name, func(t *testing.T) { | |
| mockT := &mockTestingT{} | |
| result := AssertEqual(mockT, tc.a, tc.b, tc.opts...) | |
| if result != tc.want { | |
| t.Errorf("AssertEqual() = %v, want %v", result, tc.want) | |
| } | |
| if !tc.want && len(mockT.errors) == 0 { | |
| t.Errorf("expected Errorf call but got none") | |
| } | |
| }) | |
| } | |
| } | |
| func TestAssertEqualRecord(t *testing.T) { | |
| tests := []struct { | |
| name string | |
| a Record | |
| b Record | |
| opts []AssertOption | |
| want bool | |
| }{ | |
| { | |
| name: "equal records", | |
| a: Record{ | |
| Timestamp: y2k, | |
| Context: t.Context(), | |
| Error: errBoom, | |
| Attributes: []attribute.KeyValue{attribute.Int("n", 1), attribute.String("foo", "bar")}, | |
| }, | |
| b: Record{ | |
| Timestamp: y2k, | |
| Context: t.Context(), | |
| Error: errBoom, | |
| Attributes: []attribute.KeyValue{attribute.String("foo", "bar"), attribute.Int("n", 1)}, | |
| }, | |
| want: true, | |
| }, | |
| { | |
| name: "different records", | |
| a: Record{ | |
| Attributes: []attribute.KeyValue{attribute.String("foo", "bar")}, | |
| }, | |
| b: Record{ | |
| Attributes: []attribute.KeyValue{attribute.Int("n", 1)}, | |
| }, | |
| want: false, | |
| }, | |
| { | |
| name: "Transform to ignore timestamps", | |
| a: Record{ | |
| Attributes: []attribute.KeyValue{attribute.Int("n", 1), attribute.String("foo", "bar")}, | |
| }, | |
| b: Record{ | |
| Timestamp: y2k, | |
| Attributes: []attribute.KeyValue{attribute.String("foo", "bar"), attribute.Int("n", 1)}, | |
| }, | |
| opts: []AssertOption{ | |
| Transform(func(time.Time) time.Time { | |
| return time.Time{} | |
| }), | |
| }, | |
| want: true, | |
| }, | |
| } | |
| for _, tc := range tests { | |
| t.Run(tc.name, func(t *testing.T) { | |
| mockT := &mockTestingT{} | |
| result := AssertEqual(mockT, tc.a, tc.b, tc.opts...) | |
| if result != tc.want { | |
| t.Errorf("AssertEqual() = %v, want %v", result, tc.want) | |
| } | |
| if !tc.want && len(mockT.errors) == 0 { | |
| t.Errorf("expected Errorf call but got none") | |
| } | |
| }) | |
| } | |
| } | |
| func TestDesc(t *testing.T) { | |
| mockT := &mockTestingT{} | |
| a := Record{ | |
| Attributes: []attribute.KeyValue{attribute.String("foo", "bar")}, | |
| } | |
| b := Record{ | |
| Attributes: []attribute.KeyValue{attribute.Int("n", 1)}, | |
| } | |
| AssertEqual(mockT, a, b, Desc("custom message, %s", "test")) | |
| require.Len(t, mockT.errors, 1, "expected one error") | |
| assert.Contains(t, mockT.errors[0], "custom message, test\n", "expected custom message") | |
| } |