完成
This commit is contained in:
358
golang-learning/10-projects/03-web-server/server_test.go
Normal file
358
golang-learning/10-projects/03-web-server/server_test.go
Normal file
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
server_test.go - Web服务器测试文件
|
||||
测试HTTP服务器和API的各种功能
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"webserver/models"
|
||||
"webserver/server"
|
||||
)
|
||||
|
||||
// TestHealthHandler 测试健康检查处理器
|
||||
func TestHealthHandler(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/health", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(server.HealthHandler)
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
// 检查状态码
|
||||
if status := rr.Code; status != http.StatusOK {
|
||||
t.Errorf("处理器返回错误状态码: got %v want %v", status, http.StatusOK)
|
||||
}
|
||||
|
||||
// 检查响应内容类型
|
||||
expected := "application/json"
|
||||
if ct := rr.Header().Get("Content-Type"); ct != expected {
|
||||
t.Errorf("处理器返回错误内容类型: got %v want %v", ct, expected)
|
||||
}
|
||||
|
||||
// 检查响应体
|
||||
var response server.HealthResponse
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
|
||||
t.Errorf("无法解析响应JSON: %v", err)
|
||||
}
|
||||
|
||||
if response.Status != "healthy" {
|
||||
t.Errorf("期望状态为 'healthy', 实际为 '%s'", response.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetUsersHandler 测试获取用户列表处理器
|
||||
func TestGetUsersHandler(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/api/users", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(server.GetUsersHandler)
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
// 检查状态码
|
||||
if status := rr.Code; status != http.StatusOK {
|
||||
t.Errorf("处理器返回错误状态码: got %v want %v", status, http.StatusOK)
|
||||
}
|
||||
|
||||
// 检查响应内容类型
|
||||
expected := "application/json"
|
||||
if ct := rr.Header().Get("Content-Type"); ct != expected {
|
||||
t.Errorf("处理器返回错误内容类型: got %v want %v", ct, expected)
|
||||
}
|
||||
|
||||
// 检查响应体
|
||||
var response server.Response
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
|
||||
t.Errorf("无法解析响应JSON: %v", err)
|
||||
}
|
||||
|
||||
if response.Status != "success" {
|
||||
t.Errorf("期望状态为 'success', 实际为 '%s'", response.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateUserHandler 测试创建用户处理器
|
||||
func TestCreateUserHandler(t *testing.T) {
|
||||
user := models.User{
|
||||
Name: "测试用户",
|
||||
Email: "test@example.com",
|
||||
Age: 25,
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(user)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", "/api/users", bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(server.CreateUserHandler)
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
// 检查状态码
|
||||
if status := rr.Code; status != http.StatusCreated {
|
||||
t.Errorf("处理器返回错误状态码: got %v want %v", status, http.StatusCreated)
|
||||
}
|
||||
|
||||
// 检查响应体
|
||||
var response server.Response
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
|
||||
t.Errorf("无法解析响应JSON: %v", err)
|
||||
}
|
||||
|
||||
if response.Status != "success" {
|
||||
t.Errorf("期望状态为 'success', 实际为 '%s'", response.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateUserHandlerInvalidData 测试创建用户处理器(无效数据)
|
||||
func TestCreateUserHandlerInvalidData(t *testing.T) {
|
||||
// 测试空用户名
|
||||
user := models.User{
|
||||
Name: "",
|
||||
Email: "test@example.com",
|
||||
Age: 25,
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(user)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", "/api/users", bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(server.CreateUserHandler)
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
// 检查状态码
|
||||
if status := rr.Code; status != http.StatusBadRequest {
|
||||
t.Errorf("处理器返回错误状态码: got %v want %v", status, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// 检查响应体
|
||||
var response server.Response
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
|
||||
t.Errorf("无法解析响应JSON: %v", err)
|
||||
}
|
||||
|
||||
if response.Status != "error" {
|
||||
t.Errorf("期望状态为 'error', 实际为 '%s'", response.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateUserHandlerInvalidJSON 测试创建用户处理器(无效JSON)
|
||||
func TestCreateUserHandlerInvalidJSON(t *testing.T) {
|
||||
invalidJSON := []byte(`{"name": "测试用户", "email": "test@example.com", "age":}`)
|
||||
|
||||
req, err := http.NewRequest("POST", "/api/users", bytes.NewBuffer(invalidJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(server.CreateUserHandler)
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
// 检查状态码
|
||||
if status := rr.Code; status != http.StatusBadRequest {
|
||||
t.Errorf("处理器返回错误状态码: got %v want %v", status, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStatsHandler 测试统计信息处理器
|
||||
func TestStatsHandler(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/api/stats", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(server.StatsHandler)
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
// 检查状态码
|
||||
if status := rr.Code; status != http.StatusOK {
|
||||
t.Errorf("处理器返回错误状态码: got %v want %v", status, http.StatusOK)
|
||||
}
|
||||
|
||||
// 检查响应体
|
||||
var response server.StatsResponse
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
|
||||
t.Errorf("无法解析响应JSON: %v", err)
|
||||
}
|
||||
|
||||
if response.Status != "success" {
|
||||
t.Errorf("期望状态为 'success', 实际为 '%s'", response.Status)
|
||||
}
|
||||
|
||||
if response.NumCPU <= 0 {
|
||||
t.Error("CPU数量应该大于0")
|
||||
}
|
||||
|
||||
if response.NumGoroutine <= 0 {
|
||||
t.Error("Goroutine数量应该大于0")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAPIDocHandler 测试API文档处理器
|
||||
func TestAPIDocHandler(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/api", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(server.APIDocHandler)
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
// 检查状态码
|
||||
if status := rr.Code; status != http.StatusOK {
|
||||
t.Errorf("处理器返回错误状态码: got %v want %v", status, http.StatusOK)
|
||||
}
|
||||
|
||||
// 检查响应内容类型
|
||||
expected := "application/json"
|
||||
if ct := rr.Header().Get("Content-Type"); ct != expected {
|
||||
t.Errorf("处理器返回错误内容类型: got %v want %v", ct, expected)
|
||||
}
|
||||
|
||||
// 检查响应体包含API文档信息
|
||||
var doc map[string]interface{}
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &doc); err != nil {
|
||||
t.Errorf("无法解析响应JSON: %v", err)
|
||||
}
|
||||
|
||||
if _, exists := doc["title"]; !exists {
|
||||
t.Error("API文档应该包含title字段")
|
||||
}
|
||||
|
||||
if _, exists := doc["endpoints"]; !exists {
|
||||
t.Error("API文档应该包含endpoints字段")
|
||||
}
|
||||
}
|
||||
|
||||
// TestServer 测试完整的服务器
|
||||
func TestServer(t *testing.T) {
|
||||
// 创建服务器
|
||||
srv := server.NewServer(":0") // 使用随机端口
|
||||
srv.SetupRoutes()
|
||||
|
||||
// 创建测试服务器
|
||||
testServer := httptest.NewServer(srv.GetRouter())
|
||||
defer testServer.Close()
|
||||
|
||||
// 测试健康检查
|
||||
resp, err := http.Get(testServer.URL + "/health")
|
||||
if err != nil {
|
||||
t.Fatalf("健康检查请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Errorf("健康检查返回错误状态码: got %v want %v", resp.StatusCode, http.StatusOK)
|
||||
}
|
||||
|
||||
// 测试获取用户列表
|
||||
resp, err = http.Get(testServer.URL + "/api/users")
|
||||
if err != nil {
|
||||
t.Fatalf("获取用户列表请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Errorf("获取用户列表返回错误状态码: got %v want %v", resp.StatusCode, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMiddleware 测试中间件
|
||||
func TestMiddleware(t *testing.T) {
|
||||
// 测试CORS中间件
|
||||
req, err := http.NewRequest("OPTIONS", "/api/users", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
// 创建带中间件的处理器
|
||||
handler := server.CORSMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
// 检查CORS头
|
||||
if origin := rr.Header().Get("Access-Control-Allow-Origin"); origin != "*" {
|
||||
t.Errorf("期望CORS Origin为 '*', 实际为 '%s'", origin)
|
||||
}
|
||||
|
||||
if methods := rr.Header().Get("Access-Control-Allow-Methods"); methods == "" {
|
||||
t.Error("应该设置Access-Control-Allow-Methods头")
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkHealthHandler 健康检查处理器基准测试
|
||||
func BenchmarkHealthHandler(b *testing.B) {
|
||||
req, _ := http.NewRequest("GET", "/health", nil)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(server.HealthHandler)
|
||||
handler.ServeHTTP(rr, req)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkGetUsersHandler 获取用户列表处理器基准测试
|
||||
func BenchmarkGetUsersHandler(b *testing.B) {
|
||||
req, _ := http.NewRequest("GET", "/api/users", nil)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(server.GetUsersHandler)
|
||||
handler.ServeHTTP(rr, req)
|
||||
}
|
||||
}
|
||||
|
||||
// ExampleHealthHandler 健康检查处理器示例
|
||||
func ExampleHealthHandler() {
|
||||
req, _ := http.NewRequest("GET", "/health", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(server.HealthHandler)
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
fmt.Printf("Status: %d", rr.Code)
|
||||
// Output: Status: 200
|
||||
}
|
Reference in New Issue
Block a user