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 }