From bf5de183333eedd742b690afd040f9c3849d40e6 Mon Sep 17 00:00:00 2001 From: Manas Srivastava Date: Sun, 26 Apr 2026 12:12:50 +0530 Subject: [PATCH] chore(utilities): add tests for previously-uncovered helpers internal/utilities/strings.go, context.go, and io.go held small pure-ish helpers but had no test coverage. Add focused unit tests so behaviour changes get caught: round-trip tests for StringValue and StringPtr; a context-key formatting check plus set/read/replace/derive cases for WithRequestID and GetRequestID; cancellation and completion cases for WaitForCleanup; and behaviour assertions for SafeClose covering the happy path, the error path, and io.NopCloser. Coverage for the package goes from 50.8% to 62.4% with no behaviour changes. Signed-off-by: Manas Srivastava --- internal/utilities/context_test.go | 84 ++++++++++++++++++++++++ internal/utilities/io_test.go | 50 +++++++++++++++ internal/utilities/strings_test.go | 100 +++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 internal/utilities/context_test.go create mode 100644 internal/utilities/io_test.go create mode 100644 internal/utilities/strings_test.go diff --git a/internal/utilities/context_test.go b/internal/utilities/context_test.go new file mode 100644 index 0000000000..ebf26cc88e --- /dev/null +++ b/internal/utilities/context_test.go @@ -0,0 +1,84 @@ +package utilities + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestContextKeyString(t *testing.T) { + require.Equal(t, "gotrue api context key request_id", contextKey("request_id").String()) + require.Equal(t, "gotrue api context key ", contextKey("").String()) +} + +func TestRequestIDRoundtrip(t *testing.T) { + t.Run("set then read", func(t *testing.T) { + ctx := WithRequestID(context.Background(), "abc-123") + require.Equal(t, "abc-123", GetRequestID(ctx)) + }) + + t.Run("missing key returns empty string", func(t *testing.T) { + require.Equal(t, "", GetRequestID(context.Background())) + }) + + t.Run("set replaces previous value", func(t *testing.T) { + ctx := WithRequestID(context.Background(), "first") + ctx = WithRequestID(ctx, "second") + require.Equal(t, "second", GetRequestID(ctx)) + }) + + t.Run("derived context inherits value", func(t *testing.T) { + ctx := WithRequestID(context.Background(), "parent-id") + child, cancel := context.WithCancel(ctx) + defer cancel() + require.Equal(t, "parent-id", GetRequestID(child)) + }) +} + +func TestWaitForCleanup(t *testing.T) { + t.Run("returns when wait group is done", func(t *testing.T) { + var wg sync.WaitGroup + wg.Add(1) + go func() { + time.Sleep(10 * time.Millisecond) + wg.Done() + }() + + done := make(chan struct{}) + go func() { + defer close(done) + WaitForCleanup(context.Background(), &wg) + }() + + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("WaitForCleanup did not return after wg.Done()") + } + }) + + t.Run("returns when context is cancelled before wait group is done", func(t *testing.T) { + var wg sync.WaitGroup + wg.Add(1) + + ctx, cancel := context.WithCancel(context.Background()) + done := make(chan struct{}) + go func() { + defer close(done) + WaitForCleanup(ctx, &wg) + }() + + cancel() + + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("WaitForCleanup did not return after context cancellation") + } + + wg.Done() + }) +} diff --git a/internal/utilities/io_test.go b/internal/utilities/io_test.go new file mode 100644 index 0000000000..b401a96963 --- /dev/null +++ b/internal/utilities/io_test.go @@ -0,0 +1,50 @@ +package utilities + +import ( + "errors" + "io" + "testing" + + "github.com/stretchr/testify/require" +) + +type closerFunc func() error + +func (f closerFunc) Close() error { return f() } + +func TestSafeClose(t *testing.T) { + tests := []struct { + name string + closer io.Closer + closerErr error + }{ + { + name: "happy path: Close returns nil", + closerErr: nil, + }, + { + name: "Close returns error: SafeClose must not panic", + closerErr: errors.New("close failed"), + }, + { + name: "io.NopCloser: SafeClose must not panic", + closer: io.NopCloser(nil), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + closer := tt.closer + if closer == nil { + called := false + closer = closerFunc(func() error { + called = true + return tt.closerErr + }) + require.NotPanics(t, func() { SafeClose(closer) }) + require.True(t, called, "Close should have been invoked") + return + } + require.NotPanics(t, func() { SafeClose(closer) }) + }) + } +} diff --git a/internal/utilities/strings_test.go b/internal/utilities/strings_test.go new file mode 100644 index 0000000000..272f3a1dc4 --- /dev/null +++ b/internal/utilities/strings_test.go @@ -0,0 +1,100 @@ +package utilities + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestStringValue(t *testing.T) { + empty := "" + hello := "hello" + tests := []struct { + name string + in *string + want string + }{ + { + name: "nil pointer returns empty string", + in: nil, + want: "", + }, + { + name: "pointer to empty string returns empty string", + in: &empty, + want: "", + }, + { + name: "pointer to non-empty string returns the value", + in: &hello, + want: "hello", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, StringValue(tt.in)) + }) + } +} + +func TestStringPtr(t *testing.T) { + tests := []struct { + name string + in string + expectNil bool + wantPointee string + }{ + { + name: "empty string returns nil", + in: "", + expectNil: true, + }, + { + name: "non-empty string returns pointer to that value", + in: "hello", + wantPointee: "hello", + }, + { + name: "string with whitespace and newline preserved", + in: " hi\nthere ", + wantPointee: " hi\nthere ", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := StringPtr(tt.in) + if tt.expectNil { + require.Nil(t, got) + return + } + require.NotNil(t, got) + require.Equal(t, tt.wantPointee, *got) + }) + } + + t.Run("returned pointer is independent of the input variable", func(t *testing.T) { + s := "original" + p := StringPtr(s) + require.Equal(t, "original", *p) + s = "mutated" + require.Equal(t, "original", *p) + }) +} + +func TestStringValueAndStringPtrRoundtrip(t *testing.T) { + tests := []struct { + name string + in string + }{ + {name: "empty", in: ""}, + {name: "ascii", in: "hello"}, + {name: "with spaces", in: "with spaces"}, + {name: "with newline", in: "with\nnewline"}, + {name: "multibyte rune", in: "🦄"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.in, StringValue(StringPtr(tt.in))) + }) + } +}