To gitea and beyond, let's go(-yco)
This commit is contained in:
452
internal/testutils/fixtures.go
Normal file
452
internal/testutils/fixtures.go
Normal file
@@ -0,0 +1,452 @@
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
"goyco/internal/database"
|
||||
)
|
||||
|
||||
type TestConfig struct {
|
||||
Database DatabaseConfig `json:"database"`
|
||||
Server ServerConfig `json:"server"`
|
||||
JWT JWTConfig `json:"jwt"`
|
||||
Email EmailConfig `json:"email"`
|
||||
RateLimit RateLimitConfig `json:"rate_limit"`
|
||||
Cache CacheConfig `json:"cache"`
|
||||
Security SecurityConfig `json:"security"`
|
||||
Logging LoggingConfig `json:"logging"`
|
||||
}
|
||||
|
||||
type DatabaseConfig struct {
|
||||
Driver string `json:"driver"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
DBName string `json:"dbname"`
|
||||
SSLMode string `json:"sslmode"`
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
}
|
||||
|
||||
type JWTConfig struct {
|
||||
Secret string `json:"secret"`
|
||||
Expiration int `json:"expiration"`
|
||||
}
|
||||
|
||||
type EmailConfig struct {
|
||||
SMTPHost string `json:"smtp_host"`
|
||||
SMTPPort int `json:"smtp_port"`
|
||||
SMTPUsername string `json:"smtp_username"`
|
||||
SMTPPassword string `json:"smtp_password"`
|
||||
FromEmail string `json:"from_email"`
|
||||
FromName string `json:"from_name"`
|
||||
}
|
||||
|
||||
type RateLimitConfig struct {
|
||||
RequestsPerMinute int `json:"requests_per_minute"`
|
||||
BurstSize int `json:"burst_size"`
|
||||
}
|
||||
|
||||
type CacheConfig struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type SecurityConfig struct {
|
||||
EnableCSRF bool `json:"enable_csrf"`
|
||||
CSRFSecret string `json:"csrf_secret"`
|
||||
EnableCORS bool `json:"enable_cors"`
|
||||
AllowedOrigins []string `json:"allowed_origins"`
|
||||
EnableRateLimit bool `json:"enable_rate_limit"`
|
||||
EnableCompression bool `json:"enable_compression"`
|
||||
}
|
||||
|
||||
type LoggingConfig struct {
|
||||
Level string `json:"level"`
|
||||
Format string `json:"format"`
|
||||
Output string `json:"output"`
|
||||
}
|
||||
|
||||
type TestFixtures struct {
|
||||
Users []*database.User
|
||||
Posts []*database.Post
|
||||
Votes []*database.Vote
|
||||
Config *TestConfig
|
||||
}
|
||||
|
||||
func NewTestFixtures(t *testing.T) *TestFixtures {
|
||||
t.Helper()
|
||||
|
||||
return &TestFixtures{
|
||||
Users: []*database.User{
|
||||
{
|
||||
Username: "testuser1",
|
||||
Email: "user1@test.local",
|
||||
Password: "SecurePass123!",
|
||||
EmailVerified: true,
|
||||
},
|
||||
{
|
||||
Username: "testuser2",
|
||||
Email: "user2@test.local",
|
||||
Password: "SecurePass456!",
|
||||
EmailVerified: true,
|
||||
},
|
||||
{
|
||||
Username: "unverified_user",
|
||||
Email: "unverified@test.local",
|
||||
Password: "SecurePass789!",
|
||||
EmailVerified: false,
|
||||
},
|
||||
},
|
||||
Posts: []*database.Post{
|
||||
{
|
||||
Title: "Test Post 1",
|
||||
URL: "https://example.com/post1",
|
||||
Content: "This is test content for post 1",
|
||||
UpVotes: 5,
|
||||
DownVotes: 1,
|
||||
Score: 4,
|
||||
},
|
||||
{
|
||||
Title: "Test Post 2",
|
||||
URL: "https://example.com/post2",
|
||||
Content: "This is test content for post 2",
|
||||
UpVotes: 3,
|
||||
DownVotes: 0,
|
||||
Score: 3,
|
||||
},
|
||||
},
|
||||
Votes: []*database.Vote{
|
||||
{
|
||||
Type: database.VoteUp,
|
||||
},
|
||||
{
|
||||
Type: database.VoteDown,
|
||||
},
|
||||
{
|
||||
Type: database.VoteNone,
|
||||
},
|
||||
},
|
||||
Config: &TestConfig{
|
||||
Database: DatabaseConfig{
|
||||
Driver: "sqlite",
|
||||
Host: ":memory:",
|
||||
Port: 0,
|
||||
User: "",
|
||||
Password: "",
|
||||
DBName: "test",
|
||||
SSLMode: "disable",
|
||||
},
|
||||
Server: ServerConfig{
|
||||
Host: "localhost",
|
||||
Port: 8080,
|
||||
},
|
||||
JWT: JWTConfig{
|
||||
Secret: "test-secret-key",
|
||||
Expiration: 24,
|
||||
},
|
||||
Email: EmailConfig{
|
||||
SMTPHost: "localhost",
|
||||
SMTPPort: 587,
|
||||
SMTPUsername: "test@example.com",
|
||||
SMTPPassword: "test-password",
|
||||
FromEmail: "test@example.com",
|
||||
FromName: "Test App",
|
||||
},
|
||||
RateLimit: RateLimitConfig{
|
||||
RequestsPerMinute: 60,
|
||||
BurstSize: 10,
|
||||
},
|
||||
Cache: CacheConfig{
|
||||
Type: "memory",
|
||||
},
|
||||
Security: SecurityConfig{
|
||||
EnableCSRF: true,
|
||||
CSRFSecret: "test-csrf-secret",
|
||||
EnableCORS: true,
|
||||
AllowedOrigins: []string{"http://localhost:3000"},
|
||||
EnableRateLimit: true,
|
||||
EnableCompression: true,
|
||||
},
|
||||
Logging: LoggingConfig{
|
||||
Level: "debug",
|
||||
Format: "json",
|
||||
Output: "stdout",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func CreateSecureTestUser(t *testing.T, db *gorm.DB, username, email string) *database.User {
|
||||
t.Helper()
|
||||
|
||||
if username == "" {
|
||||
username = generateSecureRandomString(8)
|
||||
}
|
||||
if email == "" {
|
||||
email = fmt.Sprintf("%s@test.local", username)
|
||||
}
|
||||
|
||||
password := generateSecurePassword()
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to hash password: %v", err)
|
||||
}
|
||||
|
||||
user := &database.User{
|
||||
Username: username,
|
||||
Email: email,
|
||||
Password: string(hashedPassword),
|
||||
EmailVerified: true,
|
||||
}
|
||||
|
||||
if err := db.Create(user).Error; err != nil {
|
||||
t.Fatalf("Failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
func CreateSecureTestPost(t *testing.T, db *gorm.DB, authorID uint) *database.Post {
|
||||
t.Helper()
|
||||
|
||||
title := generateSecureRandomString(12)
|
||||
url := fmt.Sprintf("https://example.com/%s", generateSecureRandomString(8))
|
||||
content := fmt.Sprintf("Test content for %s", title)
|
||||
|
||||
post := &database.Post{
|
||||
Title: title,
|
||||
URL: url,
|
||||
Content: content,
|
||||
AuthorID: &authorID,
|
||||
UpVotes: 0,
|
||||
DownVotes: 0,
|
||||
Score: 0,
|
||||
}
|
||||
|
||||
if err := db.Create(post).Error; err != nil {
|
||||
t.Fatalf("Failed to create test post: %v", err)
|
||||
}
|
||||
|
||||
return post
|
||||
}
|
||||
|
||||
func CreateSecureTestVote(t *testing.T, db *gorm.DB, userID, postID uint, voteType database.VoteType) *database.Vote {
|
||||
t.Helper()
|
||||
|
||||
vote := &database.Vote{
|
||||
UserID: &userID,
|
||||
PostID: postID,
|
||||
Type: voteType,
|
||||
}
|
||||
|
||||
if err := db.Create(vote).Error; err != nil {
|
||||
t.Fatalf("Failed to create test vote: %v", err)
|
||||
}
|
||||
|
||||
return vote
|
||||
}
|
||||
|
||||
func (f *TestFixtures) CreateTestUsers(t *testing.T, db *gorm.DB) []*database.User {
|
||||
t.Helper()
|
||||
|
||||
var users []*database.User
|
||||
for _, userData := range f.Users {
|
||||
user := *userData
|
||||
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to hash password: %v", err)
|
||||
}
|
||||
user.Password = string(hashedPassword)
|
||||
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
t.Fatalf("Failed to create test user: %v", err)
|
||||
}
|
||||
users = append(users, &user)
|
||||
}
|
||||
|
||||
return users
|
||||
}
|
||||
|
||||
func (f *TestFixtures) CreateTestPosts(t *testing.T, db *gorm.DB, authorID uint) []*database.Post {
|
||||
t.Helper()
|
||||
|
||||
var posts []*database.Post
|
||||
for _, postData := range f.Posts {
|
||||
post := *postData
|
||||
post.AuthorID = &authorID
|
||||
|
||||
if err := db.Create(&post).Error; err != nil {
|
||||
t.Fatalf("Failed to create test post: %v", err)
|
||||
}
|
||||
posts = append(posts, &post)
|
||||
}
|
||||
|
||||
return posts
|
||||
}
|
||||
|
||||
func (f *TestFixtures) CreateTestVotes(t *testing.T, db *gorm.DB, userID, postID uint) []*database.Vote {
|
||||
t.Helper()
|
||||
|
||||
var votes []*database.Vote
|
||||
for _, voteData := range f.Votes {
|
||||
vote := *voteData
|
||||
vote.UserID = &userID
|
||||
vote.PostID = postID
|
||||
|
||||
if err := db.Create(&vote).Error; err != nil {
|
||||
t.Fatalf("Failed to create test vote: %v", err)
|
||||
}
|
||||
votes = append(votes, &vote)
|
||||
}
|
||||
|
||||
return votes
|
||||
}
|
||||
|
||||
func generateSecureRandomString(length int) string {
|
||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
result := make([]byte, length)
|
||||
|
||||
for i := range result {
|
||||
num, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
|
||||
result[i] = charset[num.Int64()]
|
||||
}
|
||||
|
||||
return string(result)
|
||||
}
|
||||
|
||||
func generateSecurePassword() string {
|
||||
letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
numbers := "0123456789"
|
||||
special := "!@#$%^&*"
|
||||
|
||||
password := ""
|
||||
|
||||
num, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
|
||||
password += string(letters[num.Int64()])
|
||||
|
||||
num, _ = rand.Int(rand.Reader, big.NewInt(int64(len(numbers))))
|
||||
password += string(numbers[num.Int64()])
|
||||
|
||||
num, _ = rand.Int(rand.Reader, big.NewInt(int64(len(special))))
|
||||
password += string(special[num.Int64()])
|
||||
|
||||
for len(password) < 12 {
|
||||
charset := letters + numbers + special
|
||||
num, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
|
||||
password += string(charset[num.Int64()])
|
||||
}
|
||||
|
||||
return password
|
||||
}
|
||||
|
||||
func CleanupTestData(t *testing.T, db *gorm.DB) {
|
||||
t.Helper()
|
||||
|
||||
if err := db.Exec("DELETE FROM votes").Error; err != nil {
|
||||
t.Logf("Warning: Failed to clean up votes: %v", err)
|
||||
}
|
||||
if err := db.Exec("DELETE FROM posts").Error; err != nil {
|
||||
t.Logf("Warning: Failed to clean up posts: %v", err)
|
||||
}
|
||||
if err := db.Exec("DELETE FROM account_deletion_requests").Error; err != nil {
|
||||
t.Logf("Warning: Failed to clean up account deletion requests: %v", err)
|
||||
}
|
||||
if err := db.Exec("DELETE FROM users").Error; err != nil {
|
||||
t.Logf("Warning: Failed to clean up users: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertUserExists(t *testing.T, db *gorm.DB, userID uint) {
|
||||
t.Helper()
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&database.User{}).Where("id = ?", userID).Count(&count).Error; err != nil {
|
||||
t.Fatalf("Failed to check user existence: %v", err)
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
t.Errorf("Expected user with ID %d to exist", userID)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertUserNotExists(t *testing.T, db *gorm.DB, userID uint) {
|
||||
t.Helper()
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&database.User{}).Where("id = ?", userID).Count(&count).Error; err != nil {
|
||||
t.Fatalf("Failed to check user existence: %v", err)
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
t.Errorf("Expected user with ID %d to not exist", userID)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertPostExists(t *testing.T, db *gorm.DB, postID uint) {
|
||||
t.Helper()
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&database.Post{}).Where("id = ?", postID).Count(&count).Error; err != nil {
|
||||
t.Fatalf("Failed to check post existence: %v", err)
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
t.Errorf("Expected post with ID %d to exist", postID)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertVoteExists(t *testing.T, db *gorm.DB, userID, postID uint) {
|
||||
t.Helper()
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&database.Vote{}).Where("user_id = ? AND post_id = ?", userID, postID).Count(&count).Error; err != nil {
|
||||
t.Fatalf("Failed to check vote existence: %v", err)
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
t.Errorf("Expected vote for user %d and post %d to exist", userID, postID)
|
||||
}
|
||||
}
|
||||
|
||||
func GetUserCount(t *testing.T, db *gorm.DB) int64 {
|
||||
t.Helper()
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&database.User{}).Count(&count).Error; err != nil {
|
||||
t.Fatalf("Failed to get user count: %v", err)
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func GetPostCount(t *testing.T, db *gorm.DB) int64 {
|
||||
t.Helper()
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&database.Post{}).Count(&count).Error; err != nil {
|
||||
t.Fatalf("Failed to get post count: %v", err)
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func GetVoteCount(t *testing.T, db *gorm.DB) int64 {
|
||||
t.Helper()
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&database.Vote{}).Count(&count).Error; err != nil {
|
||||
t.Fatalf("Failed to get vote count: %v", err)
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
Reference in New Issue
Block a user