Add initial project structure with core functionality for book and stationery uploads
- 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`.
This commit is contained in:
205
config/config_test.go
Normal file
205
config/config_test.go
Normal file
@@ -0,0 +1,205 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user