-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhandler_test.go
More file actions
227 lines (191 loc) · 6.49 KB
/
handler_test.go
File metadata and controls
227 lines (191 loc) · 6.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package main
import (
"bytes"
"database/sql"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/gin-gonic/gin"
_ "github.com/lib/pq"
"gopkg.in/gomail.v2"
)
// 🚨 TESTING PROBLEM #1: Complex Test Setup
// We need a real database, email server, and payment API to test this handler
func TestCreateOrder_Success(t *testing.T) {
// 🚨 PROBLEM: Need to set up a real test database
db, err := setupTestDatabase()
if err != nil {
t.Fatalf("Failed to setup test database: %v", err)
}
defer teardownTestDatabase(db)
// 🚨 PROBLEM: Need to seed test data - couples test to specific schema
seedTestData(db, t)
// 🚨 PROBLEM: Need a mock email server or skip email entirely
// Here we're using a broken dialer that will fail - but order will still be created!
mailer := gomail.NewDialer("localhost", 1025, "", "")
// 🚨 PROBLEM: Stripe requires test API keys - must be configured
os.Setenv("STRIPE_KEY", "sk_test_fake_key_that_will_fail")
handler := &OrderHandler{
db: db,
mailer: mailer,
}
// 🚨 PROBLEM: Need to set up HTTP test infrastructure
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/orders", handler.CreateOrder)
// Create test request
requestBody := CreateOrderRequest{
CustomerID: 1,
CustomerEmail: "test@example.com",
Items: []Item{
{ProductID: 1, Quantity: 2},
},
DiscountCode: "SAVE10",
}
bodyBytes, _ := json.Marshal(requestBody)
req := httptest.NewRequest("POST", "/orders", bytes.NewBuffer(bodyBytes))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
// 🚨 TESTING PROBLEM #2: This test will FAIL because:
// 1. Stripe API call will fail (fake key)
// 2. Email will fail (no SMTP server)
// 3. But we can't test business logic in isolation!
router.ServeHTTP(w, req)
// 🚨 TESTING PROBLEM #3: We can't assert on business logic
// We can only check HTTP status codes, not internal behavior
if w.Code != http.StatusOK {
t.Errorf("Expected status 200, got %d", w.Code)
t.Logf("Response body: %s", w.Body.String())
}
// 🚨 TESTING PROBLEM #4: To verify the order was created, we need more DB queries
// This couples our test to the database schema
var orderCount int
db.QueryRow("SELECT COUNT(*) FROM orders WHERE customer_id = $1", 1).Scan(&orderCount)
if orderCount != 1 {
t.Errorf("Expected 1 order, found %d", orderCount)
}
}
// 🚨 TESTING PROBLEM #5: Testing edge cases is a nightmare
func TestCreateOrder_PaymentFails(t *testing.T) {
// How do we simulate a payment failure?
// 1. Mock the entire Stripe SDK? (complex)
// 2. Use Stripe's test mode with specific card numbers? (requires real API calls)
// 3. Dependency injection? (not possible with this design)
t.Skip("Can't test payment failures without major refactoring")
}
func TestCreateOrder_InsufficientStock(t *testing.T) {
// 🚨 PROBLEM: Need to set up database with specific stock levels
db, err := setupTestDatabase()
if err != nil {
t.Fatalf("Failed to setup test database: %v", err)
}
defer teardownTestDatabase(db)
// Seed product with low stock
_, err = db.Exec(
"INSERT INTO products (id, name, price, stock) VALUES ($1, $2, $3, $4)",
999, "Low Stock Product", 50.0, 1,
)
if err != nil {
t.Fatalf("Failed to seed data: %v", err)
}
mailer := gomail.NewDialer("localhost", 1025, "", "")
handler := &OrderHandler{db: db, mailer: mailer}
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/orders", handler.CreateOrder)
// Try to order more than available
requestBody := CreateOrderRequest{
CustomerID: 1,
CustomerEmail: "test@example.com",
Items: []Item{
{ProductID: 999, Quantity: 10}, // Only 1 in stock!
},
}
bodyBytes, _ := json.Marshal(requestBody)
req := httptest.NewRequest("POST", "/orders", bytes.NewBuffer(bodyBytes))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
// 🚨 TESTING PROBLEM #6: We can test the HTTP response, but not the business rule directly
if w.Code != http.StatusBadRequest {
t.Errorf("Expected status 400, got %d", w.Code)
}
// 🚨 TESTING PROBLEM #7: Error message is buried in JSON response
var response map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &response)
errorMsg, ok := response["error"].(string)
if !ok || errorMsg == "" {
t.Error("Expected error message in response")
}
}
func TestCreateOrder_DiscountCalculation(t *testing.T) {
// 🚨 TESTING PROBLEM #8: Can't test discount logic without full HTTP request
// The business logic (discount calculation) is buried in the handler
// We need database, HTTP server, everything... just to test math!
t.Skip("Discount logic is untestable in isolation - requires full integration test")
}
// 🚨 TESTING PROBLEM #9: Test helpers are complex and fragile
func setupTestDatabase() (*sql.DB, error) {
// Need a real Postgres instance running
dbURL := os.Getenv("TEST_DATABASE_URL")
if dbURL == "" {
dbURL = "postgres://postgres:postgres@localhost/test_orders?sslmode=disable"
}
db, err := sql.Open("postgres", dbURL)
if err != nil {
return nil, err
}
// Create schema
schema := `
CREATE TABLE IF NOT EXISTS products (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL
);
CREATE TABLE IF NOT EXISTS orders (
id SERIAL PRIMARY KEY,
customer_id INT NOT NULL,
total DECIMAL(10,2) NOT NULL,
payment_id VARCHAR(255),
status VARCHAR(50),
created_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS order_items (
id SERIAL PRIMARY KEY,
order_id INT REFERENCES orders(id),
product_id INT REFERENCES products(id),
quantity INT NOT NULL,
price DECIMAL(10,2) NOT NULL
);
`
_, err = db.Exec(schema)
return db, err
}
func teardownTestDatabase(db *sql.DB) {
// Clean up
db.Exec("DROP TABLE IF EXISTS order_items")
db.Exec("DROP TABLE IF EXISTS orders")
db.Exec("DROP TABLE IF EXISTS products")
db.Close()
}
func seedTestData(db *sql.DB, t *testing.T) {
// Insert test products
_, err := db.Exec(
"INSERT INTO products (id, name, price, stock) VALUES ($1, $2, $3, $4)",
1, "Test Product", 50.0, 100,
)
if err != nil {
t.Fatalf("Failed to seed products: %v", err)
}
}
// 🚨 TESTING PROBLEM #10: Test execution is SLOW
// - Each test needs DB setup/teardown: ~500ms-1s
// - HTTP round-trip overhead: ~10-50ms
// - Total: A simple test that should take <1ms takes 500ms+
//
// With hexagonal architecture:
// - Business logic tests: <1ms (no DB, no HTTP, just Go code)
// - Integration tests: Still slow, but rare and isolated