package media import ( "context" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" ) func TestControllerUsesCredentialsOnlyAtLoopbackBoundary(t *testing.T) { var payload map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { case r.URL.Path == "/v3/config/paths/get/sense_test": http.NotFound(w, r) case r.URL.Path == "/v3/config/paths/add/sense_test": if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { t.Error(err) } w.WriteHeader(http.StatusOK) default: http.NotFound(w, r) } })) defer server.Close() controller, err := NewHTTPController(server.URL) if err != nil { t.Fatal(err) } source := Source{Path: "sense_test", URI: "rtsp://192.0.2.1/live", Username: "synthetic-user", Password: "synthetic-pass"} if err = controller.Apply(context.Background(), source); err != nil { t.Fatal(err) } if payload["source"] != "rtsp://synthetic-user:synthetic-pass@192.0.2.1/live" { t.Fatalf("unexpected MediaMTX payload: %#v", payload) } if source.URI != "rtsp://192.0.2.1/live" || strings.Contains(source.URI, "synthetic") { t.Fatalf("caller source was mutated: %#v", source) } } func TestControlAPIAndGeneratedConfigAreLoopbackOnly(t *testing.T) { if _, err := NewHTTPController("http://192.0.2.1:9997"); err != ErrUnsafeControlAPI { t.Fatalf("error=%v", err) } var output strings.Builder if err := RenderBaseConfig(&output, "http://127.0.0.1:9997"); err != nil { t.Fatal(err) } if strings.Contains(strings.ToLower(output.String()), "password") || !strings.Contains(output.String(), "paths: {}") { t.Fatalf("unsafe config: %s", output.String()) } } func TestExternalProcessUsesOrphanSafetyGate(t *testing.T) { supervisor := NewSupervisor("", "") supervisor.MarkExternal() if err := supervisor.Stop(context.Background()); err != nil { t.Fatal(err) } state := supervisor.State() if !state.External || state.Owned || state.Phase != "running" { t.Fatalf("unexpected state: %#v", state) } }