Files
Tommy Verrall 54de369c1e Skip ipv6 metadata endpoint request (#6118)
Co-authored-by: Tommy Verrall <tommy@nymtech.net>
2025-10-16 11:39:53 +01:00

321 lines
8.5 KiB
Go

package main
import (
"math"
"testing"
)
// Test the abs helper function
func TestAbs(t *testing.T) {
tests := []struct {
input int
expected int
}{
{5, 5},
{-5, 5},
{0, 0},
{-1, 1},
{1, 1},
}
for _, test := range tests {
result := int(math.Abs(float64(test.input)))
if result != test.expected {
t.Errorf("abs(%d) = %d, expected %d", test.input, result, test.expected)
}
}
}
// Test URL shuffling behavior
func TestURLShuffling(t *testing.T) {
urls := []string{"url1", "url2", "url3"}
shuffledUrls := make([]string, len(urls))
copy(shuffledUrls, urls)
if len(shuffledUrls) != len(urls) {
t.Error("Shuffled URLs length mismatch")
}
// Test that all original URLs are still present
for _, originalURL := range urls {
found := false
for _, shuffledURL := range shuffledUrls {
if shuffledURL == originalURL {
found = true
break
}
}
if !found {
t.Errorf("URL %s not found in shuffled list", originalURL)
}
}
}
// Test timeout progression logic
func TestTimeoutProgression(t *testing.T) {
baseTimeout := uint64(5)
// Test ping timeout progression (+1s per retry)
for attempt := 0; attempt < 3; attempt++ {
expectedTimeout := baseTimeout + uint64(attempt*1)
actualTimeout := baseTimeout + uint64(attempt*1)
if actualTimeout != expectedTimeout {
t.Errorf("Ping attempt %d: expected timeout %d, got %d", attempt, expectedTimeout, actualTimeout)
}
}
// Test download timeout progression (+5s per retry)
for attempt := 0; attempt < 3; attempt++ {
expectedTimeout := baseTimeout + uint64(attempt*5)
actualTimeout := baseTimeout + uint64(attempt*5)
if actualTimeout != expectedTimeout {
t.Errorf("Download attempt %d: expected timeout %d, got %d", attempt, expectedTimeout, actualTimeout)
}
}
}
// Test consecutive failure counting logic
func TestConsecutiveFailures(t *testing.T) {
maxFailures := 3
consecutiveFailures := 0
// Simulate failures
for i := 0; i < maxFailures; i++ {
consecutiveFailures++
if consecutiveFailures >= maxFailures {
break
}
}
if consecutiveFailures != maxFailures {
t.Errorf("Expected %d consecutive failures, got %d", maxFailures, consecutiveFailures)
}
}
// Test early exit logic
func TestEarlyExitLogic(t *testing.T) {
maxConsecutiveFailures := 3
consecutiveFailures := 0
// Simulate early exit after 3 failures
for i := 0; i < 5; i++ { // Try 5 times
consecutiveFailures++
if consecutiveFailures >= maxConsecutiveFailures {
break // Early exit
}
}
if consecutiveFailures != maxConsecutiveFailures {
t.Errorf("Expected early exit after %d failures, got %d", maxConsecutiveFailures, consecutiveFailures)
}
}
// Test URL list selection logic
func TestURLListSelection(t *testing.T) {
// Test IPv4 URL selection
ipVersion := uint8(4)
var urlsToTry []string
if ipVersion == 4 {
urlsToTry = fileUrls
} else {
urlsToTry = fileUrlsV6
}
if len(urlsToTry) == 0 {
t.Error("Expected non-empty URL list for IPv4")
}
// Test IPv6 URL selection
ipVersion = uint8(6)
if ipVersion == 4 {
urlsToTry = fileUrls
} else {
urlsToTry = fileUrlsV6
}
if len(urlsToTry) == 0 {
t.Error("Expected non-empty URL list for IPv6")
}
}
// Test NetstackResponse struct creation
func TestNetstackResponse(t *testing.T) {
response := NetstackResponse{
CanHandshake: true,
SentIps: 5,
ReceivedIps: 5,
SentHosts: 5,
ReceivedHosts: 5,
CanResolveDns: true,
DownloadedFile: "test.dat",
DownloadDurationSec: 1,
DownloadError: "",
}
// Test that all fields are set correctly
if !response.CanHandshake {
t.Error("CanHandshake should be true")
}
if response.SentIps != 5 {
t.Error("SentIps should be 5")
}
if response.ReceivedIps != 5 {
t.Error("ReceivedIps should be 5")
}
if response.DownloadedFile != "test.dat" {
t.Error("DownloadedFile should be 'test.dat'")
}
}
// Test NetstackRequestGo struct creation
func TestNetstackRequestGo(t *testing.T) {
request := NetstackRequestGo{
WgIp: "10.0.0.1",
PrivateKey: "test-key",
PublicKey: "test-pub-key",
Endpoint: "1.1.1.1:51820",
Dns: "1.1.1.1",
IpVersion: 4,
PingHosts: []string{"example.com"},
PingIps: []string{"1.1.1.1"},
NumPing: 3,
SendTimeoutSec: 5,
RecvTimeoutSec: 10,
DownloadTimeoutSec: 30,
AwgArgs: "",
}
// Test that all fields are set correctly
if request.WgIp != "10.0.0.1" {
t.Error("WgIp should be '10.0.0.1'")
}
if request.IpVersion != 4 {
t.Error("IpVersion should be 4")
}
if len(request.PingHosts) != 1 {
t.Error("PingHosts should have 1 element")
}
if request.NumPing != 3 {
t.Error("NumPing should be 3")
}
}
// Test the ping function with valid request (will fail due to network setup)
func TestPingFunction(t *testing.T) {
// Create a request with valid IP but will fail due to network setup
req := NetstackRequestGo{
WgIp: "10.0.0.1",
PrivateKey: "0000000000000000000000000000000000000000000000000000000000000000",
PublicKey: "0000000000000000000000000000000000000000000000000000000000000000",
Endpoint: "1.1.1.1:51820",
Dns: "1.1.1.1",
IpVersion: 4,
PingHosts: []string{"example.com"},
PingIps: []string{"1.1.1.1"},
NumPing: 1,
SendTimeoutSec: 1,
RecvTimeoutSec: 1,
DownloadTimeoutSec: 5,
AwgArgs: "",
}
// This should complete (even with network timeouts) and not crash
response, err := ping(req)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
// Check that we got a valid response structure
if response.CanHandshake != true {
t.Error("Expected CanHandshake to be true")
}
// The response should show the retry attempts worked
t.Logf("Response: CanHandshake=%v, SentHosts=%d, ReceivedHosts=%d, DownloadedFile=%s",
response.CanHandshake, response.SentHosts, response.ReceivedHosts, response.DownloadedFile)
}
// Test the SuccessResult and ErrorResult structs
func TestResultStructs(t *testing.T) {
// Test SuccessResult
response := NetstackResponse{
CanHandshake: true,
SentIps: 5,
ReceivedIps: 5,
SentHosts: 5,
ReceivedHosts: 5,
CanResolveDns: true,
DownloadedFile: "test.dat",
DownloadDurationSec: 1,
DownloadError: "",
}
successResult := SuccessResult{Response: response}
if !successResult.Response.CanHandshake {
t.Error("SuccessResult should contain valid response")
}
// Test ErrorResult
errorResult := ErrorResult{Error: "test error"}
if errorResult.Error != "test error" {
t.Error("ErrorResult should contain error message")
}
}
// TestConsecutiveFailureExit validates that the ping loop exits cleanly after consecutive failures
func TestConsecutiveFailureExit(t *testing.T) {
// Create a test request that will trigger consecutive failures
// Using valid hex-encoded keys (32 bytes = 64 hex chars)
req := NetstackRequestGo{
WgIp: "10.0.0.1",
PrivateKey: "0000000000000000000000000000000000000000000000000000000000000000",
PublicKey: "0000000000000000000000000000000000000000000000000000000000000000",
Endpoint: "1.1.1.1:51820",
Dns: "1.1.1.1",
IpVersion: 4,
PingHosts: []string{}, // No hosts to ping
PingIps: []string{"192.0.2.1"}, // RFC 5737 test IP that should fail
NumPing: 5, // Try 5 pings
SendTimeoutSec: 1, // Short timeout to ensure failures
RecvTimeoutSec: 1,
DownloadTimeoutSec: 1,
AwgArgs: "",
}
// Execute the ping function - this should exit cleanly after consecutive failures
response, err := ping(req)
// Verify the response shows we attempted pings but got failures
if response.SentIps == 0 {
t.Error("Should have attempted to send at least one ping")
}
// Verify we received no successful pings (since we're using a test IP)
if response.ReceivedIps > 0 {
t.Logf("Unexpected success: received %d pings", response.ReceivedIps)
}
// The key test: verify we don't hang and return cleanly
if err != nil {
t.Logf("Function returned with error (expected): %v", err)
}
// Verify we didn't send all 5 pings due to early exit
// We should have sent at most 3 pings before hitting consecutive failure limit
if response.SentIps > 3 {
t.Errorf("Should have exited early after consecutive failures, but sent %d pings", response.SentIps)
}
t.Logf("Test completed cleanly: sent %d pings, received %d pings", response.SentIps, response.ReceivedIps)
}