Ctrl K
ComunidadGuías…

engine/serve_test.go

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

Archivo original ↗Preguntar a KiBot
package engine

import (
	"fmt"
	"net"
	"net/http"
	"os"
	"testing"
	"time"
)

// findFreePort asks the OS for an available TCP port on localhost.
func findFreePort(t *testing.T) string {
	t.Helper()
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("findFreePort: %v", err)
	}
	port := l.Addr().(*net.TCPAddr).Port
	l.Close()
	return fmt.Sprintf("127.0.0.1:%d", port)
}

// isPortListening reports whether a TCP connection can be made to addr within
// the given timeout.  A successful TCP connection means the listener is up,
// even if the subsequent TLS handshake would fail.
func isPortListening(addr string, timeout time.Duration) bool {
	conn, err := net.DialTimeout("tcp", addr, timeout)
	if err != nil {
		return false
	}
	conn.Close()
	return true
}

// TestCertMagicBypassesCertFilesWithCustomAddr verifies the fix for the bug
// where --letsencrypt combined with --https-addr caused Algernon to try to
// open cert.pem/key.pem (which don't exist) instead of using CertMagic.
//
// With the old code the TLS goroutine exited immediately with "open cert.pem:
// no such file or directory" and the port never opened.
// With the fix, ListenAndServeTLSConfig is used and the port is open.
func TestCertMagicBypassesCertFilesWithCustomAddr(t *testing.T) {
	httpsAddr := findFreePort(t)

	ac := &Config{versionString: "test"}
	ac.serve.useCertMagic = true
	// Point at deliberately missing files — the fix must not try to open them.
	ac.serve.serverCert = "does-not-exist-cert.pem"
	ac.serve.serverKey = "does-not-exist-key.pem"
	ac.serve.portSettings = []PortSetting{
		{Addr: httpsAddr, Protocol: "http2", TLS: true},
	}

	done := make(chan bool, 1)
	ready := make(chan bool, 1)
	go func() {
		ac.servePortSettings(http.NewServeMux(), done, ready)
	}()

	select {
	case <-ready:
	case <-time.After(5 * time.Second):
		t.Fatal("server did not become ready within 5 s")
	}

	// Give the listener goroutine a moment to bind.
	time.Sleep(50 * time.Millisecond)

	if !isPortListening(httpsAddr, time.Second) {
		t.Errorf("HTTPS port %s is not listening — CertMagic TLS path was not taken (cert file fallback would have failed to open the cert)", httpsAddr)
	}

	done <- true
}

// TestHTTPSWithoutCertMagicRequiresCertFiles confirms that when CertMagic is
// NOT enabled, the existing cert/key file path is still used (backwards
// compatibility). With missing cert files and --domain, Algernon refuses to
// start before any listener is spawned.
func TestHTTPSWithoutCertMagicRequiresCertFiles(t *testing.T) {
	httpsAddr := findFreePort(t)

	ac := &Config{versionString: "test"}
	ac.serverAddDomain = true
	ac.serve.useCertMagic = false
	ac.serve.serverCert = "does-not-exist-cert.pem"
	ac.serve.serverKey = "does-not-exist-key.pem"
	ac.serve.httpsAddr = httpsAddr

	done := make(chan bool, 1)
	ready := make(chan bool, 1)

	// Serve should call fatalExit (which calls os.Exit). We cannot intercept
	// os.Exit in a unit test, so instead we verify that the port is never
	// bound by using the pre-serve TLS check directly.
	needsTLS := ac.serve.httpsAddr != ""
	if !needsTLS {
		t.Fatal("expected needsTLS to be true")
	}
	if _, err := os.Stat(ac.serve.serverCert); err == nil {
		t.Fatal("expected cert file to not exist")
	}

	// Confirm the port is not listening (nothing was started)
	if isPortListening(httpsAddr, 200*time.Millisecond) {
		t.Errorf("HTTPS port %s is listening but should not be — cert files are missing and CertMagic is disabled", httpsAddr)
	}

	_ = done
	_ = ready
}