Ctrl K
ComunidadGuías…

adguardhome/context_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package adguardhome

import (
	"context"
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"sync/atomic"
	"testing"
	"time"
)

func TestProviderConstructionDoesNotMakeHTTPRequests(t *testing.T) {
	var requests atomic.Int32
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		requests.Add(1)
		http.Error(w, "unavailable", http.StatusServiceUnavailable)
	}))
	defer s.Close()
	t.Setenv(envURL, s.URL)
	t.Setenv(envUser, "test-user")
	t.Setenv(envPassword, "test-password")
	t.Setenv(envUseDNSRewrite, "true")
	p, err := NewAdguardHomeProvider(context.Background(), false)
	if err != nil {
		t.Fatalf("construction must not depend on AdGuard availability: %v", err)
	}
	if requests.Load() != 0 {
		t.Fatalf("constructor made %d HTTP requests", requests.Load())
	}
	if err = p.Initialize(context.Background()); err == nil {
		t.Fatal("explicit initialization must report the HTTP failure")
	}
	if requests.Load() != 1 {
		t.Fatalf("initialization made %d HTTP requests, want 1", requests.Load())
	}
}

func TestInitializationCancellation(t *testing.T) {
	started := make(chan struct{})
	cancelled := make(chan struct{})
	s := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
		close(started)
		<-r.Context().Done()
		close(cancelled)
	}))
	defer s.Close()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	p := &AdguardHomeProvider{client: newAdguardHomeClient(context.Background(), s.URL+"/", "test", "test", false), ruleFormat: rewriteRuleFormat()}
	result := make(chan error, 1)
	go func() { result <- p.Initialize(ctx) }()
	select {
	case <-started:
	case <-time.After(5 * time.Second):
		t.Fatal("initialization request did not start")
	}
	cancel()
	select {
	case err := <-result:
		if !errors.Is(err, context.Canceled) {
			t.Fatalf("initialization error=%v, want context cancellation", err)
		}
	case <-time.After(5 * time.Second):
		t.Fatal("initialization ignored cancellation")
	}
	select {
	case <-cancelled:
	case <-time.After(5 * time.Second):
		t.Fatal("outgoing HTTP request ignored cancellation")
	}
}

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

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

func TestClientPropagatesRequestContext(t *testing.T) {
	for _, method := range []string{http.MethodGet, http.MethodPost} {
		t.Run(method, func(t *testing.T) {
			type contextKey struct{}
			ctx, cancel := context.WithTimeout(context.WithValue(context.Background(), contextKey{}, "request-value"), 5*time.Second)
			defer cancel()
			wantDeadline, _ := ctx.Deadline()
			requests := 0
			c := newAdguardHomeClient(context.Background(), "http://adguard.test/control/", "test", "test", false)
			c.hc.Transport = contextTestTransport(func(r *http.Request) (*http.Response, error) {
				requests++
				deadline, ok := r.Context().Deadline()
				if r.Method != method || r.Context().Value(contextKey{}) != "request-value" || !ok || deadline != wantDeadline {
					t.Errorf("HTTP request did not inherit method, deadline and values")
				}
				return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader(`{"user_rules":[]}`))}, nil
			})
			var err error
			if method == http.MethodGet {
				_, err = c.GetFilteringRules(ctx)
			} else {
				err = c.SaveFilteringRules(ctx, []string{"# test rule"})
			}
			if err != nil || requests != 1 {
				t.Fatalf("HTTP request error=%v, count=%d", err, requests)
			}
		})
	}
}

func TestInitializeCancelledBeforeRequest(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	p := &AdguardHomeProvider{}
	if err := p.Initialize(ctx); !errors.Is(err, context.Canceled) {
		t.Fatalf("initialization error=%v, want context cancellation", err)
	}
}