Gitfed
bastien-mrq/gitfed / internal / federation / resolver_test.go
package federation

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"path/filepath"
	"strings"
	"testing"

	"git.neuromancer.ovh/bastien-mrq/gitfed/internal/store"
)

func newTestStore(t *testing.T) *store.Store {
	t.Helper()
	s, err := store.Open(filepath.Join(t.TempDir(), "gitfed.db"))
	if err != nil {
		t.Fatalf("open store: %v", err)
	}
	t.Cleanup(func() { s.Close() })
	if err := s.PutInstanceMeta(store.InstanceMeta{
		Domain:      "local.test",
		TrustPolicy: store.TrustPolicyWhitelist,
	}); err != nil {
		t.Fatalf("put instance meta: %v", err)
	}
	return s
}

func TestEnsureTrustCachesKnownDomain(t *testing.T) {
	var hits int
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		hits++
		domain := r.Host
		fmt.Fprintf(w, `{"version":1,"domain":%q,"ca_public_key":"ssh-ed25519 AAAAfake"}`, domain)
	}))
	defer srv.Close()
	domain := strings.TrimPrefix(srv.URL, "http://")

	s := newTestStore(t)
	r := NewResolver(s, "local.test", true /* insecure http, this is a test */)

	t1, err := r.EnsureTrust(domain)
	if err != nil {
		t.Fatalf("first EnsureTrust: %v", err)
	}
	if t1.Status != store.TrustPending {
		t.Errorf("status = %v, want pending (default policy is whitelist)", t1.Status)
	}
	if hits != 1 {
		t.Fatalf("hits after first call = %d, want 1", hits)
	}

	srv.Close() // if EnsureTrust hits the network again, this proves it isn't cached
	t2, err := r.EnsureTrust(domain)
	if err != nil {
		t.Fatalf("second EnsureTrust (should be served from cache): %v", err)
	}
	if t2.CAPublicKey != t1.CAPublicKey {
		t.Errorf("cached record changed between calls: %+v vs %+v", t1, t2)
	}
	if hits != 1 {
		t.Errorf("hits after second call = %d, want still 1 (cached, no re-fetch)", hits)
	}
}

func TestEnsureTrustRateLimitsNewDomainDiscovery(t *testing.T) {
	s := newTestStore(t)
	r := NewResolver(s, "local.test", true)

	// Nothing listens on 127.0.0.1:1, so every attempt fails fast and is
	// never cached — each call re-consumes the discovery quota.
	const unreachable = "127.0.0.1:1"

	for i := 0; i < maxNewDomainDiscoveries; i++ {
		if _, err := r.EnsureTrust(unreachable); err == nil {
			t.Fatalf("attempt %d: expected a connection error, got nil", i)
		} else if strings.Contains(err.Error(), "too many new-domain discovery attempts") {
			t.Fatalf("attempt %d: rate limited too early: %v", i, err)
		}
	}

	_, err := r.EnsureTrust(unreachable)
	if err == nil {
		t.Fatal("expected rate limit error on attempt past the quota, got nil")
	}
	if !strings.Contains(err.Error(), "too many new-domain discovery attempts") {
		t.Fatalf("attempt past quota: got %v, want a rate-limit error", err)
	}
}

func TestEnsureTrustRejectsLocalDomain(t *testing.T) {
	s := newTestStore(t)
	r := NewResolver(s, "local.test", true)

	if _, err := r.EnsureTrust("local.test"); err == nil {
		t.Fatal("expected an error resolving the local domain as federated, got nil")
	}
}