goyave.dev/goyave/v5@v5.0.0-rc9.0.20240517145003-d3f977d0b9f3/database/log_test.go (about)

     1  package database
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"regexp"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	stdslog "log/slog"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  	"goyave.dev/goyave/v5/slog"
    16  )
    17  
    18  func TestLogger(t *testing.T) {
    19  
    20  	t.Run("New", func(t *testing.T) {
    21  		slogger := slog.New(slog.NewHandler(false, &bytes.Buffer{}))
    22  		f := func() *slog.Logger { return slogger }
    23  		l := NewLogger(f)
    24  
    25  		assert.Equal(t, slogger, l.slogger())
    26  		assert.Equal(t, 200*time.Millisecond, l.SlowThreshold)
    27  	})
    28  
    29  	t.Run("LogMode", func(t *testing.T) {
    30  		slogger := slog.New(slog.NewHandler(false, &bytes.Buffer{}))
    31  		l := NewLogger(func() *slog.Logger { return slogger })
    32  		l2 := l.LogMode(0).(*Logger)
    33  		assert.Equal(t, slogger, l2.slogger())
    34  		assert.Equal(t, 200*time.Millisecond, l.SlowThreshold)
    35  	})
    36  
    37  	t.Run("Info", func(t *testing.T) {
    38  
    39  		t.Run("nil_slogger", func(t *testing.T) {
    40  			l := NewLogger(nil)
    41  
    42  			assert.NotPanics(t, func() {
    43  				l.Info(context.Background(), "nil slogger")
    44  			})
    45  		})
    46  
    47  		buf := bytes.NewBuffer(make([]byte, 0, 1024))
    48  		slogger := slog.New(slog.NewHandler(false, buf))
    49  		l := NewLogger(func() *slog.Logger { return slogger })
    50  
    51  		l.Info(context.Background(), "message %d", 1)
    52  
    53  		assert.Regexp(t, regexp.MustCompile(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"INFO","source":{"function":".+","file":".+","line":\d+},"msg":"message 1"}\n`), buf.String())
    54  	})
    55  
    56  	t.Run("Warn", func(t *testing.T) {
    57  
    58  		t.Run("nil_slogger", func(t *testing.T) {
    59  			l := NewLogger(nil)
    60  
    61  			assert.NotPanics(t, func() {
    62  				l.Warn(context.Background(), "nil slogger")
    63  			})
    64  		})
    65  
    66  		buf := bytes.NewBuffer(make([]byte, 0, 1024))
    67  		slogger := slog.New(slog.NewHandler(false, buf))
    68  		l := NewLogger(func() *slog.Logger { return slogger })
    69  
    70  		l.Warn(context.Background(), "message %d", 1)
    71  
    72  		assert.Regexp(t, regexp.MustCompile(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"WARN","source":{"function":".+","file":".+","line":\d+},"msg":"message 1"}\n`), buf.String())
    73  	})
    74  
    75  	t.Run("Error", func(t *testing.T) {
    76  
    77  		t.Run("nil_slogger", func(t *testing.T) {
    78  			l := NewLogger(nil)
    79  
    80  			assert.NotPanics(t, func() {
    81  				l.Error(context.Background(), "nil slogger")
    82  			})
    83  		})
    84  
    85  		buf := bytes.NewBuffer(make([]byte, 0, 1024))
    86  		slogger := slog.New(slog.NewHandler(false, buf))
    87  		l := NewLogger(func() *slog.Logger { return slogger })
    88  
    89  		l.Error(context.Background(), "message %d", 1)
    90  
    91  		assert.Regexp(t, regexp.MustCompile(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"ERROR","source":{"function":".+","file":".+","line":\d+},"msg":"message 1"}\n`), buf.String())
    92  	})
    93  
    94  	t.Run("Trace", func(t *testing.T) {
    95  
    96  		t.Run("nil_slogger", func(t *testing.T) {
    97  			l := NewLogger(nil)
    98  
    99  			assert.NotPanics(t, func() {
   100  				l.Trace(context.Background(), time.Now(), func() (sql string, rowsAffected int64) {
   101  					return "SELECT * FROM some_table", 4
   102  				}, nil)
   103  			})
   104  		})
   105  
   106  		cases := []struct {
   107  			want          *regexp.Regexp
   108  			err           error
   109  			begin         time.Time
   110  			desc          string
   111  			sql           string
   112  			slowThreshold time.Duration
   113  			rowsAffected  int64
   114  			level         stdslog.Level
   115  			wantEmpty     bool
   116  		}{
   117  			{
   118  				desc:          "debug",
   119  				begin:         time.Now(),
   120  				sql:           "SELECT * FROM some_table",
   121  				rowsAffected:  4,
   122  				err:           nil,
   123  				level:         stdslog.LevelDebug,
   124  				slowThreshold: -1,
   125  				want: regexp.MustCompile(
   126  					fmt.Sprintf(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"DEBUG","msg":"%s"}\n`,
   127  						fmt.Sprintf(`%s\[\d+\.\d+ms\] %s\[rows:4\]%s SELECT \* FROM some_table`, regexp.QuoteMeta(strings.ReplaceAll(slog.Yellow, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Blue, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Reset, "\033", `\u001b`))),
   128  					),
   129  				),
   130  			},
   131  			{
   132  				desc:          "debug_slow",
   133  				begin:         time.Now().Add(-time.Second),
   134  				sql:           "SELECT * FROM some_table",
   135  				rowsAffected:  4,
   136  				err:           nil,
   137  				level:         stdslog.LevelDebug,
   138  				slowThreshold: time.Millisecond * 200,
   139  				want: regexp.MustCompile(
   140  					fmt.Sprintf(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"WARN","msg":"%s"}\n`,
   141  						fmt.Sprintf(`SLOW SQL >= 200ms\\n%s\[\d+\.\d+ms\] %s\[rows:4\]%s SELECT \* FROM some_table`, regexp.QuoteMeta(strings.ReplaceAll(slog.Reset+slog.Red, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Blue, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Reset, "\033", `\u001b`))),
   142  					),
   143  				),
   144  			},
   145  			{
   146  				desc:          "debug_slow_disabled_threshold",
   147  				begin:         time.Now().Add(-time.Second),
   148  				sql:           "SELECT * FROM some_table",
   149  				rowsAffected:  4,
   150  				err:           nil,
   151  				level:         stdslog.LevelDebug,
   152  				slowThreshold: 0,
   153  				want: regexp.MustCompile(
   154  					fmt.Sprintf(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"DEBUG","msg":"%s"}\n`,
   155  						fmt.Sprintf(`%s\[\d+\.\d+ms\] %s\[rows:4\]%s SELECT \* FROM some_table`, regexp.QuoteMeta(strings.ReplaceAll(slog.Yellow, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Blue, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Reset, "\033", `\u001b`))),
   156  					),
   157  				),
   158  			},
   159  			{
   160  				desc:          "warn_slow",
   161  				begin:         time.Now().Add(-time.Second),
   162  				sql:           "SELECT * FROM some_table",
   163  				rowsAffected:  4,
   164  				err:           nil,
   165  				level:         stdslog.LevelWarn,
   166  				slowThreshold: time.Millisecond * 200,
   167  				want: regexp.MustCompile(
   168  					fmt.Sprintf(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"WARN","msg":"%s"}\n`,
   169  						fmt.Sprintf(`SLOW SQL >= 200ms\\n%s\[\d+\.\d+ms\] %s\[rows:4\]%s SELECT \* FROM some_table`, regexp.QuoteMeta(strings.ReplaceAll(slog.Reset+slog.Red, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Blue, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Reset, "\033", `\u001b`))),
   170  					),
   171  				),
   172  			},
   173  			{
   174  				desc:          "error_slow",
   175  				begin:         time.Now().Add(-time.Second),
   176  				sql:           "SELECT * FROM some_table",
   177  				rowsAffected:  4,
   178  				err:           nil,
   179  				level:         stdslog.LevelError,
   180  				slowThreshold: -1,
   181  				wantEmpty:     true,
   182  			},
   183  			{
   184  				desc:          "error",
   185  				begin:         time.Now().Add(-time.Second),
   186  				sql:           "SELECT * FROM some_table",
   187  				rowsAffected:  0,
   188  				err:           fmt.Errorf("no such table: some_table"),
   189  				level:         stdslog.LevelDebug,
   190  				slowThreshold: -1,
   191  				want: regexp.MustCompile(
   192  					fmt.Sprintf(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"ERROR","msg":"%s"}\n`,
   193  						fmt.Sprintf(`no such table: some_table\\n%s\[\d+\.\d+ms\] %s\[rows:0\]%s SELECT \* FROM some_table`, regexp.QuoteMeta(strings.ReplaceAll(slog.Reset+slog.Yellow, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Blue, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Reset, "\033", `\u001b`))),
   194  					),
   195  				),
   196  			},
   197  		}
   198  
   199  		for _, c := range cases {
   200  			c := c
   201  			t.Run(c.desc, func(t *testing.T) {
   202  				buf := bytes.NewBuffer(make([]byte, 0, 1024))
   203  				slogger := slog.New(stdslog.NewJSONHandler(buf, &stdslog.HandlerOptions{Level: c.level}))
   204  				l := NewLogger(func() *slog.Logger { return slogger })
   205  
   206  				if c.slowThreshold > -1 {
   207  					l.SlowThreshold = c.slowThreshold
   208  				}
   209  
   210  				l.Trace(context.Background(), c.begin, func() (sql string, rowsAffected int64) { return c.sql, c.rowsAffected }, c.err)
   211  
   212  				if c.wantEmpty {
   213  					assert.Empty(t, buf.String())
   214  				} else {
   215  					assert.Regexp(t, c.want, buf.String())
   216  				}
   217  			})
   218  		}
   219  	})
   220  }