You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
77 lines
3.6 KiB
77 lines
3.6 KiB
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"teraclone/internal/service"
|
|
)
|
|
|
|
// Opt-in visual fixture, compiled only into tests. No connection to the device,
|
|
// no persistent credentials, and no writes through the HTTP surface.
|
|
func TestLogExplorerPreview(t *testing.T) {
|
|
if os.Getenv("TERA_LOG_PREVIEW") != "1" {
|
|
t.Skip("set TERA_LOG_PREVIEW=1 for a five-minute loopback-only preview")
|
|
}
|
|
app, err := service.NewAppService(filepath.Join(t.TempDir(), "preview.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer app.Close()
|
|
handler, err := NewDeviceHandler(app, "http://127.0.0.1:1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
handler.sessions["preview-only"] = browserSession{Username: "UI 미리보기 · 테스트 데이터", BackendToken: "not-a-device-token", Role: "administrator", WorkspaceID: "log-preview"}
|
|
mux := http.NewServeMux()
|
|
handler.RegisterRoutes(mux)
|
|
listener, err := net.Listen("tcp", "127.0.0.1:19083")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
server := &http.Server{ReadHeaderTimeout: 3 * time.Second, Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "Preview is read-only; no logs were deleted", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
if strings.HasPrefix(r.URL.Path, "/api/v1/") {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
var payload any = []any{}
|
|
switch r.URL.Path {
|
|
case "/api/v1/system/status":
|
|
payload = map[string]any{"system_time": time.Now().UTC(), "cpu_usage_percent": 15, "memory_usage_percent": 28, "memory_total_bytes": 2000000000, "memory_used_bytes": 560000000, "memory_available_bytes": 1440000000, "storage": map[string]any{"path": "/userdata", "total_bytes": 8000000000, "used_bytes": 1600000000, "available_bytes": 6000000000, "usage_percent": 20}}
|
|
case "/api/v1/network":
|
|
payload = map[string]any{"host_name": "미리보기", "operating_mode": "Single", "interfaces": []any{}}
|
|
case "/api/v1/logs/system":
|
|
payload = []map[string]any{{"timestamp": time.Now().UTC(), "category": "system", "severity": "info", "source": "preview", "message": "테스트: 일반 시스템 이벤트"}, {"timestamp": time.Now().UTC(), "category": "system", "severity": "warning", "source": "authentication", "message": "테스트: 보존 감사 기록", "audit_protected": true, "audit_id": "preview-audit"}}
|
|
case "/api/v1/logs/links":
|
|
payload = []map[string]any{{"timestamp": time.Now().UTC(), "event_type": "snapshot", "resource": "CH18", "state": "up", "message": "must not display"}, {"timestamp": time.Now().UTC(), "event_type": "transition", "resource": "CH18", "previous_state": "down", "state": "up", "signal": "RX", "message": "테스트: 상태 변경"}}
|
|
case "/api/v1/time/settings":
|
|
payload = map[string]any{"timezone": "Asia/Seoul"}
|
|
case "/api/v1/logs/port":
|
|
channel := r.URL.Query().Get("channel")
|
|
if channel == "" || channel == "CH18" {
|
|
payload = []map[string]any{{"timestamp": time.Now().UTC(), "channel": "CH18", "event_type": "traffic", "direction": "RX", "actor": "preview", "message": "테스트 데이터: 장비 응답을 수신했습니다."}}
|
|
}
|
|
}
|
|
_ = json.NewEncoder(w).Encode(payload)
|
|
return
|
|
}
|
|
if !strings.HasPrefix(r.URL.Path, "/static/") && r.URL.Path != "/" && r.URL.Path != "/logs/ports" && r.URL.Path != "/logs/system" && r.URL.Path != "/logs/links" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
r.AddCookie(&http.Cookie{Name: sessionCookieName, Value: "preview-only"})
|
|
mux.ServeHTTP(w, r)
|
|
})}
|
|
defer server.Close()
|
|
go server.Serve(listener)
|
|
t.Log("READ_ONLY_PREVIEW http://127.0.0.1:19083/logs/ports")
|
|
<-time.After(5 * time.Minute)
|
|
}
|
|
|