bastion 0.60.0
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// actsWorld: one agent with a child, the owner's code set up, nothing real
|
||||
// signalled.
|
||||
func actsWorld(t *testing.T) (dir string, killed *[]int) {
|
||||
t.Helper()
|
||||
uiTestConfigDir(t)
|
||||
killed = stubStopWorld(t, []agentObs{{PID: 101, Kind: kindAgent, Signature: "claude-code", User: "u"}},
|
||||
map[int]int{102: 101})
|
||||
prev := homeForUserFn
|
||||
t.Cleanup(func() { homeForUserFn = prev })
|
||||
homeForUserFn = func(string) string { return "" }
|
||||
d, err := machineFactorDir()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
enrolFactorAt(t, d, machineTestSecret)
|
||||
return d, killed
|
||||
}
|
||||
|
||||
func TestStopFromTheConsoleTakesTheCodeAndTheAgentItShowed(t *testing.T) {
|
||||
_, killed := actsWorld(t)
|
||||
now := time.Now()
|
||||
if f, _ := stopFromConsole(101, "claude-code", "", now); !strings.Contains(f, "type the code") {
|
||||
t.Errorf("no code: %q", f)
|
||||
}
|
||||
if f, _ := stopFromConsole(101, "aider", codeFor(t, machineTestSecret), now); !strings.Contains(f, "is now claude-code") {
|
||||
t.Errorf("a pid that changed hands: %q", f)
|
||||
}
|
||||
if f, _ := stopFromConsole(101, "claude-code", codeFor(t, hostTestSecret), now); !strings.Contains(f, "code refused") {
|
||||
t.Errorf("a wrong code: %q", f)
|
||||
}
|
||||
if len(*killed) != 0 {
|
||||
t.Fatalf("a refused stop signalled %v", *killed)
|
||||
}
|
||||
f, class := stopFromConsole(101, "claude-code", codeFor(t, machineTestSecret), now)
|
||||
if class != "ok" || !strings.HasPrefix(f, "You stopped claude-code (pid 101)") || len(*killed) != 2 {
|
||||
t.Errorf("stop: %q %s, killed %v", f, class, *killed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopFromTheConsoleNeedsTheCodeSetUp(t *testing.T) {
|
||||
uiTestConfigDir(t)
|
||||
killed := stubStopWorld(t, []agentObs{{PID: 101, Kind: kindAgent, Signature: "claude-code"}}, nil)
|
||||
if f, _ := stopFromConsole(101, "claude-code", "123456", time.Now()); !strings.Contains(f, "set up your code") {
|
||||
t.Errorf("no code set up: %q", f)
|
||||
}
|
||||
if len(*killed) != 0 {
|
||||
t.Fatal("stopped with no code set up")
|
||||
}
|
||||
}
|
||||
|
||||
// A mark takes the owner's code; with none set up, nothing is marked.
|
||||
func TestAMarkTakesTheCodeOnceItIsSetUp(t *testing.T) {
|
||||
uiTestConfigDir(t)
|
||||
if why := markCodeRefusal("fp1", "123456"); !strings.Contains(why, "set up your code") {
|
||||
t.Errorf("with no code set up a mark was made: %q", why)
|
||||
}
|
||||
d, err := machineFactorDir()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
enrolFactorAt(t, d, machineTestSecret)
|
||||
if why := markCodeRefusal("fp1", ""); !strings.Contains(why, "type the code") {
|
||||
t.Errorf("no code: %q", why)
|
||||
}
|
||||
if why := markCodeRefusal("fp1", codeFor(t, hostTestSecret)); !strings.Contains(why, "code refused") {
|
||||
t.Errorf("a wrong code: %q", why)
|
||||
}
|
||||
if why := markCodeRefusal("fp1", codeFor(t, machineTestSecret)); why != "" {
|
||||
t.Errorf("the owner's code: %q", why)
|
||||
}
|
||||
}
|
||||
|
||||
// A stop an agent asked for waits on Waiting for you in plain words, and the
|
||||
// owner's code there carries it out.
|
||||
func TestWaitingForYouAnswersWhatIsAskedOfTheMachine(t *testing.T) {
|
||||
d, killed := actsWorld(t)
|
||||
purpose, _ := purposeForLocalStop(101, "claude-code")
|
||||
id, err := stepupNewRequest(d, purpose, map[string]any{"kind": localStopKind, "pid": "101",
|
||||
"signature": "claude-code", "agent": "Claude Code", "session": "shop-a1",
|
||||
"asked_by": "Claude Code session notes-b2 (pid 300)"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
u := uiTestServer(t)
|
||||
h, cookie := u.handler(), uiSignedIn(t, u)
|
||||
page := uiGet(t, h, "/approvals", cookie).Body.String()
|
||||
for _, want := range []string{"Asked of this machine", "Stop Claude Code session shop-a1 (pid 101)",
|
||||
"Asked from Claude Code session notes-b2 (pid 300)."} {
|
||||
if !strings.Contains(page, want) {
|
||||
t.Errorf("Waiting for you does not say %q", want)
|
||||
}
|
||||
}
|
||||
w := uiPost(t, h, "/approvals/machine/approve", url.Values{"id": {id}, "code": {codeFor(t, machineTestSecret)}}, cookie)
|
||||
if body := w.Body.String(); !strings.Contains(body, "You stopped claude-code (pid 101)") {
|
||||
t.Errorf("approve answered %q", firstLines(body))
|
||||
}
|
||||
if len(*killed) != 2 {
|
||||
t.Errorf("killed %v", *killed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDenyingWhatIsAskedOfTheMachineTakesNoCodeAndDoesNothing(t *testing.T) {
|
||||
d, killed := actsWorld(t)
|
||||
purpose, _ := purposeForLocalStop(101, "claude-code")
|
||||
id, err := stepupNewRequest(d, purpose, map[string]any{"kind": localStopKind, "pid": "101", "signature": "claude-code"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
u := uiTestServer(t)
|
||||
w := uiPost(t, u.handler(), "/approvals/machine/deny", url.Values{"id": {id}}, uiSignedIn(t, u))
|
||||
if !strings.Contains(w.Body.String(), "Denied. Nothing was done.") || len(*killed) != 0 {
|
||||
t.Errorf("deny: %q, killed %v", firstLines(w.Body.String()), *killed)
|
||||
}
|
||||
if r := requestsLoad(d)[id]; r.State == "pending" {
|
||||
t.Error("the request is still waiting")
|
||||
}
|
||||
}
|
||||
|
||||
// The owner's code is set up from the Settings page with no Host at all:
|
||||
// start, read the key, confirm with a code from it.
|
||||
func TestTheOwnersCodeIsSetUpFromSettingsWithNoHost(t *testing.T) {
|
||||
uiTestConfigDir(t)
|
||||
u := uiTestServer(t)
|
||||
h, cookie := u.handler(), uiSignedIn(t, u)
|
||||
start := uiPost(t, h, "/setup/stepup/start", url.Values{"target": {"machine"}}, cookie).Body.String()
|
||||
m := regexp.MustCompile(`name="enrol" value="([^"]+)"`).FindStringSubmatch(start)
|
||||
if m == nil || !strings.Contains(start, "Privatae bastion") {
|
||||
t.Fatalf("no enrolment started:\n%s", firstLines(start))
|
||||
}
|
||||
key := uiPost(t, h, "/setup/stepup/key", url.Values{"enrol": {m[1]}}, cookie).Body.String()
|
||||
k := regexp.MustCompile(`[A-Z2-7]{32}`).FindString(key)
|
||||
if k == "" {
|
||||
t.Fatalf("no key shown:\n%s", firstLines(key))
|
||||
}
|
||||
done := uiPost(t, h, "/setup/stepup/confirm", url.Values{"enrol": {m[1]}, "code": {codeFor(t, k)}}, cookie).Body.String()
|
||||
if !strings.Contains(done, "Your code is set up") {
|
||||
t.Errorf("confirm answered:\n%s", firstLines(done))
|
||||
}
|
||||
if !stepupEnrolled(existingMachineFactorDir()) {
|
||||
t.Error("the owner's code was not written")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user