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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package proxy
import (
"context"
"errors"
"fmt"
"net/http"
"strings"
"sync/atomic"
"testing"
"time"
"github.com/armanlife/firefox-vpn-client/core/internal/fxa"
"github.com/armanlife/firefox-vpn-client/core/internal/guardian"
)
func TestIsQuotaExhausted(t *testing.T) {
t.Parallel()
cases := []struct {
name string
pass *guardian.ProxyPassInfo
want bool
}{
{"nil pass", nil, false},
{"no quota headers", &guardian.ProxyPassInfo{}, false},
{"zero remaining", &guardian.ProxyPassInfo{QuotaLeft: "0"}, true},
{"quota remaining", &guardian.ProxyPassInfo{QuotaLeft: "1073741824"}, false},
{"unparsable value", &guardian.ProxyPassInfo{QuotaLeft: "n/a"}, false},
}
for _, tc := range cases {
if got := isQuotaExhausted(tc.pass); got != tc.want {
t.Errorf("%s: expected %v, got %v", tc.name, tc.want, got)
}
}
}
func TestFetchProxyPassRetriesTransientGuardianFailure(t *testing.T) {
var calls atomic.Int32
pass := coreFixtureJWT(10 * time.Minute)
setCoreControlPlane(t, func(r *http.Request) (*http.Response, error) {
if !strings.HasSuffix(r.URL.Path, "/api/v1/fpn/token") {
return nil, fmt.Errorf("unexpected request")
}
if calls.Add(1) < 3 {
return coreResponse(r, http.StatusServiceUnavailable, "busy", nil), nil
}
return coreResponse(r, http.StatusOK, `{"token":"`+pass+`"}`, nil), nil
})
got, err := fetchProxyPassCtx(context.Background(), "https://guardian.invalid", "access")
if err != nil || got.RawToken != pass || calls.Load() != 3 {
t.Fatalf("expected success on the third attempt, got calls=%d err=%v", calls.Load(), err)
}
}
func TestFetchProxyPassDoesNotRetryRegionRefusal(t *testing.T) {
var calls atomic.Int32
setCoreControlPlane(t, func(r *http.Request) (*http.Response, error) {
calls.Add(1)
return coreResponse(r, http.StatusUnavailableForLegalReasons, "region", nil), nil
})
_, err := fetchProxyPassCtx(context.Background(), "https://guardian.invalid", "access")
if !errors.Is(err, guardian.ErrRegionUnavailable) || calls.Load() != 1 {
t.Fatalf("expected one region refusal, got calls=%d err=%v", calls.Load(), err)
}
if GuardianFailureHint(err) == "" {
t.Fatal("expected an operator hint for HTTP 451")
}
}
func TestAcquireUsablePassActivatesOnNotEntitledWithoutRefresh(t *testing.T) {
var activated atomic.Bool
pass := coreFixtureJWT(10 * time.Minute)
setCoreControlPlane(t, func(r *http.Request) (*http.Response, error) {
switch {
case strings.HasSuffix(r.URL.Path, "/api/v1/fpn/activate") && r.Method == http.MethodPost:
activated.Store(true)
return coreResponse(r, http.StatusOK, `{"subscribed":false,"uid":1,"maxBytes":"0"}`, nil), nil
case strings.HasSuffix(r.URL.Path, "/api/v1/fpn/token") && !activated.Load():
return coreResponse(r, http.StatusForbidden, "not entitled", nil), nil
case strings.HasSuffix(r.URL.Path, "/api/v1/fpn/token"):
return coreResponse(r, http.StatusOK, `{"token":"`+pass+`"}`, nil), nil
}
return nil, fmt.Errorf("unexpected request (an OAuth refresh is not expected) %s", r.URL)
})
auth := &runtimeAuth{Token: &fxa.TokenResponse{AccessToken: "access", RefreshToken: "refresh", ExpiresIn: 3600}, ObtainedAt: time.Now()}
controller := &Controller{guardian: "https://guardian.invalid", auth: auth}
gotAuth, got, err := controller.acquireUsablePass(context.Background())
if err != nil || got.RawToken != pass || gotAuth != auth || !activated.Load() {
t.Fatalf("expected activation then a pass with the same auth, got err=%v activated=%v", err, activated.Load())
}
}
func TestAcquireUsablePassRefusesExhaustedQuota(t *testing.T) {
pass := coreFixtureJWT(10 * time.Minute)
setCoreControlPlane(t, func(r *http.Request) (*http.Response, error) {
header := make(http.Header)
header.Set("X-Quota-Limit", "100")
header.Set("X-Quota-Remaining", "0")
header.Set("X-Quota-Reset", "2026-11-01T00:00:00Z")
return coreResponse(r, http.StatusOK, `{"token":"`+pass+`"}`, header), nil
})
auth := &runtimeAuth{Token: &fxa.TokenResponse{AccessToken: "access", ExpiresIn: 3600}, ObtainedAt: time.Now()}
controller := &Controller{guardian: "https://guardian.invalid", auth: auth}
_, _, err := controller.acquireUsablePass(context.Background())
if !errors.Is(err, guardian.ErrQuotaExceeded) || !strings.Contains(err.Error(), "2026-11-01") {
t.Fatalf("expected a quota error naming the reset time, got %v", err)
}
}