@@ -0,0 +1,132 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"ymhut-box/server/unified-management/internal/config"
|
||||
)
|
||||
|
||||
func TestSetupRouterServesBuiltAssetsAndBlocksBusinessAPI(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
setupDist := filepath.Join(root, "setup")
|
||||
if err := os.MkdirAll(filepath.Join(setupDist, "assets"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(setupDist, "index.html"), []byte(`<!doctype html><script type="module" src="/setup/assets/setup.js"></script><link rel="stylesheet" href="/setup/assets/setup.css">`), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(setupDist, "assets", "setup.css"), []byte(`body{}`), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(setupDist, "assets", "setup.js"), []byte(`console.log("setup")`), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
handler := NewSetupRouter(setupConfig(root, setupDist))
|
||||
|
||||
for _, item := range []struct {
|
||||
path string
|
||||
want int
|
||||
typ string
|
||||
}{
|
||||
{"/setup", http.StatusOK, "text/html"},
|
||||
{"/setup/assets/setup.css", http.StatusOK, "text/css"},
|
||||
{"/setup/assets/setup.js", http.StatusOK, "javascript"},
|
||||
{"/api/client/bootstrap", http.StatusServiceUnavailable, "application/json"},
|
||||
} {
|
||||
req := httptest.NewRequest(http.MethodGet, item.path, nil)
|
||||
res := httptest.NewRecorder()
|
||||
handler.ServeHTTP(res, req)
|
||||
if res.Code != item.want {
|
||||
t.Fatalf("%s returned %d: %s", item.path, res.Code, res.Body.String())
|
||||
}
|
||||
if got := res.Header().Get("Content-Type"); !strings.Contains(got, item.typ) {
|
||||
t.Fatalf("%s content-type = %q, want %q", item.path, got, item.typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupSQLiteCompleteCreatesConfigAndDefaultAdmin(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
handler := NewSetupRouter(setupConfig(root, filepath.Join(root, "missing-setup-dist")))
|
||||
|
||||
body := bytes.NewBufferString(`{"provider":"sqlite","baseUrl":"https://update.ymhut.cn","sqlitePath":"storage/unified.sqlite"}`)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/setup/complete", body)
|
||||
res := httptest.NewRecorder()
|
||||
handler.ServeHTTP(res, req)
|
||||
if res.Code != http.StatusOK {
|
||||
t.Fatalf("complete returned %d: %s", res.Code, res.Body.String())
|
||||
}
|
||||
data, err := os.ReadFile(filepath.Join(root, "config.json"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var cfg config.Config
|
||||
if err := json.Unmarshal(data, &cfg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !cfg.Initialized || cfg.Database.Provider != "sqlite" {
|
||||
t.Fatalf("unexpected config: %#v", cfg)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, "storage", "unified.sqlite")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupSQLiteIgnoresStructuredMySQLDefaults(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
handler := NewSetupRouter(setupConfig(root, filepath.Join(root, "missing-setup-dist")))
|
||||
|
||||
body := bytes.NewBufferString(`{"provider":"sqlite","baseUrl":"https://update.ymhut.cn","sqlitePath":"storage/unified.sqlite","mysql":{"host":"127.0.0.1","port":3306,"database":"ymhut_unified","username":"","password":"","charset":"utf8mb4","parseTime":true,"tls":"false"}}`)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/setup/database/test", body)
|
||||
res := httptest.NewRecorder()
|
||||
handler.ServeHTTP(res, req)
|
||||
if res.Code != http.StatusOK {
|
||||
t.Fatalf("sqlite test returned %d: %s", res.Code, res.Body.String())
|
||||
}
|
||||
if strings.Contains(res.Body.String(), "mysql username is required") {
|
||||
t.Fatalf("sqlite test should ignore structured mysql defaults: %s", res.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupStructuredMySQLValidationReturnsFailureWithoutSaving(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
handler := NewSetupRouter(setupConfig(root, filepath.Join(root, "missing-setup-dist")))
|
||||
|
||||
body := bytes.NewBufferString(`{"provider":"mysql","mysql":{"host":"127.0.0.1","port":1,"database":"ymhut","username":"root","password":"secret","charset":"utf8mb4","parseTime":true,"tls":"false"}}`)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/setup/database/test", body)
|
||||
res := httptest.NewRecorder()
|
||||
handler.ServeHTTP(res, req)
|
||||
if res.Code != http.StatusBadGateway {
|
||||
t.Fatalf("mysql test returned %d, want 502: %s", res.Code, res.Body.String())
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, "config.json")); !os.IsNotExist(err) {
|
||||
t.Fatalf("config should not be written on failed test: %v", err)
|
||||
}
|
||||
if strings.Contains(res.Body.String(), "secret") {
|
||||
t.Fatalf("response leaked password: %s", res.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func setupConfig(root, setupDist string) *config.Config {
|
||||
return &config.Config{
|
||||
BaseDir: root,
|
||||
ConfigPath: filepath.Join(root, "config.json"),
|
||||
BaseURL: "https://update.ymhut.cn",
|
||||
StorageDir: filepath.Join(root, "storage"),
|
||||
SetupWebDir: setupDist,
|
||||
Database: config.DatabaseConfig{
|
||||
Provider: "sqlite",
|
||||
SQLitePath: filepath.Join(root, "storage", "unified.sqlite"),
|
||||
HealthIntervalSec: 30,
|
||||
MaxOpenConns: 1,
|
||||
MaxIdleConns: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user