Improve README SEO and increase test coverage to 50%
Optimized README for better discoverability with keywords: api, habits, self-hosted. Consolidated content to reduce redundancy while maintaining clarity. Added comprehensive test coverage across multiple layers: - Infrastructure: bcrypt hashing, JWT tokens, configuration validation - Application commands: user deletion, password reset, token revocation, email verification - Application queries: login, token refresh - Domain entities: refresh tokens, password reset tokens Coverage increased from 42.5% to 50.8% with meaningful business logic tests. Fixed integration test handler initialization with correct parameters.
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func TestNewBcryptHasher(t *testing.T) {
|
||||
hasher := NewBcryptHasher()
|
||||
if hasher == nil {
|
||||
t.Fatal("NewBcryptHasher() returned nil")
|
||||
}
|
||||
|
||||
_, ok := hasher.(*BcryptHasher)
|
||||
if !ok {
|
||||
t.Fatal("NewBcryptHasher() did not return *BcryptHasher")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcryptHasher_Hash(t *testing.T) {
|
||||
hasher := NewBcryptHasher()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
password string
|
||||
}{
|
||||
{
|
||||
name: "simple password",
|
||||
password: "password123",
|
||||
},
|
||||
{
|
||||
name: "complex password",
|
||||
password: "P@ssw0rd!123$%^&*()",
|
||||
},
|
||||
{
|
||||
name: "empty password",
|
||||
password: "",
|
||||
},
|
||||
{
|
||||
name: "unicode password",
|
||||
password: "pässwörd123",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
hashed, err := hasher.Hash(tt.password)
|
||||
if err != nil {
|
||||
t.Fatalf("Hash() error = %v", err)
|
||||
}
|
||||
|
||||
if hashed == "" {
|
||||
t.Fatal("Hash() returned empty string")
|
||||
}
|
||||
|
||||
if hashed == tt.password {
|
||||
t.Fatal("Hash() returned the same as input password")
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(hashed), []byte(tt.password))
|
||||
if err != nil {
|
||||
t.Fatalf("Generated hash does not match original password: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcryptHasher_Compare(t *testing.T) {
|
||||
hasher := NewBcryptHasher()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
password string
|
||||
compareWith string
|
||||
expectError bool
|
||||
errorAssertion func(error) bool
|
||||
}{
|
||||
{
|
||||
name: "matching passwords",
|
||||
password: "password123",
|
||||
compareWith: "password123",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "non-matching passwords",
|
||||
password: "password123",
|
||||
compareWith: "wrongpassword",
|
||||
expectError: true,
|
||||
errorAssertion: func(err error) bool {
|
||||
return err == bcrypt.ErrMismatchedHashAndPassword
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty password comparison",
|
||||
password: "",
|
||||
compareWith: "",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "unicode password match",
|
||||
password: "pässwörd123",
|
||||
compareWith: "pässwörd123",
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "unicode password mismatch",
|
||||
password: "pässwörd123",
|
||||
compareWith: "password123",
|
||||
expectError: true,
|
||||
errorAssertion: func(err error) bool {
|
||||
return err == bcrypt.ErrMismatchedHashAndPassword
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "case sensitive",
|
||||
password: "Password123",
|
||||
compareWith: "password123",
|
||||
expectError: true,
|
||||
errorAssertion: func(err error) bool {
|
||||
return err == bcrypt.ErrMismatchedHashAndPassword
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
hashed, err := hasher.Hash(tt.password)
|
||||
if err != nil {
|
||||
t.Fatalf("Hash() error = %v", err)
|
||||
}
|
||||
|
||||
err = hasher.Compare(hashed, tt.compareWith)
|
||||
if tt.expectError {
|
||||
if err == nil {
|
||||
t.Fatal("Compare() expected error but got nil")
|
||||
}
|
||||
if tt.errorAssertion != nil && !tt.errorAssertion(err) {
|
||||
t.Fatalf("Compare() error = %v, but assertion failed", err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("Compare() unexpected error = %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcryptHasher_CompareWithInvalidHash(t *testing.T) {
|
||||
hasher := NewBcryptHasher()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
invalidHash string
|
||||
password string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "invalid hash format",
|
||||
invalidHash: "not-a-valid-hash",
|
||||
password: "password123",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "empty hash",
|
||||
invalidHash: "",
|
||||
password: "password123",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "corrupted hash",
|
||||
invalidHash: "$2a$10$invalidhashdata",
|
||||
password: "password123",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := hasher.Compare(tt.invalidHash, tt.password)
|
||||
if !tt.expectError && err != nil {
|
||||
t.Fatalf("Compare() unexpected error = %v", err)
|
||||
}
|
||||
if tt.expectError && err == nil {
|
||||
t.Fatal("Compare() expected error but got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcryptHasher_HashGeneratesDifferentHashes(t *testing.T) {
|
||||
hasher := NewBcryptHasher()
|
||||
password := "samePassword123"
|
||||
|
||||
hash1, err := hasher.Hash(password)
|
||||
if err != nil {
|
||||
t.Fatalf("Hash() error = %v", err)
|
||||
}
|
||||
|
||||
hash2, err := hasher.Hash(password)
|
||||
if err != nil {
|
||||
t.Fatalf("Hash() error = %v", err)
|
||||
}
|
||||
|
||||
if hash1 == hash2 {
|
||||
t.Fatal("Hash() generated identical hashes for same password (should use salt)")
|
||||
}
|
||||
|
||||
if err := hasher.Compare(hash1, password); err != nil {
|
||||
t.Fatalf("First hash doesn't match password: %v", err)
|
||||
}
|
||||
|
||||
if err := hasher.Compare(hash2, password); err != nil {
|
||||
t.Fatalf("Second hash doesn't match password: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user