224 lines
5.7 KiB
Go
224 lines
5.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"html/template"
|
|
"io/fs"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"os/exec"
|
|
"testing"
|
|
"time"
|
|
"toolbox/pkg/base"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
const (
|
|
testPort = "9999"
|
|
baseURL = "http://localhost:" + testPort
|
|
)
|
|
|
|
func TestOrbitViewerTemplatesAndRoutes(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
r := gin.New()
|
|
|
|
for id, tool := range base.Registry {
|
|
if err := tool.Init(); err != nil {
|
|
t.Fatalf("Failed to initialize tool %s: %v", id, err)
|
|
}
|
|
tool.RegisterRoutes(r.Group("/api/" + id))
|
|
}
|
|
|
|
subFS, err := fs.Sub(webFS, "web")
|
|
if err != nil {
|
|
t.Fatalf("Failed to fs.Sub: %v", err)
|
|
}
|
|
|
|
tmpl, err := template.ParseFS(subFS, "layout.html", "index.html", "tools/*.html")
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse templates: %v", err)
|
|
}
|
|
|
|
serveIndex := func(c *gin.Context) {
|
|
data := gin.H{
|
|
"Title": "测试",
|
|
"Description": "描述",
|
|
"Keywords": "关键词",
|
|
"CanonicalURL": "https://toolbox.pengzhan.dev/orbit-viewer",
|
|
"GA_ID": "",
|
|
}
|
|
var buf bytes.Buffer
|
|
if err := tmpl.ExecuteTemplate(&buf, "layout.html", data); err != nil {
|
|
c.String(http.StatusInternalServerError, "Template error: %v", err)
|
|
return
|
|
}
|
|
c.Data(http.StatusOK, "text/html; charset=utf-8", buf.Bytes())
|
|
}
|
|
|
|
r.GET("/orbit-viewer", serveIndex)
|
|
|
|
// Test GET /orbit-viewer
|
|
req, _ := http.NewRequest("GET", "/orbit-viewer", nil)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("Expected status 200 for /orbit-viewer, got %d: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
bodyStr := w.Body.String()
|
|
if !bytes.Contains([]byte(bodyStr), []byte("panel-orbit-viewer")) {
|
|
t.Errorf("Expected body to contain panel-orbit-viewer")
|
|
}
|
|
if !bytes.Contains([]byte(bodyStr), []byte("阶段一:卫星轨道与全球投影轨迹")) {
|
|
t.Errorf("Expected body to contain orbit viewer content")
|
|
}
|
|
}
|
|
|
|
func TestToolboxE2E(t *testing.T) {
|
|
// 1. Build the binary
|
|
fmt.Println("--- Step 1: Building binary... ---")
|
|
_ = os.MkdirAll("build", 0755)
|
|
buildPath := "build/toolbox_test_bin"
|
|
buildCmd := exec.Command("go", "build", "-o", buildPath, "main.go")
|
|
if err := buildCmd.Run(); err != nil {
|
|
t.Fatalf("Failed to build binary: %v", err)
|
|
}
|
|
defer func() { _ = os.Remove(buildPath) }()
|
|
|
|
// 2. Start the server
|
|
fmt.Println("--- Step 2: Starting server... ---")
|
|
serverCmd := exec.Command("./"+buildPath, "-port", testPort)
|
|
if err := serverCmd.Start(); err != nil {
|
|
t.Fatalf("Failed to start server: %v", err)
|
|
}
|
|
defer func() {
|
|
if serverCmd.Process != nil {
|
|
_ = serverCmd.Process.Kill()
|
|
}
|
|
}()
|
|
|
|
// 3. Wait for server to be ready
|
|
fmt.Println("--- Step 3: Waiting for server readiness... ---")
|
|
ready := false
|
|
for i := 0; i < 10; i++ {
|
|
resp, err := http.Get(baseURL + "/api/tools")
|
|
if err == nil && resp.StatusCode == http.StatusOK {
|
|
ready = true
|
|
_ = resp.Body.Close()
|
|
break
|
|
}
|
|
time.Sleep(500 * time.Millisecond)
|
|
}
|
|
if !ready {
|
|
t.Fatal("Server failed to become ready in time")
|
|
}
|
|
|
|
// 4. Test endpoints
|
|
fmt.Println("--- Step 4: Testing tool endpoints... ---")
|
|
|
|
testOutputDir, err := os.MkdirTemp("", "toolbox_test_output_*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
fmt.Printf("--- Test Artifacts will be saved to: %s ---\n", testOutputDir)
|
|
|
|
tests := []struct {
|
|
name string
|
|
path string
|
|
method string
|
|
fileName string
|
|
body interface{}
|
|
}{
|
|
{
|
|
name: "Zitie Teaching",
|
|
path: "/api/zitie/teaching",
|
|
method: "POST",
|
|
fileName: "zitie_teaching.pdf",
|
|
body: map[string]string{"chars": "永和九年", "paper_size": "A4"},
|
|
},
|
|
{
|
|
name: "Zitie Step",
|
|
path: "/api/zitie/step",
|
|
method: "POST",
|
|
fileName: "zitie_step.pdf",
|
|
body: map[string]string{"chars": "永", "paper_size": "A4"},
|
|
},
|
|
{
|
|
name: "Zitie Manuscript",
|
|
path: "/api/zitie/manuscript",
|
|
method: "POST",
|
|
fileName: "zitie_manuscript.pdf",
|
|
body: map[string]string{"chars": "天道酬勤", "paper_size": "A4", "font_type": "kaiti"},
|
|
},
|
|
{
|
|
name: "Learn Number Counting",
|
|
path: "/api/learn-number/counting",
|
|
method: "POST",
|
|
fileName: "counting.pdf",
|
|
body: map[string]interface{}{"page_count": 1, "paper_size": "A4"},
|
|
},
|
|
{
|
|
name: "Learn Number Writing",
|
|
path: "/api/learn-number/writing",
|
|
method: "POST",
|
|
fileName: "writing.pdf",
|
|
body: map[string]interface{}{"start_num": 1, "end_num": 5, "paper_size": "A4"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var resp *http.Response
|
|
var err error
|
|
|
|
if tt.method == "POST" {
|
|
b, _ := json.Marshal(tt.body)
|
|
resp, err = http.Post(baseURL+tt.path, "application/json", bytes.NewBuffer(b))
|
|
} else {
|
|
resp, err = http.Get(baseURL + tt.path)
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("%s failed: %v", tt.name, err)
|
|
return
|
|
}
|
|
defer func() { _ = resp.Body.Close() }()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Errorf("%s returned status %d", tt.name, resp.StatusCode)
|
|
return
|
|
}
|
|
|
|
contentType := resp.Header.Get("Content-Type")
|
|
if contentType != "application/pdf" {
|
|
t.Errorf("%s expected application/pdf, got %s", tt.name, contentType)
|
|
} else {
|
|
// Save PDF to temp directory for human review
|
|
outPath := testOutputDir + "/" + tt.fileName
|
|
f, err := os.Create(outPath)
|
|
if err != nil {
|
|
t.Errorf("Failed to create output file: %v", err)
|
|
return
|
|
}
|
|
defer func() { _ = f.Close() }()
|
|
|
|
_, err = f.ReadFrom(resp.Body)
|
|
|
|
if err != nil {
|
|
t.Errorf("Failed to save PDF: %v", err)
|
|
return
|
|
}
|
|
|
|
fmt.Printf("[PASS] %s: Saved to %s\n", tt.name, outPath)
|
|
}
|
|
})
|
|
}
|
|
|
|
fmt.Printf("\n--- ALL TESTS DONE. PLEASE REVIEW PDFS IN: %s ---\n", testOutputDir)
|
|
}
|