Panel redesign, per-server I1 masking, node minimum version 0.5.0, installer output
ci / check (push) Successful in 23m54s
ci / check (push) Successful in 23m54s
- UI: settings tab with sections (server, API keys, Prometheus, audit, migrate, about); server settings moved there; master keys table rework (menu, chips, version, compact phone cards); three-state node version chip - I1: random per-server template (DNS / QUIC Initial / STUN) kept in users.db, GET/POST /api/masking, change from the master keys banner - Hub rejects nodes older than 0.5.0 (close 4026), /ui/nodes reports outdated - install.sh: spinner for long commands (full output in the log), step progress bar, cards; installs older than 0.5.0 require a full reinstall with data reset - Bump to Beta 0.5.0
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
|
||||
"github.com/maeneko/forgetting/internal/jsjson"
|
||||
)
|
||||
|
||||
// Нода старше MinNodeVersion (или без версии) с панелью не работает: хаб отклоняет её с 4026 и
|
||||
// не считает подключённой, но версию запоминает — панель показывает, до чего обновлять.
|
||||
func TestOutdatedNodeRejected(t *testing.T) {
|
||||
p := newMovePanel(t, "off", "40500")
|
||||
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
|
||||
if _, err := p.a.db.Exec("INSERT INTO nodes (id, name, pub, created_at) VALUES (3, 'Frankfurt', ?, 1)", b64url.EncodeToString(pub)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
srv := httptest.NewServer(http.HandlerFunc(p.a.nodes.serveWS))
|
||||
defer srv.Close()
|
||||
|
||||
connect := func(version string) (jsjson.Object, websocket.StatusCode) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
ws, _, err := websocket.Dial(ctx, "ws"+strings.TrimPrefix(srv.URL, "http"), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ws.CloseNow()
|
||||
read := func() (jsjson.Object, error) {
|
||||
_, raw, err := ws.Read(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, _ := jsjson.Decode(raw)
|
||||
o, _ := v.(jsjson.Object)
|
||||
return o, nil
|
||||
}
|
||||
hello, _ := read()
|
||||
auth := jsjson.Object{{Key: "t", Val: "auth"}, {Key: "node_id", Val: 3},
|
||||
{Key: "sig", Val: b64url.EncodeToString(ed25519.Sign(priv, authMsg(hello.Get2("nonce").(string), 3)))}}
|
||||
if version != "" {
|
||||
auth = append(auth, jsjson.KV{Key: "version", Val: version})
|
||||
}
|
||||
ws.Write(ctx, websocket.MessageText, jsjson.MustMarshal(auth))
|
||||
o, err := read()
|
||||
return o, websocket.CloseStatus(err)
|
||||
}
|
||||
|
||||
for _, v := range []string{"0.4.9", ""} {
|
||||
if o, code := connect(v); code != closeOutdated {
|
||||
t.Fatalf("версия %q: ждали закрытия %d, получили %v / %v", v, closeOutdated, o, code)
|
||||
}
|
||||
p.a.nodes.mu.Lock()
|
||||
_, on := p.a.nodes.conns[3]
|
||||
p.a.nodes.mu.Unlock()
|
||||
if on {
|
||||
t.Fatalf("версия %q: нода считается подключённой", v)
|
||||
}
|
||||
}
|
||||
var stored string
|
||||
p.a.db.QueryRow("SELECT COALESCE(version, '') FROM nodes WHERE id = 3").Scan(&stored)
|
||||
if stored != "" { // последняя попытка — без версии
|
||||
t.Fatalf("версия в БД: %q", stored)
|
||||
}
|
||||
if o, code := connect(MinNodeVersion); o.Get2("t") != "ready" {
|
||||
t.Fatalf("актуальная версия: %v / %v", o, code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCmpVersion(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
a, b string
|
||||
sign int
|
||||
}{{"0.5.0", "0.5.0", 0}, {"0.4.9", "0.5.0", -1}, {"0.10.0", "0.9.9", 1}, {"0.5", "0.5.0", 0}, {"0.5.1-beta", "0.5.1", 0}} {
|
||||
got := cmpVersion(c.a, c.b)
|
||||
if (got > 0) != (c.sign > 0) || (got < 0) != (c.sign < 0) {
|
||||
t.Errorf("cmpVersion(%q, %q) = %d", c.a, c.b, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user