arblant
firefox-vpn-client / core / internal / service / helpers_test.go
helpers_test.go79 lines · 2.6 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
package service

import (
	"context"
	"errors"
	"io"
	"net/http"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/armanlife/firefox-vpn-client/core/internal/controlplane"
	"github.com/armanlife/firefox-vpn-client/core/internal/netcheck"
	"github.com/armanlife/firefox-vpn-client/core/internal/proxy"
)

// All values in this file are local fixtures; the control plane transport
// rejects every request it does not recognise.

func init() {
	// Background device registration must never reach the real FxA from a
	// test; the tests that cover it enable it with a mock transport.
	proxy.DeviceRegistration.Store(false)
	// Nor may a test fetch Mozilla's Firefox versions.
	latestFirefoxVersion = func(context.Context) (string, error) { return "", errors.New("offline in tests") }
	// The network wait must not depend on the test machine's network.
	networkConnected = func() bool { return true }
	// Nor may the test machine's networks changing restart anything.
	networkNow = func() (string, bool) { return "", false }
	// Nor may a failure check the real internet.
	netcheck.Check = func(context.Context) netcheck.Result { return netcheck.Unknown }
}

type auditRoundTripper func(*http.Request) (*http.Response, error)

func (f auditRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }

func waitForCondition(t *testing.T, timeout time.Duration, ok func() bool) {
	t.Helper()
	deadline := time.Now().Add(timeout)
	for time.Now().Before(deadline) {
		if ok() {
			return
		}
		time.Sleep(10 * time.Millisecond)
	}
	t.Fatal("condition was not met before timeout")
}

func coreResponse(r *http.Request, status int, body string, header http.Header) *http.Response {
	if header == nil {
		header = make(http.Header)
	}
	return &http.Response{StatusCode: status, Header: header, Body: io.NopCloser(strings.NewReader(body)), Request: r}
}

func setCoreControlPlane(t *testing.T, handler func(*http.Request) (*http.Response, error)) {
	t.Helper()
	t.Setenv("USERPROFILE", t.TempDir())
	t.Setenv("HOME", t.TempDir())
	controlplane.SetTransport(auditRoundTripper(handler))
	t.Cleanup(func() { controlplane.SetTransport(nil) })
	previousDelay := proxy.GuardianRetryInitialDelay
	proxy.GuardianRetryInitialDelay = time.Millisecond
	t.Cleanup(func() { proxy.GuardianRetryInitialDelay = previousDelay })
}

const testGiB = int64(1) << 30

var quotaReset = time.Now().Add(72 * time.Hour).UTC().Truncate(time.Second)

func quotaHeader(usedGiB int64) http.Header {
	h := make(http.Header)
	h.Set("X-Quota-Limit", strconv.FormatInt(50*testGiB, 10))
	h.Set("X-Quota-Remaining", strconv.FormatInt((50-usedGiB)*testGiB, 10))
	h.Set("X-Quota-Reset", quotaReset.Format(time.RFC3339))
	return h
}