| package orchestrator |
|
|
| import ( |
| "fmt" |
| "io" |
| "net" |
| "net/http" |
| "net/http/httptest" |
| "testing" |
|
|
| "github.com/pinchtab/pinchtab/internal/bridge" |
| ) |
|
|
| func startLocalHTTPServer(t *testing.T, h http.Handler) (*httptest.Server, string) { |
| t.Helper() |
| ln, err := net.Listen("tcp", "localhost:0") |
| if err != nil { |
| t.Fatalf("listen: %v", err) |
| } |
| server := httptest.NewUnstartedServer(h) |
| server.Listener = ln |
| server.Start() |
| return server, fmt.Sprintf("%d", ln.Addr().(*net.TCPAddr).Port) |
| } |
|
|
| func TestProxyTabRequest_FallsBackToOnlyRunningInstance(t *testing.T) { |
| backend, port := startLocalHTTPServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| w.Header().Set("Content-Type", "application/json") |
| _, _ = io.WriteString(w, `{"ok":true}`) |
| })) |
| defer backend.Close() |
|
|
| o := NewOrchestrator(t.TempDir()) |
| o.client = backend.Client() |
| o.instances["inst_1"] = &InstanceInternal{ |
| Instance: bridge.Instance{ID: "inst_1", Status: "running", Port: port}, |
| URL: "http://localhost:" + port, |
| cmd: &mockCmd{pid: 1234, isAlive: true}, |
| } |
|
|
| req := httptest.NewRequest(http.MethodGet, "/tabs/ABC123/snapshot", nil) |
| req.SetPathValue("id", "ABC123") |
| w := httptest.NewRecorder() |
|
|
| orig := processAliveFunc |
| processAliveFunc = func(pid int) bool { return true } |
| defer func() { processAliveFunc = orig }() |
|
|
| o.proxyTabRequest(w, req) |
|
|
| resp := w.Result() |
| if resp.StatusCode != http.StatusOK { |
| t.Fatalf("status = %d, want 200", resp.StatusCode) |
| } |
| } |
|
|
| func TestSingleRunningInstance_MultipleInstancesReturnsNil(t *testing.T) { |
| o := NewOrchestrator(t.TempDir()) |
| o.instances["inst_1"] = &InstanceInternal{Instance: bridge.Instance{ID: "inst_1", Status: "running"}, cmd: &mockCmd{pid: 1, isAlive: true}} |
| o.instances["inst_2"] = &InstanceInternal{Instance: bridge.Instance{ID: "inst_2", Status: "running"}, cmd: &mockCmd{pid: 2, isAlive: true}} |
|
|
| orig := processAliveFunc |
| processAliveFunc = func(pid int) bool { return true } |
| defer func() { processAliveFunc = orig }() |
|
|
| if got := o.singleRunningInstance(); got != nil { |
| t.Fatalf("expected nil, got %v", got.ID) |
| } |
| } |
|
|
| func TestSingleRunningInstance_IgnoresStopped(t *testing.T) { |
| o := NewOrchestrator(t.TempDir()) |
| o.instances["inst_1"] = &InstanceInternal{Instance: bridge.Instance{ID: "inst_1", Status: "running"}, cmd: &mockCmd{pid: 1, isAlive: true}} |
| o.instances["inst_2"] = &InstanceInternal{Instance: bridge.Instance{ID: "inst_2", Status: "stopped"}} |
|
|
| orig := processAliveFunc |
| processAliveFunc = func(pid int) bool { return pid == 1 } |
| defer func() { processAliveFunc = orig }() |
|
|
| got := o.singleRunningInstance() |
| if got == nil || got.ID != "inst_1" { |
| t.Fatalf("got %#v, want inst_1", got) |
| } |
| } |
|
|
| func TestProxyToURL_UsesAttachedBridgeOriginAndAuth(t *testing.T) { |
| backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| if got := r.Header.Get("Authorization"); got != "Bearer bridge-token" { |
| t.Fatalf("authorization = %q, want %q", got, "Bearer bridge-token") |
| } |
| if r.URL.Path != "/tabs/tab-1/snapshot" { |
| t.Fatalf("path = %q", r.URL.Path) |
| } |
| _, _ = io.WriteString(w, `ok`) |
| })) |
| defer backend.Close() |
|
|
| o := NewOrchestrator(t.TempDir()) |
| o.client = backend.Client() |
| attached, err := o.AttachBridge("bridge1", backend.URL, "bridge-token") |
| if err != nil { |
| t.Fatalf("AttachBridge failed: %v", err) |
| } |
|
|
| req := httptest.NewRequest(http.MethodGet, "/tabs/tab-1/snapshot", nil) |
| req.SetPathValue("id", "tab-1") |
| w := httptest.NewRecorder() |
|
|
| targetURL, err := o.instancePathURLFromBridge(attached, "/tabs/tab-1/snapshot", "") |
| if err != nil { |
| t.Fatalf("instancePathURLFromBridge failed: %v", err) |
| } |
| o.proxyToURL(w, req, targetURL) |
|
|
| if w.Code != http.StatusOK { |
| t.Fatalf("status = %d, want 200", w.Code) |
| } |
| } |
|
|