To gitea and beyond, let's go(-yco)
This commit is contained in:
822
internal/services/common_test.go
Normal file
822
internal/services/common_test.go
Normal file
@@ -0,0 +1,822 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"goyco/internal/config"
|
||||
"goyco/internal/repositories"
|
||||
"goyco/internal/testutils"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func TestTrimString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "no_whitespace",
|
||||
input: "test",
|
||||
expected: "test",
|
||||
},
|
||||
{
|
||||
name: "leading_spaces",
|
||||
input: " test",
|
||||
expected: "test",
|
||||
},
|
||||
{
|
||||
name: "trailing_spaces",
|
||||
input: "test ",
|
||||
expected: "test",
|
||||
},
|
||||
{
|
||||
name: "leading_and_trailing_spaces",
|
||||
input: " test ",
|
||||
expected: "test",
|
||||
},
|
||||
{
|
||||
name: "only_spaces",
|
||||
input: " ",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "empty_string",
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "tabs_and_newlines",
|
||||
input: "\t\n test \n\t",
|
||||
expected: "test",
|
||||
},
|
||||
{
|
||||
name: "internal_spaces_preserved",
|
||||
input: " test string ",
|
||||
expected: "test string",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := TrimString(tt.input)
|
||||
if result != tt.expected {
|
||||
t.Errorf("TrimString(%q) = %q, want %q", tt.input, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashPassword(t *testing.T) {
|
||||
t.Run("successful_hash", func(t *testing.T) {
|
||||
password := "testpassword123"
|
||||
hashed, err := HashPassword(password, DefaultBcryptCost)
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword() error = %v, want no error", err)
|
||||
}
|
||||
if hashed == "" {
|
||||
t.Error("HashPassword() returned empty string")
|
||||
}
|
||||
if hashed == password {
|
||||
t.Error("HashPassword() returned plain password")
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(hashed), []byte(password))
|
||||
if err != nil {
|
||||
t.Errorf("HashPassword() produced invalid hash: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("different_passwords_produce_different_hashes", func(t *testing.T) {
|
||||
password1 := "password1"
|
||||
password2 := "password2"
|
||||
|
||||
hash1, err := HashPassword(password1, DefaultBcryptCost)
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword() error = %v", err)
|
||||
}
|
||||
|
||||
hash2, err := HashPassword(password2, DefaultBcryptCost)
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword() error = %v", err)
|
||||
}
|
||||
|
||||
if hash1 == hash2 {
|
||||
t.Error("Different passwords produced same hash")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("same_password_produces_different_hashes", func(t *testing.T) {
|
||||
password := "samepassword"
|
||||
hash1, err := HashPassword(password, DefaultBcryptCost)
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword() error = %v", err)
|
||||
}
|
||||
|
||||
hash2, err := HashPassword(password, DefaultBcryptCost)
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword() error = %v", err)
|
||||
}
|
||||
|
||||
if hash1 == hash2 {
|
||||
t.Error("Same password produced same hash (should be different due to salt)")
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(hash1), []byte(password)); err != nil {
|
||||
t.Errorf("First hash doesn't verify: %v", err)
|
||||
}
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(hash2), []byte(password)); err != nil {
|
||||
t.Errorf("Second hash doesn't verify: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("custom_cost", func(t *testing.T) {
|
||||
password := "testpassword"
|
||||
customCost := 12
|
||||
|
||||
hashed, err := HashPassword(password, customCost)
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword() error = %v", err)
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(hashed), []byte(password))
|
||||
if err != nil {
|
||||
t.Errorf("HashPassword() with custom cost produced invalid hash: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("zero_cost_uses_default", func(t *testing.T) {
|
||||
password := "testpassword"
|
||||
|
||||
hashed, err := HashPassword(password, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword() error = %v", err)
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(hashed), []byte(password))
|
||||
if err != nil {
|
||||
t.Errorf("HashPassword() with zero cost produced invalid hash: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("negative_cost_uses_default", func(t *testing.T) {
|
||||
password := "testpassword"
|
||||
|
||||
hashed, err := HashPassword(password, -1)
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword() error = %v", err)
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(hashed), []byte(password))
|
||||
if err != nil {
|
||||
t.Errorf("HashPassword() with negative cost produced invalid hash: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty_password", func(t *testing.T) {
|
||||
hashed, err := HashPassword("", DefaultBcryptCost)
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword() error = %v", err)
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(hashed), []byte(""))
|
||||
if err != nil {
|
||||
t.Errorf("HashPassword() with empty password produced invalid hash: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsRecordNotFound(t *testing.T) {
|
||||
t.Run("gorm_record_not_found", func(t *testing.T) {
|
||||
err := gorm.ErrRecordNotFound
|
||||
if !IsRecordNotFound(err) {
|
||||
t.Error("IsRecordNotFound() = false, want true for gorm.ErrRecordNotFound")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("wrapped_gorm_record_not_found", func(t *testing.T) {
|
||||
err := errors.New("some context")
|
||||
wrappedErr := errors.Join(err, gorm.ErrRecordNotFound)
|
||||
if !IsRecordNotFound(wrappedErr) {
|
||||
t.Error("IsRecordNotFound() = false, want true for wrapped gorm.ErrRecordNotFound")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("other_error", func(t *testing.T) {
|
||||
err := errors.New("some other error")
|
||||
if IsRecordNotFound(err) {
|
||||
t.Error("IsRecordNotFound() = true, want false for other error")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nil_error", func(t *testing.T) {
|
||||
if IsRecordNotFound(nil) {
|
||||
t.Error("IsRecordNotFound() = true, want false for nil error")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHandleUniqueConstraintError(t *testing.T) {
|
||||
t.Run("nil_error", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintError(nil)
|
||||
if err != nil {
|
||||
t.Errorf("HandleUniqueConstraintError(nil) = %v, want nil", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("non_pq_error", func(t *testing.T) {
|
||||
originalErr := errors.New("some other error")
|
||||
err := HandleUniqueConstraintError(originalErr)
|
||||
if err != originalErr {
|
||||
t.Errorf("HandleUniqueConstraintError() = %v, want %v", err, originalErr)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("pq_error_wrong_code", func(t *testing.T) {
|
||||
pqErr := &pq.Error{
|
||||
Code: "23503",
|
||||
Message: "some error",
|
||||
}
|
||||
err := HandleUniqueConstraintError(pqErr)
|
||||
if err != pqErr {
|
||||
t.Errorf("HandleUniqueConstraintError() = %v, want %v", err, pqErr)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("username_constraint_in_constraint_field", func(t *testing.T) {
|
||||
pqErr := &pq.Error{
|
||||
Code: "23505",
|
||||
Constraint: "users_username_key",
|
||||
Message: "duplicate key value violates unique constraint",
|
||||
}
|
||||
err := HandleUniqueConstraintError(pqErr)
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintError() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("username_constraint_in_message", func(t *testing.T) {
|
||||
pqErr := &pq.Error{
|
||||
Code: "23505",
|
||||
Constraint: "some_constraint",
|
||||
Message: "duplicate key value violates unique constraint users.username",
|
||||
}
|
||||
err := HandleUniqueConstraintError(pqErr)
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintError() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("username_constraint_case_insensitive", func(t *testing.T) {
|
||||
pqErr := &pq.Error{
|
||||
Code: "23505",
|
||||
Constraint: "USERS_USERNAME_KEY",
|
||||
Message: "DUPLICATE KEY VALUE VIOLATES UNIQUE CONSTRAINT",
|
||||
}
|
||||
err := HandleUniqueConstraintError(pqErr)
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintError() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("email_constraint_in_constraint_field", func(t *testing.T) {
|
||||
pqErr := &pq.Error{
|
||||
Code: "23505",
|
||||
Constraint: "users_email_key",
|
||||
Message: "duplicate key value violates unique constraint",
|
||||
}
|
||||
err := HandleUniqueConstraintError(pqErr)
|
||||
if !errors.Is(err, ErrEmailTaken) {
|
||||
t.Errorf("HandleUniqueConstraintError() = %v, want ErrEmailTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("email_constraint_in_message", func(t *testing.T) {
|
||||
pqErr := &pq.Error{
|
||||
Code: "23505",
|
||||
Constraint: "some_constraint",
|
||||
Message: "duplicate key value violates unique constraint users.email",
|
||||
}
|
||||
err := HandleUniqueConstraintError(pqErr)
|
||||
if !errors.Is(err, ErrEmailTaken) {
|
||||
t.Errorf("HandleUniqueConstraintError() = %v, want ErrEmailTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("email_constraint_case_insensitive", func(t *testing.T) {
|
||||
pqErr := &pq.Error{
|
||||
Code: "23505",
|
||||
Constraint: "USERS_EMAIL_KEY",
|
||||
Message: "DUPLICATE KEY VALUE VIOLATES UNIQUE CONSTRAINT",
|
||||
}
|
||||
err := HandleUniqueConstraintError(pqErr)
|
||||
if !errors.Is(err, ErrEmailTaken) {
|
||||
t.Errorf("HandleUniqueConstraintError() = %v, want ErrEmailTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("unknown_unique_constraint_defaults_to_username", func(t *testing.T) {
|
||||
pqErr := &pq.Error{
|
||||
Code: "23505",
|
||||
Constraint: "some_other_constraint",
|
||||
Message: "duplicate key value violates unique constraint",
|
||||
}
|
||||
err := HandleUniqueConstraintError(pqErr)
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintError() = %v, want ErrUsernameTaken (default)", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHandleUniqueConstraintErrorWithMessage(t *testing.T) {
|
||||
t.Run("nil_error", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(nil)
|
||||
if err != nil {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage(nil) = %v, want nil", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("pq_error_username_handled_by_HandleUniqueConstraintError", func(t *testing.T) {
|
||||
pqErr := &pq.Error{
|
||||
Code: "23505",
|
||||
Constraint: "users_username_key",
|
||||
Message: "duplicate key value violates unique constraint",
|
||||
}
|
||||
err := HandleUniqueConstraintErrorWithMessage(pqErr)
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("pq_error_email_handled_by_HandleUniqueConstraintError", func(t *testing.T) {
|
||||
pqErr := &pq.Error{
|
||||
Code: "23505",
|
||||
Constraint: "users_email_key",
|
||||
Message: "duplicate key value violates unique constraint",
|
||||
}
|
||||
err := HandleUniqueConstraintErrorWithMessage(pqErr)
|
||||
if !errors.Is(err, ErrEmailTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrEmailTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message_based_duplicate_key_username", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("duplicate key value violates unique constraint username"))
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message_based_unique_constraint_username", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("unique constraint failed on username"))
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message_based_violates_unique_constraint_username", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("violates unique constraint users_username_key"))
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message_based_unique_constraint_failed_username", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("unique constraint failed on users.username"))
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message_based_constraint_failed_username", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("constraint failed on username"))
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message_based_constraint_and_unique_username", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("constraint unique username failed"))
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message_based_duplicate_key_email", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("duplicate key value violates unique constraint email"))
|
||||
if !errors.Is(err, ErrEmailTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrEmailTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message_based_unique_constraint_email", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("unique constraint failed on users_email_key"))
|
||||
if !errors.Is(err, ErrEmailTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrEmailTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message_based_violates_unique_constraint_email", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("violates unique constraint users.email"))
|
||||
if !errors.Is(err, ErrEmailTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrEmailTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message_based_case_insensitive_username", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("DUPLICATE KEY VALUE VIOLATES UNIQUE CONSTRAINT USERNAME"))
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("message_based_case_insensitive_email", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("DUPLICATE KEY VALUE VIOLATES UNIQUE CONSTRAINT EMAIL"))
|
||||
if !errors.Is(err, ErrEmailTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrEmailTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("non_unique_error_passed_through", func(t *testing.T) {
|
||||
originalErr := errors.New("some other database error")
|
||||
err := HandleUniqueConstraintErrorWithMessage(originalErr)
|
||||
if err != originalErr {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want %v", err, originalErr)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("unique_error_without_username_or_email_defaults_to_username", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("duplicate key value violates unique constraint"))
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrUsernameTaken (default)", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("wrapped_pq_error", func(t *testing.T) {
|
||||
pqErr := &pq.Error{
|
||||
Code: "23505",
|
||||
Constraint: "users_username_key",
|
||||
Message: "duplicate key value violates unique constraint",
|
||||
}
|
||||
wrappedErr := errors.New("context: " + pqErr.Error())
|
||||
err := HandleUniqueConstraintErrorWithMessage(wrappedErr)
|
||||
if err == nil {
|
||||
t.Error("HandleUniqueConstraintErrorWithMessage() returned nil, expected error")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHandleUniqueConstraintErrorWithMessage_EdgeCases(t *testing.T) {
|
||||
t.Run("mixed_case_username_in_message", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("duplicate key value violates unique constraint UserName"))
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("mixed_case_email_in_message", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("duplicate key value violates unique constraint Email"))
|
||||
if !errors.Is(err, ErrEmailTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrEmailTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("username_substring_not_matched", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("duplicate key value violates unique constraint usernames"))
|
||||
if !errors.Is(err, ErrUsernameTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrUsernameTaken", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("email_substring_not_matched", func(t *testing.T) {
|
||||
err := HandleUniqueConstraintErrorWithMessage(errors.New("duplicate key value violates unique constraint emails"))
|
||||
if !errors.Is(err, ErrEmailTaken) {
|
||||
t.Errorf("HandleUniqueConstraintErrorWithMessage() = %v, want ErrEmailTaken", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkTrimString(b *testing.B) {
|
||||
input := " test string with spaces "
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = TrimString(input)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHashPassword(b *testing.B) {
|
||||
password := "testpassword123"
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = HashPassword(password, DefaultBcryptCost)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsRecordNotFound(b *testing.B) {
|
||||
err := gorm.ErrRecordNotFound
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = IsRecordNotFound(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAuthFacadeForTest(t *testing.T) {
|
||||
t.Run("successful_creation", func(t *testing.T) {
|
||||
db := testutils.NewTestDB(t)
|
||||
defer func() {
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.Close()
|
||||
}()
|
||||
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
postRepo := repositories.NewPostRepository(db)
|
||||
deletionRepo := repositories.NewAccountDeletionRepository(db)
|
||||
refreshTokenRepo := repositories.NewRefreshTokenRepository(db)
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
|
||||
authFacade, err := NewAuthFacadeForTest(testutils.AppTestConfig, userRepo, postRepo, deletionRepo, refreshTokenRepo, emailSender)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAuthFacadeForTest() error = %v, want no error", err)
|
||||
}
|
||||
|
||||
if authFacade == nil {
|
||||
t.Fatal("NewAuthFacadeForTest() returned nil AuthFacade")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("successful_creation_with_custom_config", func(t *testing.T) {
|
||||
db := testutils.NewTestDB(t)
|
||||
defer func() {
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.Close()
|
||||
}()
|
||||
|
||||
customConfig := &config.Config{
|
||||
JWT: config.JWTConfig{
|
||||
Secret: "custom-secret-key-for-testing-purposes-only",
|
||||
Expiration: 48,
|
||||
},
|
||||
App: config.AppConfig{
|
||||
BaseURL: "http://localhost:3000",
|
||||
BcryptCost: 12,
|
||||
},
|
||||
}
|
||||
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
postRepo := repositories.NewPostRepository(db)
|
||||
deletionRepo := repositories.NewAccountDeletionRepository(db)
|
||||
refreshTokenRepo := repositories.NewRefreshTokenRepository(db)
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
|
||||
authFacade, err := NewAuthFacadeForTest(customConfig, userRepo, postRepo, deletionRepo, refreshTokenRepo, emailSender)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAuthFacadeForTest() error = %v, want no error", err)
|
||||
}
|
||||
|
||||
if authFacade == nil {
|
||||
t.Fatal("NewAuthFacadeForTest() returned nil AuthFacade")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("error_on_empty_base_url", func(t *testing.T) {
|
||||
db := testutils.NewTestDB(t)
|
||||
defer func() {
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.Close()
|
||||
}()
|
||||
|
||||
invalidConfig := &config.Config{
|
||||
JWT: config.JWTConfig{
|
||||
Secret: "test-secret-key",
|
||||
Expiration: 24,
|
||||
},
|
||||
App: config.AppConfig{
|
||||
BaseURL: "",
|
||||
BcryptCost: 10,
|
||||
},
|
||||
}
|
||||
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
postRepo := repositories.NewPostRepository(db)
|
||||
deletionRepo := repositories.NewAccountDeletionRepository(db)
|
||||
refreshTokenRepo := repositories.NewRefreshTokenRepository(db)
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
|
||||
authFacade, err := NewAuthFacadeForTest(invalidConfig, userRepo, postRepo, deletionRepo, refreshTokenRepo, emailSender)
|
||||
if err == nil {
|
||||
t.Fatal("NewAuthFacadeForTest() expected error for empty base URL, got nil")
|
||||
}
|
||||
|
||||
if authFacade != nil {
|
||||
t.Fatal("NewAuthFacadeForTest() expected nil AuthFacade on error, got non-nil")
|
||||
}
|
||||
|
||||
if err.Error() != "create email service: APP_BASE_URL is required and must be externally reachable" {
|
||||
t.Errorf("NewAuthFacadeForTest() error = %v, want error about APP_BASE_URL", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("error_on_whitespace_only_base_url", func(t *testing.T) {
|
||||
db := testutils.NewTestDB(t)
|
||||
defer func() {
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.Close()
|
||||
}()
|
||||
|
||||
invalidConfig := &config.Config{
|
||||
JWT: config.JWTConfig{
|
||||
Secret: "test-secret-key",
|
||||
Expiration: 24,
|
||||
},
|
||||
App: config.AppConfig{
|
||||
BaseURL: " ",
|
||||
BcryptCost: 10,
|
||||
},
|
||||
}
|
||||
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
postRepo := repositories.NewPostRepository(db)
|
||||
deletionRepo := repositories.NewAccountDeletionRepository(db)
|
||||
refreshTokenRepo := repositories.NewRefreshTokenRepository(db)
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
|
||||
authFacade, err := NewAuthFacadeForTest(invalidConfig, userRepo, postRepo, deletionRepo, refreshTokenRepo, emailSender)
|
||||
if err == nil {
|
||||
t.Fatal("NewAuthFacadeForTest() expected error for whitespace-only base URL, got nil")
|
||||
}
|
||||
|
||||
if authFacade != nil {
|
||||
t.Fatal("NewAuthFacadeForTest() expected nil AuthFacade on error, got non-nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("facade_can_perform_operations", func(t *testing.T) {
|
||||
db := testutils.NewTestDB(t)
|
||||
defer func() {
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.Close()
|
||||
}()
|
||||
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
postRepo := repositories.NewPostRepository(db)
|
||||
deletionRepo := repositories.NewAccountDeletionRepository(db)
|
||||
refreshTokenRepo := repositories.NewRefreshTokenRepository(db)
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
|
||||
authFacade, err := NewAuthFacadeForTest(testutils.AppTestConfig, userRepo, postRepo, deletionRepo, refreshTokenRepo, emailSender)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAuthFacadeForTest() error = %v, want no error", err)
|
||||
}
|
||||
|
||||
result, err := authFacade.Register("testuser", "test@example.com", "SecurePass123!")
|
||||
if err != nil {
|
||||
t.Fatalf("AuthFacade.Register() error = %v, want no error", err)
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
t.Fatal("AuthFacade.Register() returned nil result")
|
||||
}
|
||||
|
||||
if result.User.Username != "testuser" {
|
||||
t.Errorf("AuthFacade.Register() username = %v, want 'testuser'", result.User.Username)
|
||||
}
|
||||
|
||||
if result.User.Email != "test@example.com" {
|
||||
t.Errorf("AuthFacade.Register() email = %v, want 'test@example.com'", result.User.Email)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("facade_can_login", func(t *testing.T) {
|
||||
db := testutils.NewTestDB(t)
|
||||
defer func() {
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.Close()
|
||||
}()
|
||||
|
||||
userRepo := repositories.NewUserRepository(db)
|
||||
postRepo := repositories.NewPostRepository(db)
|
||||
deletionRepo := repositories.NewAccountDeletionRepository(db)
|
||||
refreshTokenRepo := repositories.NewRefreshTokenRepository(db)
|
||||
emailSender := &testutils.MockEmailSender{}
|
||||
|
||||
authFacade, err := NewAuthFacadeForTest(testutils.AppTestConfig, userRepo, postRepo, deletionRepo, refreshTokenRepo, emailSender)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAuthFacadeForTest() error = %v, want no error", err)
|
||||
}
|
||||
|
||||
password := "SecurePass123!"
|
||||
_, err = authFacade.Register("loginuser", "login@example.com", password)
|
||||
if err != nil {
|
||||
t.Fatalf("AuthFacade.Register() error = %v, want no error", err)
|
||||
}
|
||||
|
||||
user, err := userRepo.GetByUsername("loginuser")
|
||||
if err != nil {
|
||||
t.Fatalf("GetByUsername() error = %v", err)
|
||||
}
|
||||
user.EmailVerified = true
|
||||
if err := userRepo.Update(user); err != nil {
|
||||
t.Fatalf("Update() error = %v", err)
|
||||
}
|
||||
|
||||
authResult, err := authFacade.Login("loginuser", password)
|
||||
if err != nil {
|
||||
t.Fatalf("AuthFacade.Login() error = %v, want no error", err)
|
||||
}
|
||||
|
||||
if authResult == nil {
|
||||
t.Fatal("AuthFacade.Login() returned nil result")
|
||||
}
|
||||
|
||||
if authResult.AccessToken == "" {
|
||||
t.Error("AuthFacade.Login() returned empty access token")
|
||||
}
|
||||
|
||||
if authResult.RefreshToken == "" {
|
||||
t.Error("AuthFacade.Login() returned empty refresh token")
|
||||
}
|
||||
|
||||
if authResult.User == nil {
|
||||
t.Error("AuthFacade.Login() returned nil user")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("multiple_facades_independent", func(t *testing.T) {
|
||||
db1 := testutils.NewTestDB(t)
|
||||
defer func() {
|
||||
sqlDB, _ := db1.DB()
|
||||
sqlDB.Close()
|
||||
}()
|
||||
|
||||
db2 := testutils.NewTestDB(t)
|
||||
defer func() {
|
||||
sqlDB, _ := db2.DB()
|
||||
sqlDB.Close()
|
||||
}()
|
||||
|
||||
userRepo1 := repositories.NewUserRepository(db1)
|
||||
postRepo1 := repositories.NewPostRepository(db1)
|
||||
deletionRepo1 := repositories.NewAccountDeletionRepository(db1)
|
||||
refreshTokenRepo1 := repositories.NewRefreshTokenRepository(db1)
|
||||
emailSender1 := &testutils.MockEmailSender{}
|
||||
|
||||
userRepo2 := repositories.NewUserRepository(db2)
|
||||
postRepo2 := repositories.NewPostRepository(db2)
|
||||
deletionRepo2 := repositories.NewAccountDeletionRepository(db2)
|
||||
refreshTokenRepo2 := repositories.NewRefreshTokenRepository(db2)
|
||||
emailSender2 := &testutils.MockEmailSender{}
|
||||
|
||||
authFacade1, err := NewAuthFacadeForTest(testutils.AppTestConfig, userRepo1, postRepo1, deletionRepo1, refreshTokenRepo1, emailSender1)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAuthFacadeForTest() error = %v", err)
|
||||
}
|
||||
|
||||
authFacade2, err := NewAuthFacadeForTest(testutils.AppTestConfig, userRepo2, postRepo2, deletionRepo2, refreshTokenRepo2, emailSender2)
|
||||
if err != nil {
|
||||
t.Fatalf("NewAuthFacadeForTest() error = %v", err)
|
||||
}
|
||||
|
||||
if authFacade1 == authFacade2 {
|
||||
t.Error("NewAuthFacadeForTest() returned same instance for different repositories")
|
||||
}
|
||||
|
||||
_, err = authFacade1.Register("user1", "user1@example.com", "Pass123!")
|
||||
if err != nil {
|
||||
t.Fatalf("AuthFacade1.Register() error = %v", err)
|
||||
}
|
||||
|
||||
_, err = authFacade2.Register("user2", "user2@example.com", "Pass123!")
|
||||
if err != nil {
|
||||
t.Fatalf("AuthFacade2.Register() error = %v", err)
|
||||
}
|
||||
|
||||
user1, err := userRepo1.GetByUsername("user1")
|
||||
if err != nil {
|
||||
t.Fatalf("GetByUsername() error = %v", err)
|
||||
}
|
||||
user1.EmailVerified = true
|
||||
if err := userRepo1.Update(user1); err != nil {
|
||||
t.Fatalf("Update() error = %v", err)
|
||||
}
|
||||
|
||||
user2, err := userRepo2.GetByUsername("user2")
|
||||
if err != nil {
|
||||
t.Fatalf("GetByUsername() error = %v", err)
|
||||
}
|
||||
user2.EmailVerified = true
|
||||
if err := userRepo2.Update(user2); err != nil {
|
||||
t.Fatalf("Update() error = %v", err)
|
||||
}
|
||||
|
||||
_, err = authFacade1.Login("user1", "Pass123!")
|
||||
if err != nil {
|
||||
t.Errorf("AuthFacade1.Login() error = %v, should find user1", err)
|
||||
}
|
||||
|
||||
_, err = authFacade2.Login("user2", "Pass123!")
|
||||
if err != nil {
|
||||
t.Errorf("AuthFacade2.Login() error = %v, should find user2", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user