golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/qlog/qlog_test.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build go1.21
     6  
     7  package qlog
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/json"
    12  	"fmt"
    13  	"io"
    14  	"log/slog"
    15  	"reflect"
    16  	"testing"
    17  	"time"
    18  )
    19  
    20  // QLog tests are mostly in the quic package, where we can test event generation
    21  // and serialization together.
    22  
    23  func TestQLogHandlerEvents(t *testing.T) {
    24  	for _, test := range []struct {
    25  		name string
    26  		f    func(*slog.Logger)
    27  		want []map[string]any // events, not counting the trace header
    28  	}{{
    29  		name: "various types",
    30  		f: func(log *slog.Logger) {
    31  			log.Info("message",
    32  				"bool", true,
    33  				"duration", time.Duration(1*time.Second),
    34  				"float", 0.0,
    35  				"int", 0,
    36  				"string", "value",
    37  				"uint", uint64(0),
    38  				slog.Group("group",
    39  					"a", 0,
    40  				),
    41  			)
    42  		},
    43  		want: []map[string]any{{
    44  			"name": "message",
    45  			"data": map[string]any{
    46  				"bool":     true,
    47  				"duration": float64(1000),
    48  				"float":    float64(0.0),
    49  				"int":      float64(0),
    50  				"string":   "value",
    51  				"uint":     float64(0),
    52  				"group": map[string]any{
    53  					"a": float64(0),
    54  				},
    55  			},
    56  		}},
    57  	}, {
    58  		name: "WithAttrs",
    59  		f: func(log *slog.Logger) {
    60  			log = log.With(
    61  				"with_a", "a",
    62  				"with_b", "b",
    63  			)
    64  			log.Info("m1", "field", "1")
    65  			log.Info("m2", "field", "2")
    66  		},
    67  		want: []map[string]any{{
    68  			"name": "m1",
    69  			"data": map[string]any{
    70  				"with_a": "a",
    71  				"with_b": "b",
    72  				"field":  "1",
    73  			},
    74  		}, {
    75  			"name": "m2",
    76  			"data": map[string]any{
    77  				"with_a": "a",
    78  				"with_b": "b",
    79  				"field":  "2",
    80  			},
    81  		}},
    82  	}, {
    83  		name: "WithGroup",
    84  		f: func(log *slog.Logger) {
    85  			log = log.With(
    86  				"with_a", "a",
    87  				"with_b", "b",
    88  			)
    89  			log.Info("m1", "field", "1")
    90  			log.Info("m2", "field", "2")
    91  		},
    92  		want: []map[string]any{{
    93  			"name": "m1",
    94  			"data": map[string]any{
    95  				"with_a": "a",
    96  				"with_b": "b",
    97  				"field":  "1",
    98  			},
    99  		}, {
   100  			"name": "m2",
   101  			"data": map[string]any{
   102  				"with_a": "a",
   103  				"with_b": "b",
   104  				"field":  "2",
   105  			},
   106  		}},
   107  	}} {
   108  		var out bytes.Buffer
   109  		opts := HandlerOptions{
   110  			Level: slog.LevelDebug,
   111  			NewTrace: func(TraceInfo) (io.WriteCloser, error) {
   112  				return nopCloseWriter{&out}, nil
   113  			},
   114  		}
   115  		h, err := newJSONTraceHandler(opts, []slog.Attr{
   116  			slog.String("group_id", "group"),
   117  			slog.Group("vantage_point",
   118  				slog.String("type", "client"),
   119  			),
   120  		})
   121  		if err != nil {
   122  			t.Fatal(err)
   123  		}
   124  		log := slog.New(h)
   125  		test.f(log)
   126  		got := []map[string]any{}
   127  		for i, e := range bytes.Split(out.Bytes(), []byte{0x1e}) {
   128  			// i==0: empty string before the initial record separator
   129  			// i==1: trace header; not part of this test
   130  			if i < 2 {
   131  				continue
   132  			}
   133  			var val map[string]any
   134  			if err := json.Unmarshal(e, &val); err != nil {
   135  				panic(fmt.Errorf("log unmarshal failure: %v\n%q", err, string(e)))
   136  			}
   137  			delete(val, "time")
   138  			got = append(got, val)
   139  		}
   140  		if !reflect.DeepEqual(got, test.want) {
   141  			t.Errorf("event mismatch\ngot:  %v\nwant: %v", got, test.want)
   142  		}
   143  	}
   144  
   145  }
   146  
   147  type nopCloseWriter struct {
   148  	io.Writer
   149  }
   150  
   151  func (nopCloseWriter) Close() error { return nil }