github.com/goravel/framework@v1.13.9/log/logrus_writer_test.go (about)

     1  package log
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	nethttp "net/http"
     8  	"os"
     9  	"os/exec"
    10  	"reflect"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  
    15  	configmock "github.com/goravel/framework/contracts/config/mocks"
    16  	"github.com/goravel/framework/contracts/filesystem"
    17  	contractshttp "github.com/goravel/framework/contracts/http"
    18  	"github.com/goravel/framework/contracts/validation"
    19  	"github.com/goravel/framework/support/carbon"
    20  	"github.com/goravel/framework/support/file"
    21  )
    22  
    23  var singleLog = "storage/logs/goravel.log"
    24  var dailyLog = fmt.Sprintf("storage/logs/goravel-%s.log", carbon.Now().ToDateString())
    25  
    26  func TestLogrus(t *testing.T) {
    27  	var (
    28  		mockConfig *configmock.Config
    29  		log        *Application
    30  	)
    31  
    32  	beforeEach := func() {
    33  		mockConfig = initMockConfig()
    34  	}
    35  
    36  	tests := []struct {
    37  		name   string
    38  		setup  func()
    39  		assert func()
    40  	}{
    41  		{
    42  			name: "WithContext",
    43  			setup: func() {
    44  				mockConfig.On("GetString", "logging.channels.daily.level").Return("debug").Once()
    45  				mockConfig.On("GetString", "logging.channels.single.level").Return("debug").Once()
    46  
    47  				log = NewApplication(mockConfig)
    48  			},
    49  			assert: func() {
    50  				writer := log.WithContext(context.Background())
    51  				assert.Equal(t, reflect.TypeOf(writer).String(), reflect.TypeOf(&Writer{}).String())
    52  			},
    53  		},
    54  		{
    55  			name: "Debug",
    56  			setup: func() {
    57  				mockDriverConfig(mockConfig)
    58  
    59  				log = NewApplication(mockConfig)
    60  				log.Debug("Debug Goravel")
    61  			},
    62  			assert: func() {
    63  				assert.True(t, file.Contain(singleLog, "test.debug: Debug Goravel"))
    64  				assert.True(t, file.Contain(dailyLog, "test.debug: Debug Goravel"))
    65  			},
    66  		},
    67  		{
    68  			name: "No Debug",
    69  			setup: func() {
    70  				mockConfig.On("GetString", "logging.channels.daily.level").Return("info").Once()
    71  				mockConfig.On("GetString", "logging.channels.single.level").Return("info").Once()
    72  				mockConfig.On("GetString", "app.timezone").Return("UTC").Once()
    73  				mockConfig.On("GetString", "app.env").Return("test").Once()
    74  				log = NewApplication(mockConfig)
    75  				log.Debug("No Debug Goravel")
    76  			},
    77  			assert: func() {
    78  				assert.False(t, file.Contain(singleLog, "test.debug: No Debug Goravel"))
    79  				assert.False(t, file.Contain(dailyLog, "test.debug: No Debug Goravel"))
    80  			},
    81  		},
    82  		{
    83  			name: "Debugf",
    84  			setup: func() {
    85  				mockDriverConfig(mockConfig)
    86  
    87  				log = NewApplication(mockConfig)
    88  				log.Debugf("Goravel: %s", "World")
    89  			},
    90  			assert: func() {
    91  				assert.True(t, file.Contain(singleLog, "test.debug: Goravel: World"))
    92  				assert.True(t, file.Contain(dailyLog, "test.debug: Goravel: World"))
    93  			},
    94  		},
    95  		{
    96  			name: "Info",
    97  			setup: func() {
    98  				mockDriverConfig(mockConfig)
    99  
   100  				log = NewApplication(mockConfig)
   101  				log.Info("Goravel")
   102  			},
   103  			assert: func() {
   104  				assert.True(t, file.Contain(singleLog, "test.info: Goravel"))
   105  				assert.True(t, file.Contain(dailyLog, "test.info: Goravel"))
   106  			},
   107  		},
   108  		{
   109  			name: "Infof",
   110  			setup: func() {
   111  				mockDriverConfig(mockConfig)
   112  
   113  				log = NewApplication(mockConfig)
   114  				log.Infof("Goravel: %s", "World")
   115  			},
   116  			assert: func() {
   117  				assert.True(t, file.Contain(singleLog, "test.info: Goravel: World"))
   118  				assert.True(t, file.Contain(dailyLog, "test.info: Goravel: World"))
   119  			},
   120  		},
   121  		{
   122  			name: "Warning",
   123  			setup: func() {
   124  				mockDriverConfig(mockConfig)
   125  
   126  				log = NewApplication(mockConfig)
   127  				log.Warning("Goravel")
   128  			},
   129  			assert: func() {
   130  				assert.True(t, file.Contain(singleLog, "test.warning: Goravel"))
   131  				assert.True(t, file.Contain(dailyLog, "test.warning: Goravel"))
   132  			},
   133  		},
   134  		{
   135  			name: "Warningf",
   136  			setup: func() {
   137  				mockDriverConfig(mockConfig)
   138  
   139  				log = NewApplication(mockConfig)
   140  				log.Warningf("Goravel: %s", "World")
   141  			},
   142  			assert: func() {
   143  				assert.True(t, file.Contain(singleLog, "test.warning: Goravel: World"))
   144  				assert.True(t, file.Contain(dailyLog, "test.warning: Goravel: World"))
   145  			},
   146  		},
   147  		{
   148  			name: "Error",
   149  			setup: func() {
   150  				mockDriverConfig(mockConfig)
   151  
   152  				log = NewApplication(mockConfig)
   153  				log.Error("Goravel")
   154  			},
   155  			assert: func() {
   156  				assert.True(t, file.Contain(singleLog, "test.error: Goravel"))
   157  				assert.True(t, file.Contain(dailyLog, "test.error: Goravel"))
   158  			},
   159  		},
   160  		{
   161  			name: "Errorf",
   162  			setup: func() {
   163  				mockDriverConfig(mockConfig)
   164  
   165  				log = NewApplication(mockConfig)
   166  				log.Errorf("Goravel: %s", "World")
   167  			},
   168  			assert: func() {
   169  				assert.True(t, file.Contain(singleLog, "test.error: Goravel: World"))
   170  				assert.True(t, file.Contain(dailyLog, "test.error: Goravel: World"))
   171  			},
   172  		},
   173  		{
   174  			name: "Panic",
   175  			setup: func() {
   176  				mockDriverConfig(mockConfig)
   177  
   178  				log = NewApplication(mockConfig)
   179  			},
   180  			assert: func() {
   181  				assert.Panics(t, func() {
   182  					log.Panic("Goravel")
   183  				})
   184  				assert.True(t, file.Contain(singleLog, "test.panic: Goravel"))
   185  				assert.True(t, file.Contain(dailyLog, "test.panic: Goravel"))
   186  			},
   187  		},
   188  		{
   189  			name: "Panicf",
   190  			setup: func() {
   191  				mockDriverConfig(mockConfig)
   192  
   193  				log = NewApplication(mockConfig)
   194  			},
   195  			assert: func() {
   196  				assert.Panics(t, func() {
   197  					log.Panicf("Goravel: %s", "World")
   198  				})
   199  				assert.True(t, file.Contain(singleLog, "test.panic: Goravel: World"))
   200  				assert.True(t, file.Contain(dailyLog, "test.panic: Goravel: World"))
   201  			},
   202  		},
   203  		{
   204  			name: "Code",
   205  			setup: func() {
   206  				mockDriverConfig(mockConfig)
   207  
   208  				log = NewApplication(mockConfig)
   209  				log.Code("code").Info("Goravel")
   210  			},
   211  			assert: func() {
   212  				assert.True(t, file.Contain(singleLog, "test.info: Goravel\ncode: \"code\""))
   213  				assert.True(t, file.Contain(dailyLog, "test.info: Goravel\ncode: \"code\""))
   214  			},
   215  		},
   216  		{
   217  			name: "Hint",
   218  			setup: func() {
   219  				mockDriverConfig(mockConfig)
   220  
   221  				log = NewApplication(mockConfig)
   222  				log.Hint("hint").Info("Goravel")
   223  			},
   224  			assert: func() {
   225  				assert.True(t, file.Contain(singleLog, "test.info: Goravel\nhint: \"hint\""))
   226  				assert.True(t, file.Contain(dailyLog, "test.info: Goravel\nhint: \"hint\""))
   227  			},
   228  		},
   229  		{
   230  			name: "In",
   231  			setup: func() {
   232  				mockDriverConfig(mockConfig)
   233  
   234  				log = NewApplication(mockConfig)
   235  				log.In("domain").Info("Goravel")
   236  			},
   237  			assert: func() {
   238  				assert.True(t, file.Contain(singleLog, "test.info: Goravel\ndomain: \"domain\""))
   239  				assert.True(t, file.Contain(dailyLog, "test.info: Goravel\ndomain: \"domain\""))
   240  			},
   241  		},
   242  		{
   243  			name: "Owner",
   244  			setup: func() {
   245  				mockDriverConfig(mockConfig)
   246  
   247  				log = NewApplication(mockConfig)
   248  				log.Owner("team@goravel.dev").Info("Goravel")
   249  			},
   250  			assert: func() {
   251  				assert.True(t, file.Contain(singleLog, "test.info: Goravel\nowner: \"team@goravel.dev\""))
   252  				assert.True(t, file.Contain(dailyLog, "test.info: Goravel\nowner: \"team@goravel.dev\""))
   253  			},
   254  		},
   255  		{
   256  			name: "Request",
   257  			setup: func() {
   258  				mockDriverConfig(mockConfig)
   259  
   260  				log = NewApplication(mockConfig)
   261  				log.Request(&TestRequest{}).Info("Goravel")
   262  			},
   263  			assert: func() {
   264  				expectedParts := []string{
   265  					`test.info: Goravel`,
   266  					`request: {`,
   267  					`"method":"GET`,
   268  					`"uri":"http://localhost:3000/"`,
   269  					`"Sec-Fetch-User":["?1"]`,
   270  					`"Host":["localhost:3000"]`,
   271  					`"body":{`,
   272  					`"key1":"value1"`,
   273  					`"key2":"value2"`,
   274  				}
   275  
   276  				for _, part := range expectedParts {
   277  					assert.True(t, file.Contain(singleLog, part), part)
   278  					assert.True(t, file.Contain(dailyLog, part), part)
   279  				}
   280  			},
   281  		},
   282  		{
   283  			name: "Response",
   284  			setup: func() {
   285  				mockDriverConfig(mockConfig)
   286  
   287  				log = NewApplication(mockConfig)
   288  				log.Response(&TestResponse{}).Info("Goravel")
   289  			},
   290  			assert: func() {
   291  				expectedParts := []string{
   292  					`test.info: Goravel`,
   293  					`response: {`,
   294  					`"status":200`,
   295  					`"header":{"Content-Type":["text/plain; charset=utf-8"]}`,
   296  					`"body":{}`,
   297  					`"size":4`,
   298  				}
   299  
   300  				for _, part := range expectedParts {
   301  					assert.True(t, file.Contain(singleLog, part))
   302  					assert.True(t, file.Contain(dailyLog, part))
   303  				}
   304  			},
   305  		},
   306  		{
   307  			name: "Tags",
   308  			setup: func() {
   309  				mockDriverConfig(mockConfig)
   310  
   311  				log = NewApplication(mockConfig)
   312  				log.Tags("tag").Info("Goravel")
   313  			},
   314  			assert: func() {
   315  				assert.True(t, file.Contain(singleLog, "test.info: Goravel\ntags: [\"tag\"]"))
   316  				assert.True(t, file.Contain(dailyLog, "test.info: Goravel\ntags: [\"tag\"]"))
   317  			},
   318  		},
   319  		{
   320  			name: "User",
   321  			setup: func() {
   322  				mockDriverConfig(mockConfig)
   323  
   324  				log = NewApplication(mockConfig)
   325  				log.User(map[string]any{"name": "kkumar-gcc"}).Info("Goravel")
   326  			},
   327  			assert: func() {
   328  				assert.True(t, file.Contain(singleLog, "test.info: Goravel\nuser: {\"name\":\"kkumar-gcc\"}"))
   329  				assert.True(t, file.Contain(dailyLog, "test.info: Goravel\nuser: {\"name\":\"kkumar-gcc\"}"))
   330  			},
   331  		},
   332  		{
   333  			name: "With",
   334  			setup: func() {
   335  				mockDriverConfig(mockConfig)
   336  
   337  				log = NewApplication(mockConfig)
   338  				log.With(map[string]any{"key": "value"}).Info("Goravel")
   339  			},
   340  			assert: func() {
   341  				assert.True(t, file.Contain(singleLog, "test.info: Goravel\ncontext: {\"key\":\"value\"}"))
   342  				assert.True(t, file.Contain(dailyLog, "test.info: Goravel\ncontext: {\"key\":\"value\"}"))
   343  			},
   344  		},
   345  	}
   346  
   347  	for _, test := range tests {
   348  		t.Run(test.name, func(t *testing.T) {
   349  			beforeEach()
   350  			test.setup()
   351  			test.assert()
   352  
   353  			mockConfig.AssertExpectations(t)
   354  		})
   355  	}
   356  
   357  	_ = file.Remove("storage")
   358  }
   359  
   360  func TestLogrus_Fatal(t *testing.T) {
   361  	mockConfig := initMockConfig()
   362  	mockDriverConfig(mockConfig)
   363  	log := NewApplication(mockConfig)
   364  
   365  	if os.Getenv("FATAL") == "1" {
   366  		log.Fatal("Goravel")
   367  		return
   368  	}
   369  	cmd := exec.Command(os.Args[0], "-test.run=TestLogrus_Fatal")
   370  	cmd.Env = append(os.Environ(), "FATAL=1")
   371  	err := cmd.Run()
   372  
   373  	assert.EqualError(t, err, "exit status 1")
   374  	assert.True(t, file.Contain(singleLog, "test.fatal: Goravel"))
   375  	assert.True(t, file.Contain(dailyLog, "test.fatal: Goravel"))
   376  
   377  	_ = file.Remove("storage")
   378  }
   379  
   380  func TestLogrus_Fatalf(t *testing.T) {
   381  	mockConfig := initMockConfig()
   382  	mockDriverConfig(mockConfig)
   383  	log := NewApplication(mockConfig)
   384  
   385  	if os.Getenv("FATAL") == "1" {
   386  		log.Fatalf("Goravel")
   387  		return
   388  	}
   389  	cmd := exec.Command(os.Args[0], "-test.run=TestLogrus_Fatal")
   390  	cmd.Env = append(os.Environ(), "FATAL=1")
   391  	err := cmd.Run()
   392  
   393  	assert.EqualError(t, err, "exit status 1")
   394  	assert.True(t, file.Contain(singleLog, "test.fatal: Goravel"))
   395  	assert.True(t, file.Contain(dailyLog, "test.fatal: Goravel"))
   396  
   397  	_ = file.Remove("storage")
   398  }
   399  
   400  func initMockConfig() *configmock.Config {
   401  	mockConfig := &configmock.Config{}
   402  
   403  	mockConfig.On("GetString", "logging.default").Return("stack").Once()
   404  	mockConfig.On("GetString", "logging.channels.stack.driver").Return("stack").Once()
   405  	mockConfig.On("Get", "logging.channels.stack.channels").Return([]string{"single", "daily"}).Once()
   406  	mockConfig.On("GetString", "logging.channels.daily.driver").Return("daily").Once()
   407  	mockConfig.On("GetString", "logging.channels.daily.path").Return(singleLog).Once()
   408  	mockConfig.On("GetInt", "logging.channels.daily.days").Return(7).Once()
   409  	mockConfig.On("GetBool", "logging.channels.daily.print").Return(false).Once()
   410  	mockConfig.On("GetString", "logging.channels.single.driver").Return("single").Once()
   411  	mockConfig.On("GetString", "logging.channels.single.path").Return(singleLog).Once()
   412  	mockConfig.On("GetBool", "logging.channels.single.print").Return(false).Once()
   413  
   414  	return mockConfig
   415  }
   416  
   417  func mockDriverConfig(mockConfig *configmock.Config) {
   418  	mockConfig.On("GetString", "logging.channels.daily.level").Return("debug").Once()
   419  	mockConfig.On("GetString", "logging.channels.single.level").Return("debug").Once()
   420  	mockConfig.On("GetString", "app.timezone").Return("UTC")
   421  	mockConfig.On("GetString", "app.env").Return("test")
   422  }
   423  
   424  type TestRequest struct{}
   425  
   426  func (r *TestRequest) Header(key string, defaultValue ...string) string {
   427  	panic("do not need to implement it")
   428  }
   429  
   430  func (r *TestRequest) Headers() nethttp.Header {
   431  	return nethttp.Header{
   432  		"Sec-Fetch-User": []string{"?1"},
   433  		"Host":           []string{"localhost:3000"},
   434  	}
   435  }
   436  
   437  func (r *TestRequest) Method() string {
   438  	return "GET"
   439  }
   440  
   441  func (r *TestRequest) Path() string {
   442  	return "/test"
   443  }
   444  
   445  func (r *TestRequest) Url() string {
   446  	panic("do not need to implement it")
   447  }
   448  
   449  func (r *TestRequest) FullUrl() string {
   450  	return "http://localhost:3000/"
   451  }
   452  
   453  func (r *TestRequest) Ip() string {
   454  	panic("do not need to implement it")
   455  }
   456  
   457  func (r *TestRequest) Host() string {
   458  	panic("do not need to implement it")
   459  }
   460  
   461  func (r *TestRequest) All() map[string]any {
   462  	return map[string]interface{}{
   463  		"key1": "value1",
   464  		"key2": "value2",
   465  	}
   466  }
   467  
   468  func (r *TestRequest) Bind(obj any) error {
   469  	panic("do not need to implement it")
   470  }
   471  
   472  func (r *TestRequest) Route(key string) string {
   473  	panic("do not need to implement it")
   474  }
   475  
   476  func (r *TestRequest) RouteInt(key string) int {
   477  	panic("do not need to implement it")
   478  }
   479  
   480  func (r *TestRequest) RouteInt64(key string) int64 {
   481  	panic("do not need to implement it")
   482  }
   483  
   484  func (r *TestRequest) Query(key string, defaultValue ...string) string {
   485  	panic("do not need to implement it")
   486  }
   487  
   488  func (r *TestRequest) QueryInt(key string, defaultValue ...int) int {
   489  	panic("do not need to implement it")
   490  }
   491  
   492  func (r *TestRequest) QueryInt64(key string, defaultValue ...int64) int64 {
   493  	panic("do not need to implement it")
   494  }
   495  
   496  func (r *TestRequest) QueryBool(key string, defaultValue ...bool) bool {
   497  	panic("do not need to implement it")
   498  }
   499  
   500  func (r *TestRequest) QueryArray(key string) []string {
   501  	panic("do not need to implement it")
   502  }
   503  
   504  func (r *TestRequest) QueryMap(key string) map[string]string {
   505  	panic("do not need to implement it")
   506  }
   507  
   508  func (r *TestRequest) Queries() map[string]string {
   509  	panic("do not need to implement it")
   510  }
   511  
   512  func (r *TestRequest) Form(key string, defaultValue ...string) string {
   513  	panic("do not need to implement it")
   514  }
   515  
   516  func (r *TestRequest) Json(key string, defaultValue ...string) string {
   517  	panic("do not need to implement it")
   518  }
   519  
   520  func (r *TestRequest) Input(key string, defaultValue ...string) string {
   521  	panic("do not need to implement it")
   522  }
   523  
   524  func (r *TestRequest) InputArray(key string, defaultValue ...[]string) []string {
   525  	panic("do not need to implement it")
   526  }
   527  
   528  func (r *TestRequest) InputMap(key string, defaultValue ...map[string]string) map[string]string {
   529  	panic("do not need to implement it")
   530  }
   531  
   532  func (r *TestRequest) InputInt(key string, defaultValue ...int) int {
   533  	panic("do not need to implement it")
   534  }
   535  
   536  func (r *TestRequest) InputInt64(key string, defaultValue ...int64) int64 {
   537  	panic("do not need to implement it")
   538  }
   539  
   540  func (r *TestRequest) InputBool(key string, defaultValue ...bool) bool {
   541  	panic("do not need to implement it")
   542  }
   543  
   544  func (r *TestRequest) File(name string) (filesystem.File, error) {
   545  	panic("do not need to implement it")
   546  }
   547  
   548  func (r *TestRequest) AbortWithStatus(code int) {}
   549  
   550  func (r *TestRequest) AbortWithStatusJson(code int, jsonObj any) {
   551  	panic("do not need to implement it")
   552  }
   553  
   554  func (r *TestRequest) Next() {}
   555  
   556  func (r *TestRequest) Origin() *nethttp.Request {
   557  	panic("do not need to implement it")
   558  }
   559  
   560  func (r *TestRequest) Validate(rules map[string]string, options ...validation.Option) (validation.Validator, error) {
   561  	panic("do not need to implement it")
   562  }
   563  
   564  func (r *TestRequest) ValidateRequest(request contractshttp.FormRequest) (validation.Errors, error) {
   565  	panic("do not need to implement it")
   566  }
   567  
   568  type TestResponse struct {
   569  }
   570  
   571  func (r *TestResponse) Data(code int, contentType string, data []byte) contractshttp.Response {
   572  	panic("do not need to implement it")
   573  }
   574  
   575  func (r *TestResponse) Download(filepath, filename string) contractshttp.Response {
   576  	panic("do not need to implement it")
   577  }
   578  
   579  func (r *TestResponse) File(filepath string) contractshttp.Response {
   580  	panic("do not need to implement it")
   581  }
   582  
   583  func (r *TestResponse) Header(key, value string) contractshttp.ContextResponse {
   584  	panic("do not need to implement it")
   585  }
   586  
   587  func (r *TestResponse) Json(code int, obj any) contractshttp.Response {
   588  	panic("do not need to implement it")
   589  }
   590  
   591  func (r *TestResponse) Origin() contractshttp.ResponseOrigin {
   592  	return &TestResponseOrigin{ctx: r}
   593  }
   594  
   595  func (r *TestResponse) Redirect(code int, location string) contractshttp.Response {
   596  	panic("do not need to implement it")
   597  }
   598  
   599  func (r *TestResponse) String(code int, format string, values ...any) contractshttp.Response {
   600  	panic("do not need to implement it")
   601  }
   602  
   603  func (r *TestResponse) Success() contractshttp.ResponseSuccess {
   604  	panic("do not need to implement it")
   605  }
   606  
   607  func (r *TestResponse) Status(code int) contractshttp.ResponseStatus {
   608  	panic("do not need to implement it")
   609  }
   610  
   611  func (r *TestResponse) Writer() nethttp.ResponseWriter {
   612  	panic("do not need to implement it")
   613  }
   614  
   615  func (r *TestResponse) Flush() {
   616  	panic("do not need to implement it")
   617  }
   618  
   619  func (r *TestResponse) View() contractshttp.ResponseView {
   620  	panic("do not need to implement it")
   621  }
   622  
   623  type TestResponseOrigin struct {
   624  	ctx *TestResponse
   625  }
   626  
   627  func (r *TestResponseOrigin) Body() *bytes.Buffer {
   628  	return bytes.NewBuffer([]byte("body"))
   629  }
   630  
   631  func (r *TestResponseOrigin) Header() nethttp.Header {
   632  	return nethttp.Header{
   633  		"Content-Type": []string{"text/plain; charset=utf-8"},
   634  	}
   635  }
   636  
   637  func (r *TestResponseOrigin) Size() int {
   638  	return r.Body().Len()
   639  }
   640  
   641  func (r *TestResponseOrigin) Status() int {
   642  	return 200
   643  }