mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-08 15:41:20 +02:00
- Add new dependencies: `github.com/joho/godotenv`, `github.com/stretchr/testify`, and `gopkg.in/natefinch/lumberjack.v2` - Remove indirect dependency on `github.com/joho/godotenv` - Refactor JobRunDetails template to separate content rendering for improved testing - Enhance error message display in JobRunDetails template - Introduce new test files for JWT and password functionalities - Add comprehensive tests for database operations and error handling
175 lines
6.0 KiB
Go
175 lines
6.0 KiB
Go
package auth
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// MockDB is a mock implementation of *gorm.DB for testing
|
|
type MockDB struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *MockDB) Where(query interface{}, args ...interface{}) *gorm.DB {
|
|
m.Called(query, args)
|
|
return &gorm.DB{}
|
|
}
|
|
|
|
func (m *MockDB) Order(value interface{}) *gorm.DB {
|
|
m.Called(value)
|
|
return &gorm.DB{}
|
|
}
|
|
|
|
func (m *MockDB) Limit(limit int) *gorm.DB {
|
|
m.Called(limit)
|
|
return &gorm.DB{}
|
|
}
|
|
|
|
func (m *MockDB) Find(dest interface{}, conds ...interface{}) *gorm.DB {
|
|
m.Called(dest, conds)
|
|
return &gorm.DB{}
|
|
}
|
|
|
|
func (m *MockDB) Create(value interface{}) *gorm.DB {
|
|
m.Called(value)
|
|
return &gorm.DB{}
|
|
}
|
|
|
|
func (m *MockDB) Delete(value interface{}, conds ...interface{}) *gorm.DB {
|
|
m.Called(value, conds)
|
|
return &gorm.DB{}
|
|
}
|
|
|
|
func (m *MockDB) Model(value interface{}) *gorm.DB {
|
|
m.Called(value)
|
|
return &gorm.DB{}
|
|
}
|
|
|
|
func (m *MockDB) Count(count *int64) *gorm.DB {
|
|
m.Called(count)
|
|
*count = 10 // Mock count for testing
|
|
return &gorm.DB{}
|
|
}
|
|
|
|
func TestDefaultPasswordPolicy(t *testing.T) {
|
|
policy := DefaultPasswordPolicy()
|
|
|
|
assert.Equal(t, 8, policy.MinLength, "Default min length should be 8")
|
|
assert.True(t, policy.RequireUppercase, "Should require uppercase by default")
|
|
assert.True(t, policy.RequireLowercase, "Should require lowercase by default")
|
|
assert.True(t, policy.RequireNumbers, "Should require numbers by default")
|
|
assert.True(t, policy.RequireSpecial, "Should require special chars by default")
|
|
assert.Equal(t, 90, policy.ExpirationDays, "Default expiration should be 90 days")
|
|
assert.Equal(t, 5, policy.HistoryCount, "Default history count should be 5")
|
|
assert.True(t, policy.DisallowCommon, "Should disallow common passwords by default")
|
|
assert.Equal(t, 5, policy.MaxLoginAttempts, "Default max login attempts should be 5")
|
|
assert.Equal(t, 15*time.Minute, policy.LockoutDuration, "Default lockout duration should be 15 minutes")
|
|
}
|
|
|
|
func TestValidatePassword(t *testing.T) {
|
|
policy := DefaultPasswordPolicy()
|
|
|
|
// Test valid password
|
|
err := ValidatePassword("Test1234!", policy)
|
|
assert.NoError(t, err, "Valid password should pass validation")
|
|
|
|
// Test password too short
|
|
err = ValidatePassword("Test1!", policy)
|
|
assert.Error(t, err, "Password shorter than minimum length should fail")
|
|
assert.Contains(t, err.Error(), "at least 8 characters")
|
|
|
|
// Test password without uppercase
|
|
err = ValidatePassword("test1234!", policy)
|
|
assert.Error(t, err, "Password without uppercase should fail")
|
|
assert.Contains(t, err.Error(), "uppercase letter")
|
|
|
|
// Test password without lowercase
|
|
err = ValidatePassword("TEST1234!", policy)
|
|
assert.Error(t, err, "Password without lowercase should fail")
|
|
assert.Contains(t, err.Error(), "lowercase letter")
|
|
|
|
// Test password without numbers
|
|
err = ValidatePassword("TestTest!", policy)
|
|
assert.Error(t, err, "Password without numbers should fail")
|
|
assert.Contains(t, err.Error(), "number")
|
|
|
|
// Test password without special characters
|
|
err = ValidatePassword("Test1234", policy)
|
|
assert.Error(t, err, "Password without special characters should fail")
|
|
assert.Contains(t, err.Error(), "special character")
|
|
|
|
// Test common password - we need to disable other validations to test just the common password check
|
|
customPolicy := DefaultPasswordPolicy()
|
|
customPolicy.RequireUppercase = false
|
|
customPolicy.RequireLowercase = false
|
|
customPolicy.RequireNumbers = false
|
|
customPolicy.RequireSpecial = false
|
|
|
|
err = ValidatePassword("password", customPolicy)
|
|
assert.Error(t, err, "Common password should fail even with relaxed requirements")
|
|
assert.Contains(t, err.Error(), "common or easily guessable")
|
|
|
|
// Test with custom policy (all validations disabled)
|
|
verySimplePolicy := PasswordPolicy{
|
|
MinLength: 6,
|
|
RequireUppercase: false,
|
|
RequireLowercase: false,
|
|
RequireNumbers: false,
|
|
RequireSpecial: false,
|
|
DisallowCommon: false,
|
|
}
|
|
|
|
err = ValidatePassword("simple", verySimplePolicy)
|
|
assert.NoError(t, err, "Simple password should pass with all validations disabled")
|
|
}
|
|
|
|
func TestComparePasswords(t *testing.T) {
|
|
// Generate a hashed password
|
|
plainPassword := "TestPassword123!"
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(plainPassword), bcrypt.DefaultCost)
|
|
assert.NoError(t, err, "Password hashing should not error")
|
|
|
|
// Test valid password comparison
|
|
err = ComparePasswords(string(hashedPassword), plainPassword)
|
|
assert.NoError(t, err, "Correct password should match hash")
|
|
|
|
// Test invalid password comparison
|
|
err = ComparePasswords(string(hashedPassword), "WrongPassword123!")
|
|
assert.Error(t, err, "Incorrect password should not match hash")
|
|
}
|
|
|
|
func TestIsPasswordExpired(t *testing.T) {
|
|
policy := DefaultPasswordPolicy()
|
|
|
|
// Test password within expiration period
|
|
lastChange := time.Now().Add(-80 * 24 * time.Hour) // 80 days ago
|
|
assert.False(t, IsPasswordExpired(lastChange, policy), "Password changed 80 days ago should not be expired")
|
|
|
|
// Test expired password
|
|
lastChange = time.Now().Add(-100 * 24 * time.Hour) // 100 days ago
|
|
assert.True(t, IsPasswordExpired(lastChange, policy), "Password changed 100 days ago should be expired")
|
|
|
|
// Test with expiration disabled
|
|
customPolicy := PasswordPolicy{
|
|
ExpirationDays: 0, // Disabled
|
|
}
|
|
lastChange = time.Now().Add(-1000 * 24 * time.Hour) // 1000 days ago
|
|
assert.False(t, IsPasswordExpired(lastChange, customPolicy), "Password should not expire when expiration is disabled")
|
|
}
|
|
|
|
func TestIsCommonPassword(t *testing.T) {
|
|
// Test with common passwords
|
|
assert.True(t, isCommonPassword("password"), "Should detect 'password' as common")
|
|
assert.True(t, isCommonPassword("admin123"), "Should detect 'admin123' as common")
|
|
assert.True(t, isCommonPassword("QWERTY"), "Should detect 'QWERTY' as common (case insensitive)")
|
|
|
|
// Test with uncommon passwords
|
|
assert.False(t, isCommonPassword("G4x8qT2!pL9z"), "Should not detect complex password as common")
|
|
assert.False(t, isCommonPassword("UniquePassword123!"), "Should not detect unique password as common")
|
|
}
|