github.com/anacrolix/torrent@v1.61.0/logging_test.go (about)

     1  package torrent
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"log/slog"
     8  	"math"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/anacrolix/torrent/tracker"
    13  	"github.com/go-quicktest/qt"
    14  
    15  	"github.com/anacrolix/torrent/tracker/shared"
    16  )
    17  
    18  type testHandler struct{}
    19  
    20  func TestLazyLogValuer(t *testing.T) {
    21  	// This is a test to ensure that the lazyLogValuer type implements the slog.LogValuer interface.
    22  	// The test is intentionally left empty because the implementation is already verified by the
    23  	// compiler.
    24  
    25  	//h := testHandler{}
    26  	//l := slog.New(h)
    27  	var buf bytes.Buffer
    28  	h := slog.NewTextHandler(&buf, &slog.HandlerOptions{
    29  		Level: slog.Level(math.MinInt),
    30  	})
    31  	l := slog.New(h)
    32  	cl := newTestingClient(t)
    33  	tor := cl.newTorrentForTesting()
    34  	l2 := tor.withSlogger(l)
    35  	l2.Debug("hi")
    36  	t.Log(buf.String())
    37  }
    38  
    39  func TestAnnounceEventJSONHandlerLogsAsString(t *testing.T) {
    40  	// Test that when logging a struct containing an AnnounceEvent using a JSONHandler,
    41  	// the AnnounceEvent is serialized as a string and not an integer.
    42  
    43  	type TestStruct struct {
    44  		Event shared.AnnounceEvent `json:"event"`
    45  	}
    46  
    47  	var buf bytes.Buffer
    48  	h := slog.NewJSONHandler(&buf, &slog.HandlerOptions{
    49  		Level: slog.Level(math.MinInt),
    50  	})
    51  	l := slog.New(h)
    52  
    53  	testData := TestStruct{
    54  		Event: shared.Started,
    55  	}
    56  
    57  	l.Info("test log entry", "data", testData)
    58  
    59  	logOutput := buf.String()
    60  	t.Logf("Log output: %s", logOutput)
    61  
    62  	// Parse the JSON to verify the event is logged as a string
    63  	var logEntry map[string]interface{}
    64  	err := json.Unmarshal([]byte(logOutput), &logEntry)
    65  	qt.Assert(t, qt.IsNil(err))
    66  
    67  	// Extract the data field
    68  	data, ok := logEntry["data"].(map[string]interface{})
    69  	qt.Assert(t, qt.IsTrue(ok), qt.Commentf("Expected 'data' field to be an object, got %T", logEntry["data"]))
    70  
    71  	// Check that the event field is a string, not a number
    72  	event, ok := data["event"].(string)
    73  	qt.Assert(t, qt.IsTrue(ok), qt.Commentf("Expected 'event' field to be a string, got %T with value %v", data["event"], data["event"]))
    74  
    75  	// Verify the string value matches the expected string representation
    76  	expectedEventString := shared.Started.String()
    77  	qt.Assert(t, qt.Equals(event, expectedEventString))
    78  
    79  	// Verify it contains the expected string representation
    80  	expectedJSON := `"event":"started"`
    81  	qt.Assert(t, qt.IsTrue(strings.Contains(logOutput, expectedJSON)), qt.Commentf("Expected log output to contain %s, but it didn't. Full output: %s", expectedJSON, logOutput))
    82  
    83  	// Additional verification: ensure it's not serialized as an integer
    84  	qt.Assert(t, qt.IsFalse(strings.Contains(logOutput, `"event":2`)), qt.Commentf("Event was serialized as integer (2) instead of string"))
    85  	qt.Assert(t, qt.IsFalse(strings.Contains(logOutput, `"event": 2`)), qt.Commentf("Event was serialized as integer (2) instead of string"))
    86  }
    87  
    88  func TestSlogLoggerWithSameNameCreatesDuplicateGroups(t *testing.T) {
    89  	var buf bytes.Buffer
    90  	h := slog.NewJSONHandler(&buf, &slog.HandlerOptions{
    91  		Level: slog.Level(math.MinInt),
    92  	})
    93  	l := slog.New(h)
    94  
    95  	// Create logger with initial group
    96  	l1 := l.With(slog.Group("test", "key1", "value1", "key2", "value2"))
    97  
    98  	// Add another group with same name - this creates duplicate entries
    99  	l2 := l1.With(slog.Group("test", "key3", "value3"))
   100  
   101  	l2.Info("test message")
   102  
   103  	logOutput := buf.String()
   104  	t.Logf("Log output: %s", logOutput)
   105  
   106  	// Verify both groups appear in the output (slog doesn't replace, it creates duplicates)
   107  	qt.Assert(t, qt.IsTrue(strings.Contains(logOutput, `"test":{"key1":"value1","key2":"value2"}`)))
   108  	qt.Assert(t, qt.IsTrue(strings.Contains(logOutput, `"test":{"key3":"value3"}`)))
   109  
   110  	// When parsing JSON with duplicate keys, the last one wins
   111  	var logEntry map[string]interface{}
   112  	err := json.Unmarshal([]byte(logOutput), &logEntry)
   113  	qt.Assert(t, qt.IsNil(err))
   114  
   115  	testGroup, ok := logEntry["test"].(map[string]interface{})
   116  	qt.Assert(t, qt.IsTrue(ok), qt.Commentf("Expected 'test' field to be an object"))
   117  
   118  	// The parsed JSON will have only the last occurrence due to JSON duplicate key handling
   119  	qt.Assert(t, qt.Equals(testGroup["key3"], "value3"))
   120  	_, hasKey1 := testGroup["key1"]
   121  	qt.Assert(t, qt.IsFalse(hasKey1), qt.Commentf("Due to JSON duplicate key handling, only the last group should be present"))
   122  }
   123  
   124  func TestSlogAnnounceRequest(t *testing.T) {
   125  	var buf bytes.Buffer
   126  	h := slog.NewJSONHandler(&buf, &slog.HandlerOptions{
   127  		AddSource: false,
   128  		Level:     nil,
   129  		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
   130  			fmt.Println(groups, a)
   131  			a.Value.Any()
   132  			return a
   133  		},
   134  	})
   135  	l := slog.New(h)
   136  	l.Info("test", "req", tracker.AnnounceRequest{})
   137  	t.Log(buf.String())
   138  }