225 lines
5.9 KiB
Go
225 lines
5.9 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/user-management-system/internal/domain"
|
|
)
|
|
|
|
func TestPasswordHistoryRepository_Create(t *testing.T) {
|
|
db := openTestDB(t)
|
|
if err := db.AutoMigrate(&domain.PasswordHistory{}); err != nil {
|
|
t.Fatalf("migrate password_history failed: %v", err)
|
|
}
|
|
|
|
repo := NewPasswordHistoryRepository(db)
|
|
ctx := context.Background()
|
|
|
|
history := &domain.PasswordHistory{
|
|
UserID: 1,
|
|
PasswordHash: "hash1",
|
|
CreatedAt: time.Now(),
|
|
}
|
|
|
|
if err := repo.Create(ctx, history); err != nil {
|
|
t.Fatalf("create failed: %v", err)
|
|
}
|
|
if history.ID == 0 {
|
|
t.Error("expected ID to be set after create")
|
|
}
|
|
}
|
|
|
|
func TestPasswordHistoryRepository_GetByUserID(t *testing.T) {
|
|
db := openTestDB(t)
|
|
if err := db.AutoMigrate(&domain.PasswordHistory{}); err != nil {
|
|
t.Fatalf("migrate password_history failed: %v", err)
|
|
}
|
|
|
|
repo := NewPasswordHistoryRepository(db)
|
|
ctx := context.Background()
|
|
|
|
// Create multiple records for user 1
|
|
for i := 0; i < 5; i++ {
|
|
h := &domain.PasswordHistory{
|
|
UserID: 1,
|
|
PasswordHash: "hash",
|
|
CreatedAt: time.Now().Add(time.Duration(i) * time.Second),
|
|
}
|
|
if err := repo.Create(ctx, h); err != nil {
|
|
t.Fatalf("create failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// Create record for user 2
|
|
if err := repo.Create(ctx, &domain.PasswordHistory{UserID: 2, PasswordHash: "hash", CreatedAt: time.Now()}); err != nil {
|
|
t.Fatalf("create failed: %v", err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
userID int64
|
|
limit int
|
|
wantLen int
|
|
wantUser int64
|
|
}{
|
|
{"get all for user 1", 1, 10, 5, 1},
|
|
{"limit 3 for user 1", 1, 3, 3, 1},
|
|
{"get for user 2", 2, 10, 1, 2},
|
|
{"get for nonexistent user", 999, 10, 0, 999},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
histories, err := repo.GetByUserID(ctx, tc.userID, tc.limit)
|
|
if err != nil {
|
|
t.Fatalf("get failed: %v", err)
|
|
}
|
|
if len(histories) != tc.wantLen {
|
|
t.Errorf("expected %d histories, got %d", tc.wantLen, len(histories))
|
|
}
|
|
for _, h := range histories {
|
|
if h.UserID != tc.wantUser {
|
|
t.Errorf("expected user_id %d, got %d", tc.wantUser, h.UserID)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPasswordHistoryRepository_GetByUserID_Order(t *testing.T) {
|
|
db := openTestDB(t)
|
|
if err := db.AutoMigrate(&domain.PasswordHistory{}); err != nil {
|
|
t.Fatalf("migrate password_history failed: %v", err)
|
|
}
|
|
|
|
repo := NewPasswordHistoryRepository(db)
|
|
ctx := context.Background()
|
|
|
|
// Create records with different timestamps
|
|
now := time.Now()
|
|
for i := 0; i < 3; i++ {
|
|
h := &domain.PasswordHistory{
|
|
UserID: 1,
|
|
PasswordHash: "hash",
|
|
CreatedAt: now.Add(time.Duration(i) * time.Hour),
|
|
}
|
|
if err := repo.Create(ctx, h); err != nil {
|
|
t.Fatalf("create failed: %v", err)
|
|
}
|
|
}
|
|
|
|
histories, err := repo.GetByUserID(ctx, 1, 10)
|
|
if err != nil {
|
|
t.Fatalf("get failed: %v", err)
|
|
}
|
|
if len(histories) != 3 {
|
|
t.Fatalf("expected 3 histories, got %d", len(histories))
|
|
}
|
|
|
|
// Should be ordered by created_at DESC (newest first)
|
|
for i := 0; i < len(histories)-1; i++ {
|
|
if !histories[i].CreatedAt.After(histories[i+1].CreatedAt) && !histories[i].CreatedAt.Equal(histories[i+1].CreatedAt) {
|
|
t.Errorf("expected descending order, got %v before %v", histories[i].CreatedAt, histories[i+1].CreatedAt)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPasswordHistoryRepository_DeleteOldRecords(t *testing.T) {
|
|
db := openTestDB(t)
|
|
if err := db.AutoMigrate(&domain.PasswordHistory{}); err != nil {
|
|
t.Fatalf("migrate password_history failed: %v", err)
|
|
}
|
|
|
|
repo := NewPasswordHistoryRepository(db)
|
|
ctx := context.Background()
|
|
|
|
// Create 5 records for user 1
|
|
now := time.Now()
|
|
for i := 0; i < 5; i++ {
|
|
h := &domain.PasswordHistory{
|
|
UserID: 1,
|
|
PasswordHash: "hash",
|
|
CreatedAt: now.Add(time.Duration(i) * time.Hour),
|
|
}
|
|
if err := repo.Create(ctx, h); err != nil {
|
|
t.Fatalf("create failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// Delete old records, keep only 3
|
|
if err := repo.DeleteOldRecords(ctx, 1, 3); err != nil {
|
|
t.Fatalf("delete old records failed: %v", err)
|
|
}
|
|
|
|
histories, err := repo.GetByUserID(ctx, 1, 10)
|
|
if err != nil {
|
|
t.Fatalf("get failed: %v", err)
|
|
}
|
|
if len(histories) != 3 {
|
|
t.Errorf("expected 3 histories after cleanup, got %d", len(histories))
|
|
}
|
|
}
|
|
|
|
func TestPasswordHistoryRepository_DeleteOldRecords_NoRecords(t *testing.T) {
|
|
db := openTestDB(t)
|
|
if err := db.AutoMigrate(&domain.PasswordHistory{}); err != nil {
|
|
t.Fatalf("migrate password_history failed: %v", err)
|
|
}
|
|
|
|
repo := NewPasswordHistoryRepository(db)
|
|
ctx := context.Background()
|
|
|
|
// Should not error when no records exist
|
|
if err := repo.DeleteOldRecords(ctx, 999, 3); err != nil {
|
|
t.Fatalf("delete old records on empty table should not error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPasswordHistoryRepository_KeepsNewestRecords(t *testing.T) {
|
|
db := openTestDB(t)
|
|
if err := db.AutoMigrate(&domain.PasswordHistory{}); err != nil {
|
|
t.Fatalf("migrate password_history failed: %v", err)
|
|
}
|
|
|
|
repo := NewPasswordHistoryRepository(db)
|
|
ctx := context.Background()
|
|
|
|
// Create 5 records with different timestamps
|
|
now := time.Now()
|
|
var createdIDs []int64
|
|
for i := 0; i < 5; i++ {
|
|
h := &domain.PasswordHistory{
|
|
UserID: 1,
|
|
PasswordHash: "hash",
|
|
CreatedAt: now.Add(time.Duration(i) * time.Hour),
|
|
}
|
|
if err := repo.Create(ctx, h); err != nil {
|
|
t.Fatalf("create failed: %v", err)
|
|
}
|
|
createdIDs = append(createdIDs, h.ID)
|
|
}
|
|
|
|
// Delete old records, keep only 2
|
|
if err := repo.DeleteOldRecords(ctx, 1, 2); err != nil {
|
|
t.Fatalf("delete old records failed: %v", err)
|
|
}
|
|
|
|
histories, err := repo.GetByUserID(ctx, 1, 10)
|
|
if err != nil {
|
|
t.Fatalf("get failed: %v", err)
|
|
}
|
|
if len(histories) != 2 {
|
|
t.Fatalf("expected 2 histories after cleanup, got %d", len(histories))
|
|
}
|
|
|
|
// The remaining records should be the newest (last 2 created)
|
|
expectedIDs := map[int64]bool{createdIDs[3]: true, createdIDs[4]: true}
|
|
for _, h := range histories {
|
|
if !expectedIDs[h.ID] {
|
|
t.Errorf("expected remaining IDs to be %v, got %d", expectedIDs, h.ID)
|
|
}
|
|
}
|
|
}
|