Choose the hub: Settings' choice, else the install's channel's; state kept per hub
CI / build (push) Successful in 18s
CI / build (push) Successful in 18s
The client's Settings > T5DE chooses the hub from the next start on (POST /hub/server, kept in server.json); without a choice, -channel (which the client passes) decides: staging for nightly and dev, T5DE's hub for releases. $T5DE_HUB still wins. Each hub's sign-in, product IDs and products are kept in their own folder (T5DE's hub in the data folder as before, others in hubs/<host>), so a hub never gets another's token and product IDs from two hubs never clash; a sign-in saved for another hub is ignored. Signing in always goes to this run's hub. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
8daf91ede3
commit
8ae45fc07a
6 files changed
+292
-53
No files matched your search
@@ -0,0 +1,108 @@
|
||||
package control
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.t5de.dev/t5de/t5de-proxy/internal/hub"
|
||||
"git.t5de.dev/t5de/t5de-proxy/internal/proxy"
|
||||
)
|
||||
|
||||
func TestServer(t *testing.T) {
|
||||
choice := filepath.Join(t.TempDir(), "server.json")
|
||||
for channel, want := range map[string]string{"release": Production, "": Production, "nightly": Staging, "dev": Staging} {
|
||||
if got := Server(choice, channel, ""); got != want {
|
||||
t.Errorf("channel %q: %s", channel, got)
|
||||
}
|
||||
}
|
||||
if err := SaveChoice(choice, "https://hub.example.org"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := Server(choice, "nightly", ""); got != "https://hub.example.org" {
|
||||
t.Errorf("chosen: %s", got)
|
||||
}
|
||||
if got := Server(choice, "nightly", "http://localhost:4101/"); got != "http://localhost:4101" {
|
||||
t.Errorf("$T5DE_HUB: %s", got)
|
||||
}
|
||||
if err := SaveChoice(choice, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := Server(choice, "release", ""); got != Production {
|
||||
t.Errorf("default again: %s", got)
|
||||
}
|
||||
// A choice that isn't a hub address is ignored.
|
||||
os.WriteFile(choice, []byte(`{"hub": "http://evil.test"}`), 0o600)
|
||||
if got := Server(choice, "release", ""); got != Production {
|
||||
t.Errorf("bad choice: %s", got)
|
||||
}
|
||||
|
||||
data := `C:\data`
|
||||
if got := StateDir(data, Production); got != data {
|
||||
t.Errorf("production state %s", got)
|
||||
}
|
||||
if got := StateDir(data, Staging); got != filepath.Join(data, "hubs", "hub.staging.t5de.dev") {
|
||||
t.Errorf("staging state %s", got)
|
||||
}
|
||||
if got := StateDir(data, "http://LocalHost:4101/x"); got != filepath.Join(data, "hubs", "localhost_4101_x") {
|
||||
t.Errorf("local state %s", got)
|
||||
}
|
||||
|
||||
for _, bad := range []string{"http://evil.test", "ftp://x", "hub.t5de.dev", "https://", "https://x/?a=1"} {
|
||||
if _, err := CheckHub(bad); err == nil {
|
||||
t.Errorf("accepted %q", bad)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChoosingServer(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
m := &Manager{Path: filepath.Join(dir, "hub.json"), Hub: Staging, Default: Staging, ChoicePath: filepath.Join(dir, "server.json"),
|
||||
Log: log.New(io.Discard, "", 0)}
|
||||
if s := ask(t, m, "GET", "/hub", ""); s.Hub != Staging || s.Default != Staging || s.Choice != "" || s.Restart {
|
||||
t.Fatalf("status %+v", s)
|
||||
}
|
||||
if s := ask(t, m, "POST", "/hub/server", "hub="+url.QueryEscape(Production+"/")); s.Choice != Production || !s.Restart || s.Hub != Staging {
|
||||
t.Fatalf("chose production: %+v", s)
|
||||
}
|
||||
// Choosing the default again forgets the choice.
|
||||
if s := ask(t, m, "POST", "/hub/server", "hub="+url.QueryEscape(Staging)); s.Choice != "" || s.Restart {
|
||||
t.Fatalf("back to the default: %+v", s)
|
||||
}
|
||||
if _, err := os.Stat(m.ChoicePath); !os.IsNotExist(err) {
|
||||
t.Fatalf("server.json kept: %v", err)
|
||||
}
|
||||
u, _ := url.Parse("http://" + Host + "/hub/server")
|
||||
body := "hub=http://evil.test"
|
||||
req, _ := http.NewRequest("POST", u.String(), strings.NewReader(body))
|
||||
x := &proxy.Exchange{URL: u, Request: req, Body: []byte(body)}
|
||||
m.Intercept(x)
|
||||
if x.Response.StatusCode != 400 {
|
||||
t.Fatalf("plain http hub: %d", x.Response.StatusCode)
|
||||
}
|
||||
// Set by $T5DE_HUB: never a restart to suggest.
|
||||
m.Fixed = true
|
||||
ask(t, m, "POST", "/hub/server", "hub="+url.QueryEscape(Production))
|
||||
if s := ask(t, m, "GET", "/hub", ""); s.Restart || !s.Fixed {
|
||||
t.Fatalf("fixed: %+v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignInForAnotherHub(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "hub.json")
|
||||
if err := (&hub.Saved{URL: "http://localhost:4101", Token: "old"}).Save(path); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
attached := 0
|
||||
m := &Manager{Path: path, Hub: Production, Log: log.New(io.Discard, "", 0), Attach: func(*hub.Client) { attached++ }}
|
||||
m.Start()
|
||||
if s := ask(t, m, "GET", "/hub", ""); s.SignedIn || attached != 0 {
|
||||
t.Fatalf("used another hub's sign-in: %+v", s)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user