github.com/Mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/stream/logger_test.go (about)

     1  /*
     2  Copyright 2017 Mirantis
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package stream
    18  
    19  import (
    20  	"bufio"
    21  	"encoding/json"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  )
    29  
    30  func setupTmpLogFile() string {
    31  	baseDir, _ := ioutil.TempDir("", "virtlet-log")
    32  	outputFile := filepath.Join(baseDir, "output.log")
    33  	os.Mkdir(baseDir, 0777)
    34  	return outputFile
    35  }
    36  
    37  func verifyJSONLines(t *testing.T, filePath string, lines []map[string]interface{}) {
    38  	if _, err := os.Stat(filePath); os.IsNotExist(err) {
    39  		t.Errorf("output file should exist, but does not")
    40  	}
    41  
    42  	f, err := os.Open(filePath)
    43  	if err != nil {
    44  		t.Errorf("failed to open file: %s", filePath)
    45  	}
    46  	defer f.Close()
    47  
    48  	scanner := bufio.NewScanner(f)
    49  	for n := 0; scanner.Scan(); n++ {
    50  		l := scanner.Text()
    51  		if n >= len(lines) {
    52  			t.Errorf("excess line in the log: %q", l)
    53  			continue
    54  		}
    55  		var m map[string]interface{}
    56  		if err := json.Unmarshal([]byte(l), &m); err != nil {
    57  			t.Errorf("failed to unmarshal log line %q: %v", l, err)
    58  			continue
    59  		}
    60  		timeStr, ok := m["time"].(string)
    61  		if !ok {
    62  			t.Errorf("bad/absent time in the log line %q", l)
    63  			continue
    64  		}
    65  		logTime, err := time.Parse(time.RFC3339, timeStr)
    66  		if err != nil {
    67  			t.Errorf("failed to parse log time %q: %v", m["time"], err)
    68  			continue
    69  		}
    70  		timeDiff := time.Now().Sub(logTime)
    71  		if timeDiff < 0 || timeDiff > 10*time.Minute {
    72  			t.Errorf("log time too far from now: %v", m["time"])
    73  			continue
    74  		}
    75  		for k, v := range lines[n] {
    76  			if m[k] != v {
    77  				t.Errorf("bad %q value in log line %q: got %v, expected %v", k, l, m[k], v)
    78  			}
    79  		}
    80  	}
    81  	if err := scanner.Err(); err != nil {
    82  		t.Errorf("error reading the output file: %v", err)
    83  	}
    84  }
    85  
    86  func TestLoggingInNewLogWritter(t *testing.T) {
    87  	var wg sync.WaitGroup
    88  
    89  	cases := []struct {
    90  		name       string
    91  		outputFile string
    92  		c          chan []byte
    93  		lines      [][]byte
    94  		jsonLines  []map[string]interface{}
    95  	}{
    96  		{
    97  			name:       "One line",
    98  			outputFile: setupTmpLogFile(),
    99  			c:          make(chan []byte),
   100  			lines:      [][]byte{[]byte("test")},
   101  			jsonLines: []map[string]interface{}{
   102  				{
   103  					"stream": "stdout",
   104  					"log":    "test\n",
   105  				},
   106  			},
   107  		},
   108  		{
   109  			name:       "Many lines in one message",
   110  			outputFile: setupTmpLogFile(),
   111  			c:          make(chan []byte),
   112  			lines:      [][]byte{[]byte("test\ntest2\n")},
   113  			jsonLines: []map[string]interface{}{
   114  				{
   115  					"stream": "stdout",
   116  					"log":    "test\n",
   117  				},
   118  				{
   119  					"stream": "stdout",
   120  					"log":    "test2\n",
   121  				},
   122  			},
   123  		},
   124  		{
   125  			name:       "Many messages",
   126  			outputFile: setupTmpLogFile(),
   127  			c:          make(chan []byte),
   128  			lines:      [][]byte{[]byte("test\n"), []byte("test2\n")},
   129  			jsonLines: []map[string]interface{}{
   130  				{
   131  					"stream": "stdout",
   132  					"log":    "test\n",
   133  				},
   134  				{
   135  					"stream": "stdout",
   136  					"log":    "test2\n",
   137  				},
   138  			},
   139  		},
   140  		{
   141  			name:       "No messages",
   142  			outputFile: setupTmpLogFile(),
   143  			c:          make(chan []byte),
   144  			lines:      [][]byte{},
   145  			jsonLines:  []map[string]interface{}{},
   146  		},
   147  	}
   148  
   149  	for _, test := range cases {
   150  		t.Logf("Running `%s` test", test.name)
   151  		defer os.RemoveAll(test.outputFile)
   152  		wg.Add(1)
   153  		go NewLogWriter(test.c, test.outputFile, &wg)
   154  		for _, line := range test.lines {
   155  			test.c <- line
   156  		}
   157  		close(test.c)
   158  		wg.Wait()
   159  
   160  		verifyJSONLines(t, test.outputFile, test.jsonLines)
   161  	}
   162  }