bd24a0db98
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
107 lines
4.7 KiB
Go
107 lines
4.7 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)
|
|
}
|
|
if len(res.Enabled) != 1 || res.Enabled[0] != "bj-npr" {
|
|
t.Fatalf("служба впервые установленного модуля должна включаться: %+v", res.Enabled)
|
|
}
|
|
|
|
// Повторная установка того же модуля: служба уже стоит и модуль был — автозапуск не трогаем
|
|
// (администратор мог выключить службу сам).
|
|
res = InstallStaged(stageNPR(t, priv, `{"names":["bj-npr"]}`), InstallOptions{Root: root, PubKey: pub, Units: units})
|
|
if !res.OK || len(res.Enabled) != 0 {
|
|
t.Fatalf("повторная установка не должна включать службу: %+v", res)
|
|
}
|
|
|
|
// Установка ядра с образа: файла службы ещё нет (его кладёт следующий шаг установщика) — включать нечего,
|
|
// и это не ошибка (иначе systemctl enable падает: «Unit file … does not exist»).
|
|
root3 := t.TempDir()
|
|
res = InstallStaged(stageNPR(t, priv, `{"names":["bj-npr"],"new":["bj-npr"]}`), InstallOptions{Root: root3, PubKey: pub})
|
|
if !res.OK || len(res.Enabled) != 0 {
|
|
t.Fatalf("без unit-файла служба не включается: %+v", res)
|
|
}
|
|
|
|
// Установка с образа: unit-файл уже положен (выключенным), файла модуля нет — служба включается.
|
|
root2 := t.TempDir()
|
|
if err := os.MkdirAll(filepath.Join(root2, "/etc/systemd/system"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(root2, "/etc/systemd/system/bj-npr.service"), []byte("[Service]\n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
res = InstallStaged(stageNPR(t, priv, `{"names":["bj-npr"],"new":["bj-npr"]}`), InstallOptions{Root: root2, PubKey: pub, Units: units})
|
|
if !res.OK || len(res.Enabled) != 1 || res.Enabled[0] != "bj-npr" {
|
|
t.Fatalf("unit-файл из образа: служба должна включиться при первой установке модуля: %+v", res)
|
|
}
|
|
|
|
// Самостоятельный модуль (не FromCore) по new не ставится.
|
|
c, _ := ComponentByName("bj-lk")
|
|
if c.FromCore {
|
|
t.Fatal("bj-lk не выделялся из ядра")
|
|
}
|
|
}
|