To gitea and beyond, let's go(-yco)

This commit is contained in:
2025-11-10 19:12:09 +01:00
parent 8f6133392d
commit 71a031342b
245 changed files with 83994 additions and 0 deletions

View 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
}