arblant
firefox-vpn-client / core / internal / service / systemproxy_test.go
systemproxy_test.go84 lines · 2.7 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
package service

import (
	"encoding/json"
	"net/http"
	"testing"
	"time"

	"github.com/armanlife/firefox-vpn-client/core/internal/config"
)

func waitPhase(t *testing.T, d *Daemon, want string) {
	t.Helper()
	deadline := time.Now().Add(5 * time.Second)
	for time.Now().Before(deadline) {
		d.mu.Lock()
		phase := d.state.Phase
		d.mu.Unlock()
		if phase == want {
			return
		}
		time.Sleep(20 * time.Millisecond)
	}
	t.Fatalf("phase did not become %q", want)
}

// POST /api/system-proxy accepts only set and clear; the choice is saved
// and reported by the status.
func TestSystemProxyEndpointAcceptsSetAndClear(t *testing.T) {
	d := newTestDaemon(t)
	client := newDashboardClient(t, d)
	if view, _ := d.StatusView(); view.SystemProxy != config.SystemProxyClear {
		t.Fatalf("default system proxy mode %q", view.SystemProxy)
	}
	for _, mode := range []string{config.SystemProxySet, config.SystemProxyClear} {
		if rec := client.do(http.MethodPost, "/api/system-proxy", map[string]string{"mode": mode}); rec.Code != http.StatusOK {
			t.Fatalf("mode %s: %d %s", mode, rec.Code, rec.Body.String())
		}
		saved, _, err := config.Load(d.paths.Config)
		if err != nil || saved.SystemProxy.Mode != mode {
			t.Fatalf("mode %s must be saved: %q %v", mode, saved.SystemProxy.Mode, err)
		}
		if view, _ := d.StatusView(); view.SystemProxy != mode {
			t.Fatalf("status reports %q, want %q", view.SystemProxy, mode)
		}
	}
	for _, mode := range []string{"pac", "unchanged", ""} {
		if rec := client.do(http.MethodPost, "/api/system-proxy", map[string]string{"mode": mode}); rec.Code != http.StatusBadRequest {
			t.Fatalf("mode %q must be refused: %d %s", mode, rec.Code, rec.Body.String())
		}
	}
	if d.config().SystemProxy.Mode != config.SystemProxyClear {
		t.Fatalf("a refused mode must not change the setting: %q", d.config().SystemProxy.Mode)
	}
}

// The proxy has no on/off switch: the endpoint and the status field are
// gone, and the engine starts on its own.
func TestNoVPNSwitch(t *testing.T) {
	d := newTestDaemon(t)
	d.mu.Lock()
	d.cfg.Dashboard.Listen = "127.0.0.1:0"
	d.mu.Unlock()
	done := make(chan error, 1)
	go func() { done <- d.Run() }()
	defer func() {
		d.Shutdown()
		<-done
	}()
	waitPhase(t, d, PhaseWaiting) // no account yet

	client := newDashboardClient(t, d)
	if rec := client.do(http.MethodPost, "/api/proxy/enabled", map[string]bool{"enabled": false}); rec.Code == http.StatusOK {
		t.Fatalf("POST /api/proxy/enabled must not exist: %d", rec.Code)
	}
	rec := client.do(http.MethodGet, "/api/status", nil)
	var status map[string]any
	if err := json.Unmarshal(rec.Body.Bytes(), &status); err != nil {
		t.Fatal(err)
	}
	if _, ok := status["vpn_enabled"]; ok {
		t.Fatal("the status must not report vpn_enabled")
	}
}