github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/Server/logging/analysis/analysis_test.go (about)

     1  /*
     2   * Copyright (c) 2018, Psiphon Inc.
     3   * All rights reserved.
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License as published by
     7   * the Free Software Foundation, either version 3 of the License, or
     8   * (at your option) any later version.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package analysis
    21  
    22  import (
    23  	"reflect"
    24  	"testing"
    25  )
    26  
    27  func TestAllLogModelsAndSorting(t *testing.T) {
    28  	l := parseLogsAndTestExpectations(logLinesWithExpectations(), t)
    29  
    30  	logs, _ := l.SortLogModels(true, true, true)
    31  	var prevLogCount uint
    32  
    33  	for _, x := range logs {
    34  		var count uint
    35  
    36  		switch v := x.(type) {
    37  		case MessageLogModelStats:
    38  			count = v.Count
    39  		case MetricsLogModelStats:
    40  			count = v.Count
    41  		case UnknownLogModelStats:
    42  			count = v.Count
    43  		default:
    44  			t.Errorf("Encountered unexpected struct of type %v\n", reflect.TypeOf(v))
    45  		}
    46  
    47  		if prevLogCount != 0 && prevLogCount > count {
    48  			t.Errorf("Expected list to be sorted in ascending order")
    49  		}
    50  		prevLogCount = count
    51  	}
    52  }
    53  
    54  func TestMessageLogsWithErrorAndContext(t *testing.T) {
    55  	logs := []LogLineWithExpectation{
    56  		// The following messages should parse into 4 distinct log models
    57  		messageLogExpectation(`{"msg":"a", "level":"info"}`),
    58  		messageLogExpectation(`{"msg":"a", "level":"info"}`),
    59  		messageLogExpectation(`{"msg":"a", "level":"info", "error": "b"}`),
    60  		messageLogExpectation(`{"msg":"a", "level":"info", "error": "b"}`),
    61  		messageLogExpectation(`{"msg":"a", "level":"info", "context": "c"}`),
    62  		messageLogExpectation(`{"msg":"a", "level":"info", "context": "c"}`),
    63  		messageLogExpectation(`{"msg":"a", "level":"info", "error": "b", "context": "c"}`),
    64  		messageLogExpectation(`{"msg":"a", "level":"info", "error": "b", "context": "c"}`),
    65  
    66  		// The following messages should parse into 2 distinct log models
    67  		messageLogExpectation(`{"msg":"b", "level":"info", "error": "b"}`),
    68  		messageLogExpectation(`{"msg":"b", "level":"info", "context": "b"}`),
    69  
    70  		// The following messages should parse into 2 distinct log models
    71  		messageLogExpectation(`{"msg":"c", "level":"info"}`),
    72  		messageLogExpectation(`{"msg":"c", "level":"warning"}`),
    73  	}
    74  
    75  	l := parseLogsAndTestExpectations(logs, t)
    76  
    77  	numLogModels := len(l.MessageLogModels.modelStats)
    78  	expectedUniqueModels := 8
    79  	if numLogModels != expectedUniqueModels {
    80  		t.Errorf("Expected %d message log models but found %d\n", expectedUniqueModels, numLogModels)
    81  	}
    82  }
    83  
    84  func TestMessageLogsWithRedactedIpAddresses(t *testing.T) {
    85  	logs := []LogLineWithExpectation{
    86  		// The following messages should parse into 1 distinct log model
    87  		messageLogExpectation(`{"msg":"a", "level":"info", "error": "1.1.1.1->2.2.2.2"}`),
    88  		messageLogExpectation(`{"msg":"a", "level":"info", "error": "3.3.3.3->4.4.4.4"}`),
    89  		messageLogExpectation(`{"msg":"a", "level":"info", "error": "1.1.1.1->2.2.2.2:1"}`),
    90  		messageLogExpectation(`{"msg":"a", "level":"info", "error": "1.1.1.1->2.2.2.2:65535"}`),
    91  	}
    92  
    93  	l := parseLogsAndTestExpectations(logs, t)
    94  
    95  	numLogModels := len(l.MessageLogModels.modelStats)
    96  	expectedUniqueModels := 1
    97  	if numLogModels != expectedUniqueModels {
    98  		t.Errorf("Expected %d message log models but found %d\n", expectedUniqueModels, numLogModels)
    99  	}
   100  }
   101  
   102  // Helpers
   103  
   104  type LogLineWithExpectation struct {
   105  	log     string
   106  	expects parseLineExpectation
   107  }
   108  
   109  type parseLineExpectation struct {
   110  	error   bool
   111  	message bool
   112  	metrics bool
   113  	unknown bool
   114  }
   115  
   116  func parseLogsAndTestExpectations(expectations []LogLineWithExpectation, t *testing.T) (l *LogStats) {
   117  	l = NewLogStats()
   118  
   119  	for _, expectation := range expectations {
   120  		snapshot := *l
   121  
   122  		err := l.ParseLogLine(expectation.log)
   123  
   124  		// Check that the expectation is valid
   125  		if !(expectation.expects.error || expectation.expects.message ||
   126  			expectation.expects.metrics || expectation.expects.unknown) {
   127  			t.Errorf("Malformed expectation expects nothing")
   128  			t.FailNow()
   129  		}
   130  
   131  		// Check error expectation
   132  		if err != nil {
   133  			if expectation.expects.error != true {
   134  				t.Errorf("Unexpected error: < %s >, from log line: \"%s\"\n", err, expectation.log)
   135  			}
   136  		}
   137  
   138  		// Check message expectation
   139  		if expectation.expects.message {
   140  			if l.MessageLogModels.Count != snapshot.MessageLogModels.Count+1 {
   141  				t.Errorf("Expected message log from: \"%s\"\n", expectation.log)
   142  			}
   143  		}
   144  
   145  		// Check metric expectation
   146  		if expectation.expects.metrics {
   147  			if l.MetricsLogModels.Count != snapshot.MetricsLogModels.Count+1 {
   148  				t.Errorf("Expected metric log model from: \"%s\"\n", expectation.log)
   149  			}
   150  		}
   151  
   152  		// Check unknown expectation
   153  		if expectation.expects.unknown {
   154  			if l.UnknownLogModels.Count != snapshot.UnknownLogModels.Count+1 {
   155  				t.Errorf("Expected unknown log model from: \"%s\"\n", expectation.log)
   156  			}
   157  		}
   158  	}
   159  
   160  	return l
   161  }
   162  
   163  func logLinesWithExpectations() (l []LogLineWithExpectation) {
   164  	l = []LogLineWithExpectation{
   165  
   166  		// ************
   167  		// Message logs
   168  		// ************
   169  
   170  		// Test collision of basic message logs
   171  		messageLogExpectation(`{"msg":"a", "level":"info"}`),
   172  		messageLogExpectation(`{"msg":"a", "level":"info"}`),
   173  
   174  		// Different valid levels
   175  		messageLogExpectation(`{"msg":"a", "level":"debug"}`),
   176  		messageLogExpectation(`{"msg":"a", "level":"warning"}`),
   177  		messageLogExpectation(`{"msg":"a", "level":"error"}`),
   178  
   179  		// ************
   180  		// Metrics logs
   181  		// ************
   182  
   183  		// Test collision of basic metrics logs
   184  		metricsLogExpectation(`{"event_name":"a"}`),
   185  		metricsLogExpectation(`{"event_name":"a"}`),
   186  
   187  		// ************
   188  		// Unknown logs
   189  		// ************
   190  
   191  		unknownLogExpectation(`{}`),
   192  		unknownLogExpectation(`{"a":"b"}`),
   193  
   194  		// Test collision of unknown logs with depth
   195  		unknownLogExpectation(`{"a":{"b":[{"c":{}}]}}`),
   196  		unknownLogExpectation(`{"a":{"b":[{"c":{}}]}}`),
   197  
   198  		// Message log line missing level field
   199  		unknownLogExpectation(`{"msg":"a"}`),
   200  
   201  		// **************
   202  		// Malformed logs
   203  		// **************
   204  
   205  		malformedLogExpectation(`{`),
   206  		// Invalid message log levels
   207  		malformedLogExpectation(`{"msg":"a", "level":"{"}`),
   208  		malformedLogExpectation(`{"msg":"a", "level":"unknown"}`),
   209  	}
   210  
   211  	return l
   212  }
   213  
   214  func messageLogExpectation(log string) LogLineWithExpectation {
   215  	return LogLineWithExpectation{
   216  		log: log,
   217  		expects: parseLineExpectation{
   218  			message: true,
   219  		},
   220  	}
   221  }
   222  
   223  func metricsLogExpectation(log string) LogLineWithExpectation {
   224  	return LogLineWithExpectation{
   225  		log: log,
   226  		expects: parseLineExpectation{
   227  			metrics: true,
   228  		},
   229  	}
   230  }
   231  
   232  func unknownLogExpectation(log string) LogLineWithExpectation {
   233  	return LogLineWithExpectation{
   234  		log: log,
   235  		expects: parseLineExpectation{
   236  			unknown: true,
   237  		},
   238  	}
   239  }
   240  
   241  func malformedLogExpectation(log string) LogLineWithExpectation {
   242  	return LogLineWithExpectation{
   243  		log: log,
   244  		expects: parseLineExpectation{
   245  			error: true,
   246  		},
   247  	}
   248  }