- Created main application entry point in `main.go`. - Added configuration management in `config/config.go` and tests in `config/config_test.go`. - Implemented handlers for book and stationery uploads in `handlers/book.go` and `handlers/stationery.go`, including validation logic. - Established database connection and services in `services/database.go` and `services/book_service.go`. - Defined models for books and stationery in `models/book.go` and `models/stationery.go`. - Set up Firebase integration for image uploads in `services/firebase.go`. - Created migration scripts for database schema in `migrations/001_create_tables.sql` and subsequent updates. - Added CORS and error handling middleware. - Included comprehensive tests for handlers, services, and utilities. - Documented API endpoints and usage in `README.md` and migration instructions in `migrations/README.md`. - Introduced `.gitignore` to exclude unnecessary files and directories. - Added Go module support with `go.mod` and `go.sum` files. - Implemented utility functions for slug generation and validation in `utils/slug.go` and `utils/validation.go`.
206 lines
5.0 KiB
Go
206 lines
5.0 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
)
|
|
|
|
func TestLoadConfig(t *testing.T) {
|
|
// Save original env values
|
|
originalEnv := make(map[string]string)
|
|
envVars := []string{
|
|
"PORT", "FRONTEND_URL",
|
|
"DB_HOST", "DB_PORT", "DB_USER", "DB_PASSWORD", "DB_NAME",
|
|
"FIREBASE_PROJECT_ID", "FIREBASE_STORAGE_BUCKET", "FIREBASE_CREDENTIALS_FILE",
|
|
}
|
|
for _, key := range envVars {
|
|
originalEnv[key] = os.Getenv(key)
|
|
os.Unsetenv(key)
|
|
}
|
|
defer func() {
|
|
// Restore original env values
|
|
for key, value := range originalEnv {
|
|
if value != "" {
|
|
os.Setenv(key, value)
|
|
} else {
|
|
os.Unsetenv(key)
|
|
}
|
|
}
|
|
}()
|
|
|
|
tests := []struct {
|
|
name string
|
|
setup func()
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "valid config",
|
|
setup: func() {
|
|
os.Setenv("DB_USER", "test_user")
|
|
os.Setenv("DB_PASSWORD", "test_password")
|
|
os.Setenv("DB_NAME", "test_db")
|
|
os.Setenv("FIREBASE_PROJECT_ID", "test-project")
|
|
os.Setenv("FIREBASE_STORAGE_BUCKET", "test-bucket.appspot.com")
|
|
os.Setenv("FIREBASE_CREDENTIALS_FILE", "./test-credentials.json")
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "missing DB_USER",
|
|
setup: func() {
|
|
os.Setenv("DB_PASSWORD", "test_password")
|
|
os.Setenv("DB_NAME", "test_db")
|
|
os.Setenv("FIREBASE_PROJECT_ID", "test-project")
|
|
os.Setenv("FIREBASE_STORAGE_BUCKET", "test-bucket.appspot.com")
|
|
os.Setenv("FIREBASE_CREDENTIALS_FILE", "./test-credentials.json")
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "missing FIREBASE_PROJECT_ID",
|
|
setup: func() {
|
|
os.Setenv("DB_USER", "test_user")
|
|
os.Setenv("DB_PASSWORD", "test_password")
|
|
os.Setenv("DB_NAME", "test_db")
|
|
os.Setenv("FIREBASE_STORAGE_BUCKET", "test-bucket.appspot.com")
|
|
os.Setenv("FIREBASE_CREDENTIALS_FILE", "./test-credentials.json")
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "uses defaults for optional fields",
|
|
setup: func() {
|
|
os.Setenv("DB_USER", "test_user")
|
|
os.Setenv("DB_PASSWORD", "test_password")
|
|
os.Setenv("DB_NAME", "test_db")
|
|
os.Setenv("FIREBASE_PROJECT_ID", "test-project")
|
|
os.Setenv("FIREBASE_STORAGE_BUCKET", "test-bucket.appspot.com")
|
|
os.Setenv("FIREBASE_CREDENTIALS_FILE", "./test-credentials.json")
|
|
},
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Clean up env before each test
|
|
for _, key := range envVars {
|
|
os.Unsetenv(key)
|
|
}
|
|
|
|
tt.setup()
|
|
|
|
cfg, err := LoadConfig()
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("LoadConfig() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
|
|
if !tt.wantErr && cfg == nil {
|
|
t.Error("LoadConfig() returned nil config without error")
|
|
return
|
|
}
|
|
|
|
if !tt.wantErr {
|
|
// Verify defaults are used
|
|
if cfg.Port == "" {
|
|
t.Error("Port should have default value")
|
|
}
|
|
if cfg.DBHost == "" {
|
|
t.Error("DBHost should have default value")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConfig_Validate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
config *Config
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "valid config",
|
|
config: &Config{
|
|
DBUser: "user",
|
|
DBPassword: "password",
|
|
DBName: "dbname",
|
|
FirebaseProjectID: "project-id",
|
|
FirebaseStorageBucket: "bucket.appspot.com",
|
|
FirebaseCredentialsFile: "./credentials.json",
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "missing DB_USER",
|
|
config: &Config{
|
|
DBPassword: "password",
|
|
DBName: "dbname",
|
|
FirebaseProjectID: "project-id",
|
|
FirebaseStorageBucket: "bucket.appspot.com",
|
|
FirebaseCredentialsFile: "./credentials.json",
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "missing FIREBASE_PROJECT_ID",
|
|
config: &Config{
|
|
DBUser: "user",
|
|
DBPassword: "password",
|
|
DBName: "dbname",
|
|
FirebaseStorageBucket: "bucket.appspot.com",
|
|
FirebaseCredentialsFile: "./credentials.json",
|
|
},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := tt.config.Validate()
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("Config.Validate() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLoadConfig_EnvFilePrecedence(t *testing.T) {
|
|
// This test verifies that .env.local overrides .env values
|
|
// Note: This is a conceptual test - actual file loading would require test files
|
|
|
|
// Save original env
|
|
originalPort := os.Getenv("PORT")
|
|
defer func() {
|
|
if originalPort != "" {
|
|
os.Setenv("PORT", originalPort)
|
|
} else {
|
|
os.Unsetenv("PORT")
|
|
}
|
|
}()
|
|
|
|
// Set required vars
|
|
os.Setenv("DB_USER", "test")
|
|
os.Setenv("DB_PASSWORD", "test")
|
|
os.Setenv("DB_NAME", "test")
|
|
os.Setenv("FIREBASE_PROJECT_ID", "test")
|
|
os.Setenv("FIREBASE_STORAGE_BUCKET", "test.appspot.com")
|
|
os.Setenv("FIREBASE_CREDENTIALS_FILE", "./test.json")
|
|
|
|
// Test that LoadConfig works
|
|
cfg, err := LoadConfig()
|
|
if err != nil {
|
|
t.Fatalf("LoadConfig() error = %v", err)
|
|
}
|
|
|
|
if cfg == nil {
|
|
t.Fatal("LoadConfig() returned nil")
|
|
}
|
|
|
|
// Verify it uses default PORT if not set
|
|
if cfg.Port == "" {
|
|
t.Error("Port should have default value")
|
|
}
|
|
}
|