package testutil import ( "context" "fmt" "testing" "time" "git.logidex.ru/fakz9/logidex-id/internal/api/user/domain" "github.com/google/uuid" "github.com/stretchr/testify/assert" ) // TestContext creates a context with timeout for tests func TestContext(t *testing.T) context.Context { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) t.Cleanup(cancel) return ctx } // CreateTestUser creates a test user with default values func CreateTestUser(t *testing.T) *domain.User { return &domain.User{ Uuid: uuid.New().String(), PhoneNumber: "+79161234567", } } // CreateTestUserWithPhone creates a test user with specified phone number func CreateTestUserWithPhone(t *testing.T, phoneNumber string) *domain.User { return &domain.User{ Uuid: uuid.New().String(), PhoneNumber: phoneNumber, } } // AssertValidUUID checks if a string is a valid UUID func AssertValidUUID(t *testing.T, uuidStr string) { _, err := uuid.Parse(uuidStr) assert.NoError(t, err, "Expected valid UUID, got: %s", uuidStr) } // AssertValidPhoneNumber checks if a phone number follows expected format func AssertValidPhoneNumber(t *testing.T, phoneNumber string) { assert.NotEmpty(t, phoneNumber) assert.True(t, len(phoneNumber) >= 10, "Phone number should be at least 10 characters") assert.True(t, phoneNumber[0] == '+', "Phone number should start with +") } // TimeoutContext creates a context that times out after the specified duration // Note: This function returns a context without a cancel function. // Use TestContext(t) instead for proper cleanup in tests. func TimeoutContext(timeout time.Duration) (context.Context, context.CancelFunc) { return context.WithTimeout(context.Background(), timeout) } // ErrorsContain checks if an error contains a specific substring func ErrorsContain(t *testing.T, err error, substring string) { assert.Error(t, err) assert.Contains(t, err.Error(), substring) } // AssertNoError is a helper that fails the test if error is not nil func AssertNoError(t *testing.T, err error, msgAndArgs ...interface{}) { assert.NoError(t, err, msgAndArgs...) } // AssertError is a helper that fails the test if error is nil func AssertError(t *testing.T, err error, msgAndArgs ...interface{}) { assert.Error(t, err, msgAndArgs...) } // StringPtr returns a pointer to a string value func StringPtr(s string) *string { return &s } // IntPtr returns a pointer to an int value func IntPtr(i int) *int { return &i } // BoolPtr returns a pointer to a bool value func BoolPtr(b bool) *bool { return &b } // TestPhoneNumbers contains various phone numbers for testing var TestPhoneNumbers = struct { ValidRussian string ValidUS string ValidUK string Invalid string Empty string TooShort string WithSpaces string WithDashes string WithParentheses string }{ ValidRussian: "+79161234567", ValidUS: "+12345678901", ValidUK: "+441234567890", Invalid: "not-a-phone", Empty: "", TooShort: "123", WithSpaces: "+7 916 123 45 67", WithDashes: "+7-916-123-45-67", WithParentheses: "+7 (916) 123-45-67", } // TestUUIDs contains various UUIDs for testing var TestUUIDs = struct { Valid1 string Valid2 string Invalid string Empty string }{ Valid1: "123e4567-e89b-12d3-a456-426614174000", Valid2: "987fcdeb-51a2-43d1-9f12-345678901234", Invalid: "not-a-uuid", Empty: "", } // TestErrors contains common test errors var TestErrors = struct { Generic error NotFound error Invalid error Timeout error }{ Generic: assert.AnError, NotFound: domain.ErrUserNotFound{PhoneNumber: "+79161234567"}, Invalid: assert.AnError, Timeout: context.DeadlineExceeded, } // CleanupFunc is a function that cleans up test resources type CleanupFunc func() // SetupTestEnvironment sets up a test environment and returns cleanup function func SetupTestEnvironment(t *testing.T) CleanupFunc { // This could set up test databases, Redis connections, etc. // For now, it's a placeholder for future implementation return func() { // Cleanup resources } } // WaitFor waits for a condition to be true or timeout func WaitFor(t *testing.T, condition func() bool, timeout time.Duration, message string) { ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() timeoutChan := time.After(timeout) for { select { case <-ticker.C: if condition() { return } case <-timeoutChan: t.Fatalf("Timeout waiting for condition: %s", message) } } } // Eventually runs a function until it succeeds or times out func Eventually(t *testing.T, fn func() error, timeout time.Duration, message string) { ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() timeoutChan := time.After(timeout) var lastErr error for { select { case <-ticker.C: if err := fn(); err == nil { return } else { lastErr = err } case <-timeoutChan: t.Fatalf("Timeout waiting for function to succeed: %s. Last error: %v", message, lastErr) } } } // ParallelTest runs a test function in parallel multiple times func ParallelTest(t *testing.T, fn func(*testing.T), count int) { for i := 0; i < count; i++ { i := i // capture loop variable t.Run(fmt.Sprintf("parallel_%d", i), func(t *testing.T) { t.Parallel() fn(t) }) } } // BenchmarkHelper provides utilities for benchmark tests type BenchmarkHelper struct { b *testing.B } // NewBenchmarkHelper creates a new benchmark helper func NewBenchmarkHelper(b *testing.B) *BenchmarkHelper { return &BenchmarkHelper{b: b} } // ResetTimer resets the benchmark timer func (bh *BenchmarkHelper) ResetTimer() { bh.b.ResetTimer() } // StopTimer stops the benchmark timer func (bh *BenchmarkHelper) StopTimer() { bh.b.StopTimer() } // StartTimer starts the benchmark timer func (bh *BenchmarkHelper) StartTimer() { bh.b.StartTimer() }