Features: - PDF service for generating task reports with ReportLab-style formatting - Storage service for file uploads (local and S3-compatible) - Admin authentication middleware with JWT support - Admin user model and repository Infrastructure: - Updated Docker configuration for admin panel builds - Email service enhancements for task notifications - Updated router with admin and file upload routes - Environment configuration updates Tests: - Unit tests for handlers (auth, residence, task) - Unit tests for models (user, residence, task) - Unit tests for repositories (user, residence, task) - Unit tests for services (residence, task) - Integration test setup - Test utilities for mocking database and services Database: - Admin user seed data - Updated test data seeds 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
218 lines
5.0 KiB
Go
218 lines
5.0 KiB
Go
package models
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestUser_SetPassword(t *testing.T) {
|
|
user := &User{}
|
|
|
|
err := user.SetPassword("testpassword123")
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, user.Password)
|
|
assert.NotEqual(t, "testpassword123", user.Password) // Should be hashed
|
|
}
|
|
|
|
func TestUser_CheckPassword(t *testing.T) {
|
|
user := &User{}
|
|
err := user.SetPassword("correctpassword")
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
password string
|
|
expected bool
|
|
}{
|
|
{"correct password", "correctpassword", true},
|
|
{"wrong password", "wrongpassword", false},
|
|
{"empty password", "", false},
|
|
{"similar password", "correctpassword1", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := user.CheckPassword(tt.password)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUser_GetFullName(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
user User
|
|
expected string
|
|
}{
|
|
{
|
|
name: "first and last name",
|
|
user: User{FirstName: "John", LastName: "Doe", Username: "johndoe"},
|
|
expected: "John Doe",
|
|
},
|
|
{
|
|
name: "first name only",
|
|
user: User{FirstName: "John", LastName: "", Username: "johndoe"},
|
|
expected: "John",
|
|
},
|
|
{
|
|
name: "username fallback",
|
|
user: User{FirstName: "", LastName: "", Username: "johndoe"},
|
|
expected: "johndoe",
|
|
},
|
|
{
|
|
name: "last name only returns username",
|
|
user: User{FirstName: "", LastName: "Doe", Username: "johndoe"},
|
|
expected: "johndoe",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := tt.user.GetFullName()
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUser_TableName(t *testing.T) {
|
|
user := User{}
|
|
assert.Equal(t, "auth_user", user.TableName())
|
|
}
|
|
|
|
func TestAuthToken_TableName(t *testing.T) {
|
|
token := AuthToken{}
|
|
assert.Equal(t, "user_authtoken", token.TableName())
|
|
}
|
|
|
|
func TestUserProfile_TableName(t *testing.T) {
|
|
profile := UserProfile{}
|
|
assert.Equal(t, "user_userprofile", profile.TableName())
|
|
}
|
|
|
|
func TestConfirmationCode_IsValid(t *testing.T) {
|
|
now := time.Now().UTC()
|
|
future := now.Add(1 * time.Hour)
|
|
past := now.Add(-1 * time.Hour)
|
|
|
|
tests := []struct {
|
|
name string
|
|
code ConfirmationCode
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "valid code",
|
|
code: ConfirmationCode{IsUsed: false, ExpiresAt: future},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "used code",
|
|
code: ConfirmationCode{IsUsed: true, ExpiresAt: future},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "expired code",
|
|
code: ConfirmationCode{IsUsed: false, ExpiresAt: past},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "used and expired",
|
|
code: ConfirmationCode{IsUsed: true, ExpiresAt: past},
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := tt.code.IsValid()
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPasswordResetCode_IsValid(t *testing.T) {
|
|
now := time.Now().UTC()
|
|
future := now.Add(1 * time.Hour)
|
|
past := now.Add(-1 * time.Hour)
|
|
|
|
tests := []struct {
|
|
name string
|
|
code PasswordResetCode
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "valid code",
|
|
code: PasswordResetCode{Used: false, ExpiresAt: future, Attempts: 0, MaxAttempts: 5},
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "used code",
|
|
code: PasswordResetCode{Used: true, ExpiresAt: future, Attempts: 0, MaxAttempts: 5},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "expired code",
|
|
code: PasswordResetCode{Used: false, ExpiresAt: past, Attempts: 0, MaxAttempts: 5},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "max attempts reached",
|
|
code: PasswordResetCode{Used: false, ExpiresAt: future, Attempts: 5, MaxAttempts: 5},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "attempts under max",
|
|
code: PasswordResetCode{Used: false, ExpiresAt: future, Attempts: 4, MaxAttempts: 5},
|
|
expected: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := tt.code.IsValid()
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPasswordResetCode_SetAndCheckCode(t *testing.T) {
|
|
code := &PasswordResetCode{}
|
|
|
|
err := code.SetCode("123456")
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, code.CodeHash)
|
|
|
|
// Check correct code
|
|
assert.True(t, code.CheckCode("123456"))
|
|
|
|
// Check wrong code
|
|
assert.False(t, code.CheckCode("654321"))
|
|
assert.False(t, code.CheckCode(""))
|
|
}
|
|
|
|
func TestGenerateConfirmationCode(t *testing.T) {
|
|
code := GenerateConfirmationCode()
|
|
assert.Len(t, code, 6)
|
|
|
|
// Generate multiple codes and ensure they're different
|
|
codes := make(map[string]bool)
|
|
for i := 0; i < 10; i++ {
|
|
c := GenerateConfirmationCode()
|
|
assert.Len(t, c, 6)
|
|
codes[c] = true
|
|
}
|
|
// Most codes should be unique (very unlikely to have collisions)
|
|
assert.Greater(t, len(codes), 5)
|
|
}
|
|
|
|
func TestGenerateResetToken(t *testing.T) {
|
|
token := GenerateResetToken()
|
|
assert.Len(t, token, 64) // 32 bytes = 64 hex chars
|
|
|
|
// Ensure uniqueness
|
|
token2 := GenerateResetToken()
|
|
assert.NotEqual(t, token, token2)
|
|
}
|