github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/daemon/logger/awslogs/cloudwatchlogs_test.go (about)

     1  package awslogs
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/http"
     7  	"reflect"
     8  	"runtime"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/aws/aws-sdk-go/aws"
    14  	"github.com/aws/aws-sdk-go/aws/awserr"
    15  	"github.com/aws/aws-sdk-go/aws/request"
    16  	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
    17  	"github.com/docker/docker/daemon/logger"
    18  	"github.com/docker/docker/daemon/logger/loggerutils"
    19  	"github.com/docker/docker/dockerversion"
    20  )
    21  
    22  const (
    23  	groupName         = "groupName"
    24  	streamName        = "streamName"
    25  	sequenceToken     = "sequenceToken"
    26  	nextSequenceToken = "nextSequenceToken"
    27  	logline           = "this is a log line"
    28  )
    29  
    30  func TestNewAWSLogsClientUserAgentHandler(t *testing.T) {
    31  	info := logger.Info{
    32  		Config: map[string]string{
    33  			regionKey: "us-east-1",
    34  		},
    35  	}
    36  
    37  	client, err := newAWSLogsClient(info)
    38  	if err != nil {
    39  		t.Fatal(err)
    40  	}
    41  	realClient, ok := client.(*cloudwatchlogs.CloudWatchLogs)
    42  	if !ok {
    43  		t.Fatal("Could not cast client to cloudwatchlogs.CloudWatchLogs")
    44  	}
    45  	buildHandlerList := realClient.Handlers.Build
    46  	request := &request.Request{
    47  		HTTPRequest: &http.Request{
    48  			Header: http.Header{},
    49  		},
    50  	}
    51  	buildHandlerList.Run(request)
    52  	expectedUserAgentString := fmt.Sprintf("Docker %s (%s) %s/%s (%s; %s; %s)",
    53  		dockerversion.Version, runtime.GOOS, aws.SDKName, aws.SDKVersion, runtime.Version(), runtime.GOOS, runtime.GOARCH)
    54  	userAgent := request.HTTPRequest.Header.Get("User-Agent")
    55  	if userAgent != expectedUserAgentString {
    56  		t.Errorf("Wrong User-Agent string, expected \"%s\" but was \"%s\"",
    57  			expectedUserAgentString, userAgent)
    58  	}
    59  }
    60  
    61  func TestNewAWSLogsClientRegionDetect(t *testing.T) {
    62  	info := logger.Info{
    63  		Config: map[string]string{},
    64  	}
    65  
    66  	mockMetadata := newMockMetadataClient()
    67  	newRegionFinder = func() regionFinder {
    68  		return mockMetadata
    69  	}
    70  	mockMetadata.regionResult <- &regionResult{
    71  		successResult: "us-east-1",
    72  	}
    73  
    74  	_, err := newAWSLogsClient(info)
    75  	if err != nil {
    76  		t.Fatal(err)
    77  	}
    78  }
    79  
    80  func TestCreateSuccess(t *testing.T) {
    81  	mockClient := newMockClient()
    82  	stream := &logStream{
    83  		client:        mockClient,
    84  		logGroupName:  groupName,
    85  		logStreamName: streamName,
    86  	}
    87  	mockClient.createLogStreamResult <- &createLogStreamResult{}
    88  
    89  	err := stream.create()
    90  
    91  	if err != nil {
    92  		t.Errorf("Received unexpected err: %v\n", err)
    93  	}
    94  	argument := <-mockClient.createLogStreamArgument
    95  	if argument.LogGroupName == nil {
    96  		t.Fatal("Expected non-nil LogGroupName")
    97  	}
    98  	if *argument.LogGroupName != groupName {
    99  		t.Errorf("Expected LogGroupName to be %s", groupName)
   100  	}
   101  	if argument.LogStreamName == nil {
   102  		t.Fatal("Expected non-nil LogStreamName")
   103  	}
   104  	if *argument.LogStreamName != streamName {
   105  		t.Errorf("Expected LogStreamName to be %s", streamName)
   106  	}
   107  }
   108  
   109  func TestCreateLogGroupSuccess(t *testing.T) {
   110  	mockClient := newMockClient()
   111  	stream := &logStream{
   112  		client:         mockClient,
   113  		logGroupName:   groupName,
   114  		logStreamName:  streamName,
   115  		logCreateGroup: true,
   116  	}
   117  	mockClient.createLogGroupResult <- &createLogGroupResult{}
   118  	mockClient.createLogStreamResult <- &createLogStreamResult{}
   119  
   120  	err := stream.create()
   121  
   122  	if err != nil {
   123  		t.Errorf("Received unexpected err: %v\n", err)
   124  	}
   125  	argument := <-mockClient.createLogStreamArgument
   126  	if argument.LogGroupName == nil {
   127  		t.Fatal("Expected non-nil LogGroupName")
   128  	}
   129  	if *argument.LogGroupName != groupName {
   130  		t.Errorf("Expected LogGroupName to be %s", groupName)
   131  	}
   132  	if argument.LogStreamName == nil {
   133  		t.Fatal("Expected non-nil LogStreamName")
   134  	}
   135  	if *argument.LogStreamName != streamName {
   136  		t.Errorf("Expected LogStreamName to be %s", streamName)
   137  	}
   138  }
   139  
   140  func TestCreateError(t *testing.T) {
   141  	mockClient := newMockClient()
   142  	stream := &logStream{
   143  		client: mockClient,
   144  	}
   145  	mockClient.createLogStreamResult <- &createLogStreamResult{
   146  		errorResult: errors.New("Error!"),
   147  	}
   148  
   149  	err := stream.create()
   150  
   151  	if err == nil {
   152  		t.Fatal("Expected non-nil err")
   153  	}
   154  }
   155  
   156  func TestCreateAlreadyExists(t *testing.T) {
   157  	mockClient := newMockClient()
   158  	stream := &logStream{
   159  		client: mockClient,
   160  	}
   161  	mockClient.createLogStreamResult <- &createLogStreamResult{
   162  		errorResult: awserr.New(resourceAlreadyExistsCode, "", nil),
   163  	}
   164  
   165  	err := stream.create()
   166  
   167  	if err != nil {
   168  		t.Fatal("Expected nil err")
   169  	}
   170  }
   171  
   172  func TestPublishBatchSuccess(t *testing.T) {
   173  	mockClient := newMockClient()
   174  	stream := &logStream{
   175  		client:        mockClient,
   176  		logGroupName:  groupName,
   177  		logStreamName: streamName,
   178  		sequenceToken: aws.String(sequenceToken),
   179  	}
   180  	mockClient.putLogEventsResult <- &putLogEventsResult{
   181  		successResult: &cloudwatchlogs.PutLogEventsOutput{
   182  			NextSequenceToken: aws.String(nextSequenceToken),
   183  		},
   184  	}
   185  	events := []wrappedEvent{
   186  		{
   187  			inputLogEvent: &cloudwatchlogs.InputLogEvent{
   188  				Message: aws.String(logline),
   189  			},
   190  		},
   191  	}
   192  
   193  	stream.publishBatch(events)
   194  	if stream.sequenceToken == nil {
   195  		t.Fatal("Expected non-nil sequenceToken")
   196  	}
   197  	if *stream.sequenceToken != nextSequenceToken {
   198  		t.Errorf("Expected sequenceToken to be %s, but was %s", nextSequenceToken, *stream.sequenceToken)
   199  	}
   200  	argument := <-mockClient.putLogEventsArgument
   201  	if argument == nil {
   202  		t.Fatal("Expected non-nil PutLogEventsInput")
   203  	}
   204  	if argument.SequenceToken == nil {
   205  		t.Fatal("Expected non-nil PutLogEventsInput.SequenceToken")
   206  	}
   207  	if *argument.SequenceToken != sequenceToken {
   208  		t.Errorf("Expected PutLogEventsInput.SequenceToken to be %s, but was %s", sequenceToken, *argument.SequenceToken)
   209  	}
   210  	if len(argument.LogEvents) != 1 {
   211  		t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents))
   212  	}
   213  	if argument.LogEvents[0] != events[0].inputLogEvent {
   214  		t.Error("Expected event to equal input")
   215  	}
   216  }
   217  
   218  func TestPublishBatchError(t *testing.T) {
   219  	mockClient := newMockClient()
   220  	stream := &logStream{
   221  		client:        mockClient,
   222  		logGroupName:  groupName,
   223  		logStreamName: streamName,
   224  		sequenceToken: aws.String(sequenceToken),
   225  	}
   226  	mockClient.putLogEventsResult <- &putLogEventsResult{
   227  		errorResult: errors.New("Error!"),
   228  	}
   229  
   230  	events := []wrappedEvent{
   231  		{
   232  			inputLogEvent: &cloudwatchlogs.InputLogEvent{
   233  				Message: aws.String(logline),
   234  			},
   235  		},
   236  	}
   237  
   238  	stream.publishBatch(events)
   239  	if stream.sequenceToken == nil {
   240  		t.Fatal("Expected non-nil sequenceToken")
   241  	}
   242  	if *stream.sequenceToken != sequenceToken {
   243  		t.Errorf("Expected sequenceToken to be %s, but was %s", sequenceToken, *stream.sequenceToken)
   244  	}
   245  }
   246  
   247  func TestPublishBatchInvalidSeqSuccess(t *testing.T) {
   248  	mockClient := newMockClientBuffered(2)
   249  	stream := &logStream{
   250  		client:        mockClient,
   251  		logGroupName:  groupName,
   252  		logStreamName: streamName,
   253  		sequenceToken: aws.String(sequenceToken),
   254  	}
   255  	mockClient.putLogEventsResult <- &putLogEventsResult{
   256  		errorResult: awserr.New(invalidSequenceTokenCode, "use token token", nil),
   257  	}
   258  	mockClient.putLogEventsResult <- &putLogEventsResult{
   259  		successResult: &cloudwatchlogs.PutLogEventsOutput{
   260  			NextSequenceToken: aws.String(nextSequenceToken),
   261  		},
   262  	}
   263  
   264  	events := []wrappedEvent{
   265  		{
   266  			inputLogEvent: &cloudwatchlogs.InputLogEvent{
   267  				Message: aws.String(logline),
   268  			},
   269  		},
   270  	}
   271  
   272  	stream.publishBatch(events)
   273  	if stream.sequenceToken == nil {
   274  		t.Fatal("Expected non-nil sequenceToken")
   275  	}
   276  	if *stream.sequenceToken != nextSequenceToken {
   277  		t.Errorf("Expected sequenceToken to be %s, but was %s", nextSequenceToken, *stream.sequenceToken)
   278  	}
   279  
   280  	argument := <-mockClient.putLogEventsArgument
   281  	if argument == nil {
   282  		t.Fatal("Expected non-nil PutLogEventsInput")
   283  	}
   284  	if argument.SequenceToken == nil {
   285  		t.Fatal("Expected non-nil PutLogEventsInput.SequenceToken")
   286  	}
   287  	if *argument.SequenceToken != sequenceToken {
   288  		t.Errorf("Expected PutLogEventsInput.SequenceToken to be %s, but was %s", sequenceToken, *argument.SequenceToken)
   289  	}
   290  	if len(argument.LogEvents) != 1 {
   291  		t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents))
   292  	}
   293  	if argument.LogEvents[0] != events[0].inputLogEvent {
   294  		t.Error("Expected event to equal input")
   295  	}
   296  
   297  	argument = <-mockClient.putLogEventsArgument
   298  	if argument == nil {
   299  		t.Fatal("Expected non-nil PutLogEventsInput")
   300  	}
   301  	if argument.SequenceToken == nil {
   302  		t.Fatal("Expected non-nil PutLogEventsInput.SequenceToken")
   303  	}
   304  	if *argument.SequenceToken != "token" {
   305  		t.Errorf("Expected PutLogEventsInput.SequenceToken to be %s, but was %s", "token", *argument.SequenceToken)
   306  	}
   307  	if len(argument.LogEvents) != 1 {
   308  		t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents))
   309  	}
   310  	if argument.LogEvents[0] != events[0].inputLogEvent {
   311  		t.Error("Expected event to equal input")
   312  	}
   313  }
   314  
   315  func TestPublishBatchAlreadyAccepted(t *testing.T) {
   316  	mockClient := newMockClient()
   317  	stream := &logStream{
   318  		client:        mockClient,
   319  		logGroupName:  groupName,
   320  		logStreamName: streamName,
   321  		sequenceToken: aws.String(sequenceToken),
   322  	}
   323  	mockClient.putLogEventsResult <- &putLogEventsResult{
   324  		errorResult: awserr.New(dataAlreadyAcceptedCode, "use token token", nil),
   325  	}
   326  
   327  	events := []wrappedEvent{
   328  		{
   329  			inputLogEvent: &cloudwatchlogs.InputLogEvent{
   330  				Message: aws.String(logline),
   331  			},
   332  		},
   333  	}
   334  
   335  	stream.publishBatch(events)
   336  	if stream.sequenceToken == nil {
   337  		t.Fatal("Expected non-nil sequenceToken")
   338  	}
   339  	if *stream.sequenceToken != "token" {
   340  		t.Errorf("Expected sequenceToken to be %s, but was %s", "token", *stream.sequenceToken)
   341  	}
   342  
   343  	argument := <-mockClient.putLogEventsArgument
   344  	if argument == nil {
   345  		t.Fatal("Expected non-nil PutLogEventsInput")
   346  	}
   347  	if argument.SequenceToken == nil {
   348  		t.Fatal("Expected non-nil PutLogEventsInput.SequenceToken")
   349  	}
   350  	if *argument.SequenceToken != sequenceToken {
   351  		t.Errorf("Expected PutLogEventsInput.SequenceToken to be %s, but was %s", sequenceToken, *argument.SequenceToken)
   352  	}
   353  	if len(argument.LogEvents) != 1 {
   354  		t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents))
   355  	}
   356  	if argument.LogEvents[0] != events[0].inputLogEvent {
   357  		t.Error("Expected event to equal input")
   358  	}
   359  }
   360  
   361  func TestCollectBatchSimple(t *testing.T) {
   362  	mockClient := newMockClient()
   363  	stream := &logStream{
   364  		client:        mockClient,
   365  		logGroupName:  groupName,
   366  		logStreamName: streamName,
   367  		sequenceToken: aws.String(sequenceToken),
   368  		messages:      make(chan *logger.Message),
   369  	}
   370  	mockClient.putLogEventsResult <- &putLogEventsResult{
   371  		successResult: &cloudwatchlogs.PutLogEventsOutput{
   372  			NextSequenceToken: aws.String(nextSequenceToken),
   373  		},
   374  	}
   375  	ticks := make(chan time.Time)
   376  	newTicker = func(_ time.Duration) *time.Ticker {
   377  		return &time.Ticker{
   378  			C: ticks,
   379  		}
   380  	}
   381  
   382  	go stream.collectBatch()
   383  
   384  	stream.Log(&logger.Message{
   385  		Line:      []byte(logline),
   386  		Timestamp: time.Time{},
   387  	})
   388  
   389  	ticks <- time.Time{}
   390  	stream.Close()
   391  
   392  	argument := <-mockClient.putLogEventsArgument
   393  	if argument == nil {
   394  		t.Fatal("Expected non-nil PutLogEventsInput")
   395  	}
   396  	if len(argument.LogEvents) != 1 {
   397  		t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents))
   398  	}
   399  	if *argument.LogEvents[0].Message != logline {
   400  		t.Errorf("Expected message to be %s but was %s", logline, *argument.LogEvents[0].Message)
   401  	}
   402  }
   403  
   404  func TestCollectBatchTicker(t *testing.T) {
   405  	mockClient := newMockClient()
   406  	stream := &logStream{
   407  		client:        mockClient,
   408  		logGroupName:  groupName,
   409  		logStreamName: streamName,
   410  		sequenceToken: aws.String(sequenceToken),
   411  		messages:      make(chan *logger.Message),
   412  	}
   413  	mockClient.putLogEventsResult <- &putLogEventsResult{
   414  		successResult: &cloudwatchlogs.PutLogEventsOutput{
   415  			NextSequenceToken: aws.String(nextSequenceToken),
   416  		},
   417  	}
   418  	ticks := make(chan time.Time)
   419  	newTicker = func(_ time.Duration) *time.Ticker {
   420  		return &time.Ticker{
   421  			C: ticks,
   422  		}
   423  	}
   424  
   425  	go stream.collectBatch()
   426  
   427  	stream.Log(&logger.Message{
   428  		Line:      []byte(logline + " 1"),
   429  		Timestamp: time.Time{},
   430  	})
   431  	stream.Log(&logger.Message{
   432  		Line:      []byte(logline + " 2"),
   433  		Timestamp: time.Time{},
   434  	})
   435  
   436  	ticks <- time.Time{}
   437  
   438  	// Verify first batch
   439  	argument := <-mockClient.putLogEventsArgument
   440  	if argument == nil {
   441  		t.Fatal("Expected non-nil PutLogEventsInput")
   442  	}
   443  	if len(argument.LogEvents) != 2 {
   444  		t.Errorf("Expected LogEvents to contain 2 elements, but contains %d", len(argument.LogEvents))
   445  	}
   446  	if *argument.LogEvents[0].Message != logline+" 1" {
   447  		t.Errorf("Expected message to be %s but was %s", logline+" 1", *argument.LogEvents[0].Message)
   448  	}
   449  	if *argument.LogEvents[1].Message != logline+" 2" {
   450  		t.Errorf("Expected message to be %s but was %s", logline+" 2", *argument.LogEvents[0].Message)
   451  	}
   452  
   453  	stream.Log(&logger.Message{
   454  		Line:      []byte(logline + " 3"),
   455  		Timestamp: time.Time{},
   456  	})
   457  
   458  	ticks <- time.Time{}
   459  	argument = <-mockClient.putLogEventsArgument
   460  	if argument == nil {
   461  		t.Fatal("Expected non-nil PutLogEventsInput")
   462  	}
   463  	if len(argument.LogEvents) != 1 {
   464  		t.Errorf("Expected LogEvents to contain 1 elements, but contains %d", len(argument.LogEvents))
   465  	}
   466  	if *argument.LogEvents[0].Message != logline+" 3" {
   467  		t.Errorf("Expected message to be %s but was %s", logline+" 3", *argument.LogEvents[0].Message)
   468  	}
   469  
   470  	stream.Close()
   471  
   472  }
   473  
   474  func TestCollectBatchClose(t *testing.T) {
   475  	mockClient := newMockClient()
   476  	stream := &logStream{
   477  		client:        mockClient,
   478  		logGroupName:  groupName,
   479  		logStreamName: streamName,
   480  		sequenceToken: aws.String(sequenceToken),
   481  		messages:      make(chan *logger.Message),
   482  	}
   483  	mockClient.putLogEventsResult <- &putLogEventsResult{
   484  		successResult: &cloudwatchlogs.PutLogEventsOutput{
   485  			NextSequenceToken: aws.String(nextSequenceToken),
   486  		},
   487  	}
   488  	var ticks = make(chan time.Time)
   489  	newTicker = func(_ time.Duration) *time.Ticker {
   490  		return &time.Ticker{
   491  			C: ticks,
   492  		}
   493  	}
   494  
   495  	go stream.collectBatch()
   496  
   497  	stream.Log(&logger.Message{
   498  		Line:      []byte(logline),
   499  		Timestamp: time.Time{},
   500  	})
   501  
   502  	// no ticks
   503  	stream.Close()
   504  
   505  	argument := <-mockClient.putLogEventsArgument
   506  	if argument == nil {
   507  		t.Fatal("Expected non-nil PutLogEventsInput")
   508  	}
   509  	if len(argument.LogEvents) != 1 {
   510  		t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents))
   511  	}
   512  	if *argument.LogEvents[0].Message != logline {
   513  		t.Errorf("Expected message to be %s but was %s", logline, *argument.LogEvents[0].Message)
   514  	}
   515  }
   516  
   517  func TestCollectBatchLineSplit(t *testing.T) {
   518  	mockClient := newMockClient()
   519  	stream := &logStream{
   520  		client:        mockClient,
   521  		logGroupName:  groupName,
   522  		logStreamName: streamName,
   523  		sequenceToken: aws.String(sequenceToken),
   524  		messages:      make(chan *logger.Message),
   525  	}
   526  	mockClient.putLogEventsResult <- &putLogEventsResult{
   527  		successResult: &cloudwatchlogs.PutLogEventsOutput{
   528  			NextSequenceToken: aws.String(nextSequenceToken),
   529  		},
   530  	}
   531  	var ticks = make(chan time.Time)
   532  	newTicker = func(_ time.Duration) *time.Ticker {
   533  		return &time.Ticker{
   534  			C: ticks,
   535  		}
   536  	}
   537  
   538  	go stream.collectBatch()
   539  
   540  	longline := strings.Repeat("A", maximumBytesPerEvent)
   541  	stream.Log(&logger.Message{
   542  		Line:      []byte(longline + "B"),
   543  		Timestamp: time.Time{},
   544  	})
   545  
   546  	// no ticks
   547  	stream.Close()
   548  
   549  	argument := <-mockClient.putLogEventsArgument
   550  	if argument == nil {
   551  		t.Fatal("Expected non-nil PutLogEventsInput")
   552  	}
   553  	if len(argument.LogEvents) != 2 {
   554  		t.Errorf("Expected LogEvents to contain 2 elements, but contains %d", len(argument.LogEvents))
   555  	}
   556  	if *argument.LogEvents[0].Message != longline {
   557  		t.Errorf("Expected message to be %s but was %s", longline, *argument.LogEvents[0].Message)
   558  	}
   559  	if *argument.LogEvents[1].Message != "B" {
   560  		t.Errorf("Expected message to be %s but was %s", "B", *argument.LogEvents[1].Message)
   561  	}
   562  }
   563  
   564  func TestCollectBatchMaxEvents(t *testing.T) {
   565  	mockClient := newMockClientBuffered(1)
   566  	stream := &logStream{
   567  		client:        mockClient,
   568  		logGroupName:  groupName,
   569  		logStreamName: streamName,
   570  		sequenceToken: aws.String(sequenceToken),
   571  		messages:      make(chan *logger.Message),
   572  	}
   573  	mockClient.putLogEventsResult <- &putLogEventsResult{
   574  		successResult: &cloudwatchlogs.PutLogEventsOutput{
   575  			NextSequenceToken: aws.String(nextSequenceToken),
   576  		},
   577  	}
   578  	var ticks = make(chan time.Time)
   579  	newTicker = func(_ time.Duration) *time.Ticker {
   580  		return &time.Ticker{
   581  			C: ticks,
   582  		}
   583  	}
   584  
   585  	go stream.collectBatch()
   586  
   587  	line := "A"
   588  	for i := 0; i <= maximumLogEventsPerPut; i++ {
   589  		stream.Log(&logger.Message{
   590  			Line:      []byte(line),
   591  			Timestamp: time.Time{},
   592  		})
   593  	}
   594  
   595  	// no ticks
   596  	stream.Close()
   597  
   598  	argument := <-mockClient.putLogEventsArgument
   599  	if argument == nil {
   600  		t.Fatal("Expected non-nil PutLogEventsInput")
   601  	}
   602  	if len(argument.LogEvents) != maximumLogEventsPerPut {
   603  		t.Errorf("Expected LogEvents to contain %d elements, but contains %d", maximumLogEventsPerPut, len(argument.LogEvents))
   604  	}
   605  
   606  	argument = <-mockClient.putLogEventsArgument
   607  	if argument == nil {
   608  		t.Fatal("Expected non-nil PutLogEventsInput")
   609  	}
   610  	if len(argument.LogEvents) != 1 {
   611  		t.Errorf("Expected LogEvents to contain %d elements, but contains %d", 1, len(argument.LogEvents))
   612  	}
   613  }
   614  
   615  func TestCollectBatchMaxTotalBytes(t *testing.T) {
   616  	mockClient := newMockClientBuffered(1)
   617  	stream := &logStream{
   618  		client:        mockClient,
   619  		logGroupName:  groupName,
   620  		logStreamName: streamName,
   621  		sequenceToken: aws.String(sequenceToken),
   622  		messages:      make(chan *logger.Message),
   623  	}
   624  	mockClient.putLogEventsResult <- &putLogEventsResult{
   625  		successResult: &cloudwatchlogs.PutLogEventsOutput{
   626  			NextSequenceToken: aws.String(nextSequenceToken),
   627  		},
   628  	}
   629  	var ticks = make(chan time.Time)
   630  	newTicker = func(_ time.Duration) *time.Ticker {
   631  		return &time.Ticker{
   632  			C: ticks,
   633  		}
   634  	}
   635  
   636  	go stream.collectBatch()
   637  
   638  	longline := strings.Repeat("A", maximumBytesPerPut)
   639  	stream.Log(&logger.Message{
   640  		Line:      []byte(longline + "B"),
   641  		Timestamp: time.Time{},
   642  	})
   643  
   644  	// no ticks
   645  	stream.Close()
   646  
   647  	argument := <-mockClient.putLogEventsArgument
   648  	if argument == nil {
   649  		t.Fatal("Expected non-nil PutLogEventsInput")
   650  	}
   651  	bytes := 0
   652  	for _, event := range argument.LogEvents {
   653  		bytes += len(*event.Message)
   654  	}
   655  	if bytes > maximumBytesPerPut {
   656  		t.Errorf("Expected <= %d bytes but was %d", maximumBytesPerPut, bytes)
   657  	}
   658  
   659  	argument = <-mockClient.putLogEventsArgument
   660  	if len(argument.LogEvents) != 1 {
   661  		t.Errorf("Expected LogEvents to contain 1 elements, but contains %d", len(argument.LogEvents))
   662  	}
   663  	message := *argument.LogEvents[0].Message
   664  	if message[len(message)-1:] != "B" {
   665  		t.Errorf("Expected message to be %s but was %s", "B", message[len(message)-1:])
   666  	}
   667  }
   668  
   669  func TestCollectBatchWithDuplicateTimestamps(t *testing.T) {
   670  	mockClient := newMockClient()
   671  	stream := &logStream{
   672  		client:        mockClient,
   673  		logGroupName:  groupName,
   674  		logStreamName: streamName,
   675  		sequenceToken: aws.String(sequenceToken),
   676  		messages:      make(chan *logger.Message),
   677  	}
   678  	mockClient.putLogEventsResult <- &putLogEventsResult{
   679  		successResult: &cloudwatchlogs.PutLogEventsOutput{
   680  			NextSequenceToken: aws.String(nextSequenceToken),
   681  		},
   682  	}
   683  	ticks := make(chan time.Time)
   684  	newTicker = func(_ time.Duration) *time.Ticker {
   685  		return &time.Ticker{
   686  			C: ticks,
   687  		}
   688  	}
   689  
   690  	go stream.collectBatch()
   691  
   692  	times := maximumLogEventsPerPut
   693  	expectedEvents := []*cloudwatchlogs.InputLogEvent{}
   694  	timestamp := time.Now()
   695  	for i := 0; i < times; i++ {
   696  		line := fmt.Sprintf("%d", i)
   697  		if i%2 == 0 {
   698  			timestamp.Add(1 * time.Nanosecond)
   699  		}
   700  		stream.Log(&logger.Message{
   701  			Line:      []byte(line),
   702  			Timestamp: timestamp,
   703  		})
   704  		expectedEvents = append(expectedEvents, &cloudwatchlogs.InputLogEvent{
   705  			Message:   aws.String(line),
   706  			Timestamp: aws.Int64(timestamp.UnixNano() / int64(time.Millisecond)),
   707  		})
   708  	}
   709  
   710  	ticks <- time.Time{}
   711  	stream.Close()
   712  
   713  	argument := <-mockClient.putLogEventsArgument
   714  	if argument == nil {
   715  		t.Fatal("Expected non-nil PutLogEventsInput")
   716  	}
   717  	if len(argument.LogEvents) != times {
   718  		t.Errorf("Expected LogEvents to contain %d elements, but contains %d", times, len(argument.LogEvents))
   719  	}
   720  	for i := 0; i < times; i++ {
   721  		if !reflect.DeepEqual(*argument.LogEvents[i], *expectedEvents[i]) {
   722  			t.Errorf("Expected event to be %v but was %v", *expectedEvents[i], *argument.LogEvents[i])
   723  		}
   724  	}
   725  }
   726  
   727  func TestCreateTagSuccess(t *testing.T) {
   728  	mockClient := newMockClient()
   729  	info := logger.Info{
   730  		ContainerName: "/test-container",
   731  		ContainerID:   "container-abcdefghijklmnopqrstuvwxyz01234567890",
   732  		Config:        map[string]string{"tag": "{{.Name}}/{{.FullID}}"},
   733  	}
   734  	logStreamName, e := loggerutils.ParseLogTag(info, loggerutils.DefaultTemplate)
   735  	if e != nil {
   736  		t.Errorf("Error generating tag: %q", e)
   737  	}
   738  	stream := &logStream{
   739  		client:        mockClient,
   740  		logGroupName:  groupName,
   741  		logStreamName: logStreamName,
   742  	}
   743  	mockClient.createLogStreamResult <- &createLogStreamResult{}
   744  
   745  	err := stream.create()
   746  
   747  	if err != nil {
   748  		t.Errorf("Received unexpected err: %v\n", err)
   749  	}
   750  	argument := <-mockClient.createLogStreamArgument
   751  
   752  	if *argument.LogStreamName != "test-container/container-abcdefghijklmnopqrstuvwxyz01234567890" {
   753  		t.Errorf("Expected LogStreamName to be %s", "test-container/container-abcdefghijklmnopqrstuvwxyz01234567890")
   754  	}
   755  }