// 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/log" ) var ( y2k = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) errBoom = errors.New("boom") ) // Compile-time check to ensure 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: log.StringValue("msg"), Attributes: []log.KeyValue{log.String("foo", "bar"), log.Int("n", 1)}}, }, } b := Recording{ Scope{Name: t.Name()}: []Record{ {Body: log.StringValue("msg"), Attributes: []log.KeyValue{log.Int("n", 1), log.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: []log.KeyValue{log.Int("n", 1), log.String("foo", "bar")}, }, }, }, b: Recording{ Scope{Name: t.Name()}: []Record{ { Timestamp: y2k, Context: t.Context(), Error: errBoom, Attributes: []log.KeyValue{log.String("foo", "bar"), log.Int("n", 1)}, }, }, }, want: true, }, { name: "different recordings", a: Recording{ Scope{Name: t.Name()}: []Record{ {Attributes: []log.KeyValue{log.String("foo", "bar")}}, }, }, b: Recording{ Scope{Name: t.Name()}: []Record{ {Attributes: []log.KeyValue{log.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: log.StringValue("msg"), Attributes: []log.KeyValue{}}, }, }, b: Recording{ Scope{Name: t.Name()}: []Record{ {Body: log.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: []log.KeyValue{log.Int("n", 1), log.String("foo", "bar")}, }, b: Record{ Timestamp: y2k, Context: t.Context(), Error: errBoom, Attributes: []log.KeyValue{log.String("foo", "bar"), log.Int("n", 1)}, }, want: true, }, { name: "different records", a: Record{ Attributes: []log.KeyValue{log.String("foo", "bar")}, }, b: Record{ Attributes: []log.KeyValue{log.Int("n", 1)}, }, want: false, }, { name: "Transform to ignore timestamps", a: Record{ Attributes: []log.KeyValue{log.Int("n", 1), log.String("foo", "bar")}, }, b: Record{ Timestamp: y2k, Attributes: []log.KeyValue{log.String("foo", "bar"), log.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: []log.KeyValue{log.String("foo", "bar")}, } b := Record{ Attributes: []log.KeyValue{log.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") }