97e99b72c7
bj-quikexport (modules/quikexport + cmd/bj-quikexport, 127.0.0.1:8084): управление ARQA quikexport перенесено из bj-server. Настройки у службы в /var/lib/bj/.bj/quik-export/settings.json (при первом запуске переносятся из setup.json), раздел отдаётся фрагментом с индикатором меню, действия отвечают X-BJ-Flash. bj-server встраивает раздел в «Настройку», проксирует формы /admin/setup/quikexport/… и передаёт лицензию (X-BJ-Licensed). Общая проверка служебного ключа — modules/modkit (им пользуется и bj-npr), общее для прокси — lkgateway/modules.go. Выделенные из ядра модули (bj-npr, bj-quikexport: Component.FromCore) при обновлении ставятся и там, где их ещё нет, если входят в лицензию: release.MarkSplitModules, request.json "new", root-установщик ставит и включает unit-файл из комплекта. Если в обновлении меняется сам установщик, модули ставятся после перезапуска bj-server (ensureSplitModules). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
75 lines
2.6 KiB
Go
75 lines
2.6 KiB
Go
package quikexport
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
const testKey = "k-0123456789abcdef0123456789abcdef"
|
|
|
|
func TestServiceSectionAndSave(t *testing.T) {
|
|
dir := t.TempDir()
|
|
keyPath := filepath.Join(dir, "module.key")
|
|
setup := filepath.Join(dir, "setup.json")
|
|
store := filepath.Join(dir, "quik-export", "settings.json")
|
|
if err := os.WriteFile(keyPath, []byte(testKey+"\n"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(setup, []byte(`{"quik_export":{"enabled":true,"quik_host":"10.0.0.1","quik_user":"exp","preset":"npr"}}`), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
srv, err := New(Config{StorePath: store, SetupPath: setup, KeyPath: keyPath})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := srv.store.Get(); got.QuikHost != "10.0.0.1" || !got.Enabled || got.QuikPort != 15100 {
|
|
t.Fatalf("перенос настроек из setup.json: %+v", got)
|
|
}
|
|
h := srv.Handler()
|
|
|
|
rec := httptest.NewRecorder()
|
|
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/section", nil))
|
|
if rec.Code != http.StatusForbidden {
|
|
t.Fatalf("без ключа: код %d", rec.Code)
|
|
}
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/section", nil)
|
|
req.Header.Set("X-BJ-Module-Key", testKey)
|
|
req.Header.Set("X-BJ-Licensed", "1")
|
|
rec = httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("раздел: код %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
body := rec.Body.String()
|
|
for _, want := range []string{"Подключение к серверу QUIK", `value="10.0.0.1"`, "/admin/setup/quikexport/apply"} {
|
|
if !strings.Contains(body, want) {
|
|
t.Errorf("в разделе нет %q", want)
|
|
}
|
|
}
|
|
if ind := rec.Header().Get("X-BJ-Indicator"); ind == "err" {
|
|
t.Errorf("индикатор при действующей лицензии: %q", ind)
|
|
}
|
|
|
|
form := url.Values{"part": {"quik"}, "quik_host": {"10.0.0.2"}, "quik_user": {"exp"}, "section": {"sec-quikexport"}}
|
|
req = httptest.NewRequest(http.MethodPost, "/admin/setup/quikexport", strings.NewReader(form.Encode()))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
req.Header.Set("X-BJ-Module-Key", testKey)
|
|
rec = httptest.NewRecorder()
|
|
h.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusNoContent || rec.Header().Get("X-BJ-Flash") == "" {
|
|
t.Fatalf("сохранение: код %d, flash %q", rec.Code, rec.Header().Get("X-BJ-Flash"))
|
|
}
|
|
if got := srv.store.Get(); got.QuikHost != "10.0.0.2" {
|
|
t.Fatalf("после сохранения quik_host = %q", got.QuikHost)
|
|
}
|
|
if fi, err := os.Stat(store); err != nil || fi.Mode().Perm() != 0o600 {
|
|
t.Fatalf("файл настроек: %v %v", fi, err)
|
|
}
|
|
}
|