...

Source file src/go.mongodb.org/mongo-driver/mongo/integration/log_helpers_test.go

Documentation: go.mongodb.org/mongo-driver/mongo/integration

     1  // Copyright (C) MongoDB, Inc. 2023-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  package integration
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  
    13  	"go.mongodb.org/mongo-driver/internal/logger"
    14  	"go.mongodb.org/mongo-driver/mongo/integration/mtest"
    15  )
    16  
    17  type testLogSink struct {
    18  	logs       chan func() (int, string, []interface{})
    19  	bufferSize int
    20  	logsCount  int
    21  	errsCh     chan error
    22  }
    23  
    24  type logValidator func(order int, lvl int, msg string, kv ...interface{}) error
    25  
    26  func newTestLogSink(ctx context.Context, mt *mtest.T, bufferSize int, validator logValidator) *testLogSink {
    27  	mt.Helper()
    28  
    29  	sink := &testLogSink{
    30  		logs:       make(chan func() (int, string, []interface{}), bufferSize),
    31  		errsCh:     make(chan error, bufferSize),
    32  		bufferSize: bufferSize,
    33  	}
    34  
    35  	go func() {
    36  		order := 0
    37  		for log := range sink.logs {
    38  			select {
    39  			case <-ctx.Done():
    40  				sink.errsCh <- ctx.Err()
    41  
    42  				return
    43  			default:
    44  			}
    45  
    46  			level, msg, args := log()
    47  			if err := validator(order, level, msg, args...); err != nil {
    48  				sink.errsCh <- fmt.Errorf("invalid log at position %d, level %d, and msg %q: %w", order,
    49  					level, msg, err)
    50  			}
    51  
    52  			order++
    53  		}
    54  
    55  		close(sink.errsCh)
    56  	}()
    57  
    58  	return sink
    59  }
    60  
    61  func (sink *testLogSink) Info(level int, msg string, keysAndValues ...interface{}) {
    62  	sink.logs <- func() (int, string, []interface{}) {
    63  		return level, msg, keysAndValues
    64  	}
    65  
    66  	if sink.logsCount++; sink.logsCount == sink.bufferSize {
    67  		close(sink.logs)
    68  	}
    69  }
    70  
    71  func (sink *testLogSink) Error(err error, msg string, keysAndValues ...interface{}) {
    72  	keysAndValues = append(keysAndValues, "error", err)
    73  	sink.Info(int(logger.LevelInfo), msg, keysAndValues)
    74  }
    75  
    76  func (sink *testLogSink) errs() <-chan error {
    77  	return sink.errsCh
    78  }
    79  
    80  func findLogValue(mt *mtest.T, key string, values ...interface{}) interface{} {
    81  	mt.Helper()
    82  
    83  	for i := 0; i < len(values); i += 2 {
    84  		if values[i] == key {
    85  			return values[i+1]
    86  		}
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  type truncValidator func(values ...interface{}) error
    93  
    94  // newTruncValidator will return a logger validator for validating truncated
    95  // messages. It takes the key for the portion of the document to validate
    96  // (e.g. "command" for started events, "reply" for finished events, etc), and
    97  // returns an anonymous function that can be used to validate the truncated
    98  // message.
    99  func newTruncValidator(mt *mtest.T, key string, cond func(string) error) truncValidator {
   100  	mt.Helper()
   101  
   102  	return func(values ...interface{}) error {
   103  		cmd := findLogValue(mt, key, values...)
   104  		if cmd == nil {
   105  			return fmt.Errorf("%q not found in keys and values", key)
   106  		}
   107  
   108  		cmdStr, ok := cmd.(string)
   109  
   110  		if !ok {
   111  			return fmt.Errorf("command is not a string")
   112  		}
   113  
   114  		return cond(cmdStr)
   115  	}
   116  }
   117  

View as plain text