Files
Bridge-and-Join-s/internal/release/install_test.go
T
zuevav 97e99b72c7 Экспорт QUIK — отдельная служба bj-quikexport; модули, выделенные из ядра, ставятся с обновлением
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>
2026-09-16 01:15:27 +03:00

76 lines
2.6 KiB
Go

package release
import (
"crypto/ed25519"
"crypto/rand"
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"testing/fstest"
"time"
)
func stageNPR(t *testing.T, priv ed25519.PrivateKey, req string) string {
t.Helper()
staging := t.TempDir()
bin := filepath.Join(staging, "bj-npr")
if err := os.WriteFile(bin, []byte("npr 0.1.0"), 0o644); err != nil {
t.Fatal(err)
}
sha, size, err := HashFile(bin)
if err != nil {
t.Fatal(err)
}
m := &Manifest{Version: "2026.09.2", Channel: "beta", ReleasedAt: time.Now().UTC().Truncate(time.Second),
Artifacts: []Artifact{{Name: "bj-npr", File: "bj-npr", Version: "0.1.0", SHA256: sha, Size: size, Exec: true}}}
sm, err := Sign(m, priv, "main")
if err != nil {
t.Fatal(err)
}
b, _ := json.Marshal(sm)
if err := os.WriteFile(filepath.Join(staging, "manifest.json"), b, 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(staging, "request.json"), []byte(req), 0o644); err != nil {
t.Fatal(err)
}
return staging
}
// Выделенный из ядра модуль ставится впервые только по явному запросу (new) — вместе с unit-файлом.
func TestInstallSplitModuleWithUnit(t *testing.T) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
units := fstest.MapFS{"systemd/bj-npr.service": {Data: []byte("[Service]\nExecStart=/opt/bj/bj-npr\n")}}
root := t.TempDir()
res := InstallStaged(stageNPR(t, priv, `{"names":["bj-npr"]}`), InstallOptions{Root: root, PubKey: pub, Units: units})
if res.OK || !strings.Contains(res.Error, "не установлен") {
t.Fatalf("без new модуль не должен ставиться: %+v", res)
}
res = InstallStaged(stageNPR(t, priv, `{"names":["bj-npr"],"new":["bj-npr"]}`), InstallOptions{Root: root, PubKey: pub, Units: units})
if !res.OK {
t.Fatalf("установка: %+v", res)
}
if fi, err := os.Stat(filepath.Join(root, "/opt/bj/bj-npr")); err != nil || fi.Mode().Perm() != 0o755 {
t.Fatalf("файл модуля: %v %v", fi, err)
}
if b, err := os.ReadFile(filepath.Join(root, "/etc/systemd/system/bj-npr.service")); err != nil || !strings.Contains(string(b), "ExecStart=/opt/bj/bj-npr") {
t.Fatalf("unit-файл: %q %v", b, err)
}
if inst := LoadInstalled(filepath.Join(root, InstalledFile)); inst["bj-npr"].Version != "0.1.0" {
t.Fatalf("components.json: %+v", inst)
}
// Самостоятельный модуль (не FromCore) по new не ставится.
c, _ := ComponentByName("bj-lk")
if c.FromCore {
t.Fatal("bj-lk не выделялся из ядра")
}
}