package lkgateway import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "git.zetit.ru/zuevav/Bridge-and-Join-s/modules/lk" ) // Режим ожидания целиком: настоящий кабинет (modules/lk) в режиме agent и настоящий цикл // ядра. Кабинет ставит запрос в очередь, ядро своим исходящим подключением забирает его, // выполняет обработчиком /api/lk/* и возвращает ответ. func TestLKAgentEndToEnd(t *testing.T) { const key = "lk-agent-test-key" cabinet, err := lk.New(lk.Config{BJKey: key, Agent: true}) if err != nil { t.Fatal(err) } cab := httptest.NewServer(cabinet.Handler()) defer cab.Close() rc, err := NewRuntimeConfig(t.TempDir() + "/setup.json") if err != nil { t.Fatal(err) } lkCfg := rc.Snapshot().LK lkCfg.APIKey, lkCfg.AgentMode, lkCfg.CabinetURL = key, true, cab.URL if err := rc.UpdateLK(lkCfg); err != nil { t.Fatal(err) } mux := http.NewServeMux() registerLKAPI(mux, rc, nil) core := &Server{rc: rc, mux: mux} ctx, cancel := context.WithCancel(context.Background()) defer cancel() go core.lkAgentLoop(ctx) // Администратор проверяет связь: кабинет спрашивает ядро через очередь. req, _ := http.NewRequest(http.MethodGet, cab.URL+"/health", nil) req.Header.Set("X-BJ-Key", key) client := &http.Client{Timeout: 15 * time.Second} resp, err := client.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() var out struct { OK bool `json:"ok"` Mode string `json:"mode"` BJ map[string]any `json:"bj"` } if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { t.Fatal(err) } if resp.StatusCode != http.StatusOK || !out.OK || out.Mode != "agent" || out.BJ["service"] != "bj-server" { t.Fatalf("ответ ядра через очередь: код %d, %+v", resp.StatusCode, out) } if st := lkAgentStatus(rc.Snapshot()); !st.Connected || st.Served == 0 { t.Fatalf("состояние канала: %+v", st) } // Без ключа наружу — только признак жизни, без адресов и подробностей. resp2, err := client.Get(cab.URL + "/health") if err != nil { t.Fatal(err) } defer resp2.Body.Close() var pub map[string]any _ = json.NewDecoder(resp2.Body).Decode(&pub) if len(pub) != 1 || pub["ok"] != true { t.Fatalf("/health без ключа раскрывает лишнее: %+v", pub) } } // Ядро выполняет через канал только запросы к API кабинета — ничего другого. func TestLKAgentRejectsForeignPath(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(map[string]string{"id": "1", "method": "GET", "path": "/admin/setup"}) })) defer srv.Close() if task, err := lkAgentFetch(context.Background(), srv.Client(), srv.URL, "k"); err == nil || task != nil { t.Fatalf("чужой путь принят: %+v", task) } }