Skip to content

Commit 393aeef

Browse files
committed
Refactor unit tests with cleanup
1 parent 83b0ce8 commit 393aeef

File tree

2 files changed

+150
-100
lines changed

2 files changed

+150
-100
lines changed

apm-lambda-extension/logsapi/subscribe.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ func startHTTPServer(transport *LogsTransport) error {
117117
extension.Log.Errorf("Error upon Logs API server start : %v", err)
118118
}
119119
}()
120+
121+
go func() {
122+
<-transport.ctx.Done()
123+
transport.Server.Close()
124+
}()
125+
120126
return nil
121127
}
122128

apm-lambda-extension/main_test.go

Lines changed: 144 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"elastic/apm-lambda-extension/logsapi"
2626
"encoding/json"
2727
"fmt"
28-
"github.com/stretchr/testify/suite"
2928
"io/ioutil"
3029
"net"
3130
"net/http"
@@ -81,9 +80,7 @@ type ApmInfo struct {
8180
Version string `json:"version"`
8281
}
8382

84-
func initMockServers(eventsChannel chan MockEvent) (*httptest.Server, *httptest.Server, *MockServerInternals, *MockServerInternals) {
85-
86-
// Mock APM Server
83+
func newMockApmServer(t *testing.T) (*MockServerInternals, *httptest.Server) {
8784
var apmServerInternals MockServerInternals
8885
apmServerInternals.WaitForUnlockSignal = true
8986
apmServerInternals.UnlockSignalChannel = make(chan struct{})
@@ -135,16 +132,21 @@ func initMockServers(eventsChannel chan MockEvent) (*httptest.Server, *httptest.
135132
}
136133
}
137134
}))
135+
138136
if err := os.Setenv("ELASTIC_APM_LAMBDA_APM_SERVER", apmServer.URL); err != nil {
139137
extension.Log.Fatalf("Could not set environment variable : %v", err)
140-
return nil, nil, nil, nil
138+
return nil, nil
141139
}
142140
if err := os.Setenv("ELASTIC_APM_SECRET_TOKEN", "none"); err != nil {
143141
extension.Log.Fatalf("Could not set environment variable : %v", err)
144-
return nil, nil, nil, nil
142+
return nil, nil
145143
}
146144

147-
// Mock Lambda Server
145+
t.Cleanup(func() { apmServer.Close() })
146+
return &apmServerInternals, apmServer
147+
}
148+
149+
func newMockLambdaServer(t *testing.T, eventsChannel chan MockEvent) *MockServerInternals {
148150
var lambdaServerInternals MockServerInternals
149151
lambdaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
150152
switch r.RequestURI {
@@ -184,7 +186,7 @@ func initMockServers(eventsChannel chan MockEvent) (*httptest.Server, *httptest.
184186
strippedLambdaURL := slicedLambdaURL[1]
185187
if err := os.Setenv("AWS_LAMBDA_RUNTIME_API", strippedLambdaURL); err != nil {
186188
extension.Log.Fatalf("Could not set environment variable : %v", err)
187-
return nil, nil, nil, nil
189+
return nil
188190
}
189191
extensionClient = extension.NewClient(os.Getenv("AWS_LAMBDA_RUNTIME_API"))
190192

@@ -196,10 +198,26 @@ func initMockServers(eventsChannel chan MockEvent) (*httptest.Server, *httptest.
196198
}
197199
if err = os.Setenv("ELASTIC_APM_DATA_RECEIVER_SERVER_PORT", fmt.Sprint(extensionPort)); err != nil {
198200
extension.Log.Fatalf("Could not set environment variable : %v", err)
199-
return nil, nil, nil, nil
201+
return nil
202+
}
203+
204+
t.Cleanup(func() { lambdaServer.Close() })
205+
return &lambdaServerInternals
206+
}
207+
208+
// TODO : Move logger out of extension package and stop using it as a package-level variable
209+
func newLogger(t *testing.T, logLevel string) {
210+
if err := os.Setenv("ELASTIC_APM_LOG_LEVEL", logLevel); err != nil {
211+
t.Fail()
200212
}
213+
}
201214

202-
return lambdaServer, apmServer, &apmServerInternals, &lambdaServerInternals
215+
func newTestStructs(t *testing.T) (context.Context, chan MockEvent) {
216+
http.DefaultServeMux = new(http.ServeMux)
217+
ctx, cancel := context.WithCancel(context.Background())
218+
t.Cleanup(func() { cancel() })
219+
eventsChannel := make(chan MockEvent, 100)
220+
return ctx, eventsChannel
203221
}
204222

205223
func processMockEvent(currId string, event MockEvent, extensionPort string, internals *MockServerInternals) {
@@ -319,187 +337,213 @@ func eventQueueGenerator(inputQueue []MockEvent, eventsChannel chan MockEvent) {
319337
}
320338
}
321339

322-
// TESTS
323-
type MainUnitTestsSuite struct {
324-
suite.Suite
325-
eventsChannel chan MockEvent
326-
lambdaServer *httptest.Server
327-
apmServer *httptest.Server
328-
apmServerInternals *MockServerInternals
329-
lambdaServerInternals *MockServerInternals
330-
ctx context.Context
331-
cancel context.CancelFunc
332-
}
333-
334-
func TestMainUnitTestsSuite(t *testing.T) {
335-
suite.Run(t, new(MainUnitTestsSuite))
336-
}
337-
338-
// This function executes before each test case
339-
func (suite *MainUnitTestsSuite) SetupTest() {
340-
if err := os.Setenv("ELASTIC_APM_LOG_LEVEL", "trace"); err != nil {
341-
suite.T().Fail()
342-
}
343-
suite.ctx, suite.cancel = context.WithCancel(context.Background())
344-
http.DefaultServeMux = new(http.ServeMux)
345-
suite.eventsChannel = make(chan MockEvent, 100)
346-
suite.lambdaServer, suite.apmServer, suite.apmServerInternals, suite.lambdaServerInternals = initMockServers(suite.eventsChannel)
347-
}
348-
349-
// This function executes after each test case
350-
func (suite *MainUnitTestsSuite) TearDownTest() {
351-
suite.lambdaServer.Close()
352-
suite.apmServer.Close()
353-
suite.cancel()
354-
}
355-
356340
// TestStandardEventsChain checks a nominal sequence of events (fast APM server, only one standard event)
357-
func (suite *MainUnitTestsSuite) TestStandardEventsChain() {
341+
func TestStandardEventsChain(t *testing.T) {
342+
newLogger(t, "trace")
343+
_, eventsChannel := newTestStructs(t)
344+
apmServerInternals, _ := newMockApmServer(t)
345+
newMockLambdaServer(t, eventsChannel)
346+
358347
eventsChain := []MockEvent{
359348
{Type: InvokeStandard, APMServerBehavior: TimelyResponse, ExecutionDuration: 1, Timeout: 5},
360349
}
361-
eventQueueGenerator(eventsChain, suite.eventsChannel)
362-
assert.NotPanics(suite.T(), main)
363-
assert.True(suite.T(), strings.Contains(suite.apmServerInternals.Data, string(TimelyResponse)))
350+
eventQueueGenerator(eventsChain, eventsChannel)
351+
assert.NotPanics(t, main)
352+
assert.True(t, strings.Contains(apmServerInternals.Data, string(TimelyResponse)))
364353
}
365354

366355
// TestFlush checks if the flushed param does not cause a panic or an unexpected behavior
367-
func (suite *MainUnitTestsSuite) TestFlush() {
356+
func TestFlush(t *testing.T) {
357+
newLogger(t, "trace")
358+
_, eventsChannel := newTestStructs(t)
359+
apmServerInternals, _ := newMockApmServer(t)
360+
newMockLambdaServer(t, eventsChannel)
361+
368362
eventsChain := []MockEvent{
369363
{Type: InvokeStandardFlush, APMServerBehavior: TimelyResponse, ExecutionDuration: 1, Timeout: 5},
370364
}
371-
eventQueueGenerator(eventsChain, suite.eventsChannel)
372-
assert.NotPanics(suite.T(), main)
373-
assert.True(suite.T(), strings.Contains(suite.apmServerInternals.Data, string(TimelyResponse)))
365+
eventQueueGenerator(eventsChain, eventsChannel)
366+
assert.NotPanics(t, main)
367+
assert.True(t, strings.Contains(apmServerInternals.Data, string(TimelyResponse)))
374368
}
375369

376370
// TestWaitGroup checks if there is no race condition between the main waitgroups (issue #128)
377-
func (suite *MainUnitTestsSuite) TestWaitGroup() {
371+
func TestWaitGroup(t *testing.T) {
372+
newLogger(t, "trace")
373+
_, eventsChannel := newTestStructs(t)
374+
apmServerInternals, _ := newMockApmServer(t)
375+
newMockLambdaServer(t, eventsChannel)
376+
378377
eventsChain := []MockEvent{
379378
{Type: InvokeWaitgroupsRace, APMServerBehavior: TimelyResponse, ExecutionDuration: 1, Timeout: 500},
380379
}
381-
eventQueueGenerator(eventsChain, suite.eventsChannel)
382-
assert.NotPanics(suite.T(), main)
383-
assert.True(suite.T(), strings.Contains(suite.apmServerInternals.Data, string(TimelyResponse)))
380+
eventQueueGenerator(eventsChain, eventsChannel)
381+
assert.NotPanics(t, main)
382+
assert.True(t, strings.Contains(apmServerInternals.Data, string(TimelyResponse)))
384383
}
385384

386385
// TestAPMServerDown tests that main does not panic nor runs indefinitely when the APM server is inactive.
387-
func (suite *MainUnitTestsSuite) TestAPMServerDown() {
388-
suite.apmServer.Close()
386+
func TestAPMServerDown(t *testing.T) {
387+
newLogger(t, "trace")
388+
_, eventsChannel := newTestStructs(t)
389+
apmServerInternals, apmServer := newMockApmServer(t)
390+
newMockLambdaServer(t, eventsChannel)
391+
392+
apmServer.Close()
389393
eventsChain := []MockEvent{
390394
{Type: InvokeStandard, APMServerBehavior: TimelyResponse, ExecutionDuration: 1, Timeout: 5},
391395
}
392-
eventQueueGenerator(eventsChain, suite.eventsChannel)
393-
assert.NotPanics(suite.T(), main)
394-
assert.False(suite.T(), strings.Contains(suite.apmServerInternals.Data, string(TimelyResponse)))
396+
eventQueueGenerator(eventsChain, eventsChannel)
397+
assert.NotPanics(t, main)
398+
assert.False(t, strings.Contains(apmServerInternals.Data, string(TimelyResponse)))
395399
}
396400

397401
// TestAPMServerHangs tests that main does not panic nor runs indefinitely when the APM server does not respond.
398-
func (suite *MainUnitTestsSuite) TestAPMServerHangs() {
402+
func TestAPMServerHangs(t *testing.T) {
403+
newLogger(t, "trace")
404+
_, eventsChannel := newTestStructs(t)
405+
apmServerInternals, _ := newMockApmServer(t)
406+
newMockLambdaServer(t, eventsChannel)
407+
399408
eventsChain := []MockEvent{
400409
{Type: InvokeStandard, APMServerBehavior: Hangs, ExecutionDuration: 1, Timeout: 500},
401410
}
402-
eventQueueGenerator(eventsChain, suite.eventsChannel)
403-
assert.NotPanics(suite.T(), main)
404-
assert.False(suite.T(), strings.Contains(suite.apmServerInternals.Data, string(Hangs)))
405-
suite.apmServerInternals.UnlockSignalChannel <- struct{}{}
411+
eventQueueGenerator(eventsChain, eventsChannel)
412+
assert.NotPanics(t, main)
413+
assert.False(t, strings.Contains(apmServerInternals.Data, string(Hangs)))
414+
apmServerInternals.UnlockSignalChannel <- struct{}{}
406415
}
407416

408417
// TestAPMServerRecovery tests a scenario where the APM server recovers after hanging.
409418
// The default forwarder timeout is 3 seconds, so we wait 5 seconds until we unlock that hanging state.
410419
// Hence, the APM server is waked up just in time to process the TimelyResponse data frame.
411-
func (suite *MainUnitTestsSuite) TestAPMServerRecovery() {
420+
func TestAPMServerRecovery(t *testing.T) {
421+
newLogger(t, "trace")
422+
_, eventsChannel := newTestStructs(t)
423+
apmServerInternals, _ := newMockApmServer(t)
424+
newMockLambdaServer(t, eventsChannel)
425+
412426
if err := os.Setenv("ELASTIC_APM_DATA_FORWARDER_TIMEOUT_SECONDS", "1"); err != nil {
413-
suite.T().Fail()
427+
t.Fail()
414428
}
415429

416430
eventsChain := []MockEvent{
417431
{Type: InvokeStandard, APMServerBehavior: Hangs, ExecutionDuration: 1, Timeout: 5},
418432
{Type: InvokeStandard, APMServerBehavior: TimelyResponse, ExecutionDuration: 1, Timeout: 5},
419433
}
420-
eventQueueGenerator(eventsChain, suite.eventsChannel)
434+
eventQueueGenerator(eventsChain, eventsChannel)
421435
go func() {
422436
time.Sleep(2500 * time.Millisecond) // Cannot multiply time.Second by a float
423-
suite.apmServerInternals.UnlockSignalChannel <- struct{}{}
437+
apmServerInternals.UnlockSignalChannel <- struct{}{}
424438
}()
425-
assert.NotPanics(suite.T(), main)
426-
assert.True(suite.T(), strings.Contains(suite.apmServerInternals.Data, string(Hangs)))
427-
assert.True(suite.T(), strings.Contains(suite.apmServerInternals.Data, string(TimelyResponse)))
439+
assert.NotPanics(t, main)
440+
assert.True(t, strings.Contains(apmServerInternals.Data, string(Hangs)))
441+
assert.True(t, strings.Contains(apmServerInternals.Data, string(TimelyResponse)))
428442
if err := os.Setenv("ELASTIC_APM_DATA_FORWARDER_TIMEOUT_SECONDS", ""); err != nil {
429-
suite.T().Fail()
443+
t.Fail()
430444
}
431445
}
432446

433447
// TestGracePeriodHangs verifies that the WaitforGracePeriod goroutine ends when main() ends.
434448
// This can be checked by asserting that apmTransportStatus is pending after the execution of main.
435-
func (suite *MainUnitTestsSuite) TestGracePeriodHangs() {
449+
func TestGracePeriodHangs(t *testing.T) {
450+
newLogger(t, "trace")
451+
_, eventsChannel := newTestStructs(t)
452+
apmServerInternals, _ := newMockApmServer(t)
453+
newMockLambdaServer(t, eventsChannel)
454+
436455
eventsChain := []MockEvent{
437456
{Type: InvokeStandard, APMServerBehavior: Hangs, ExecutionDuration: 1, Timeout: 500},
438457
}
439-
eventQueueGenerator(eventsChain, suite.eventsChannel)
440-
assert.NotPanics(suite.T(), main)
458+
eventQueueGenerator(eventsChain, eventsChannel)
459+
assert.NotPanics(t, main)
441460

442461
time.Sleep(100 * time.Millisecond)
443-
suite.apmServerInternals.UnlockSignalChannel <- struct{}{}
462+
apmServerInternals.UnlockSignalChannel <- struct{}{}
444463
}
445464

446465
// TestAPMServerCrashesDuringExecution tests that main does not panic nor runs indefinitely when the APM server crashes
447466
// during execution.
448-
func (suite *MainUnitTestsSuite) TestAPMServerCrashesDuringExecution() {
467+
func TestAPMServerCrashesDuringExecution(t *testing.T) {
468+
newLogger(t, "trace")
469+
_, eventsChannel := newTestStructs(t)
470+
apmServerInternals, _ := newMockApmServer(t)
471+
newMockLambdaServer(t, eventsChannel)
472+
449473
eventsChain := []MockEvent{
450474
{Type: InvokeStandard, APMServerBehavior: Crashes, ExecutionDuration: 1, Timeout: 5},
451475
}
452-
eventQueueGenerator(eventsChain, suite.eventsChannel)
453-
assert.NotPanics(suite.T(), main)
454-
assert.False(suite.T(), strings.Contains(suite.apmServerInternals.Data, string(Crashes)))
476+
eventQueueGenerator(eventsChain, eventsChannel)
477+
assert.NotPanics(t, main)
478+
assert.False(t, strings.Contains(apmServerInternals.Data, string(Crashes)))
455479
}
456480

457481
// TestFullChannel checks that an overload of APM data chunks is handled correctly, events dropped beyond the 100th one
458482
// if no space left in channel, no panic, no infinite hanging.
459-
func (suite *MainUnitTestsSuite) TestFullChannel() {
483+
func TestFullChannel(t *testing.T) {
484+
newLogger(t, "trace")
485+
_, eventsChannel := newTestStructs(t)
486+
apmServerInternals, _ := newMockApmServer(t)
487+
newMockLambdaServer(t, eventsChannel)
488+
460489
eventsChain := []MockEvent{
461490
{Type: InvokeMultipleTransactionsOverload, APMServerBehavior: TimelyResponse, ExecutionDuration: 0.1, Timeout: 5},
462491
}
463-
eventQueueGenerator(eventsChain, suite.eventsChannel)
464-
assert.NotPanics(suite.T(), main)
465-
assert.True(suite.T(), strings.Contains(suite.apmServerInternals.Data, string(TimelyResponse)))
492+
eventQueueGenerator(eventsChain, eventsChannel)
493+
assert.NotPanics(t, main)
494+
assert.True(t, strings.Contains(apmServerInternals.Data, string(TimelyResponse)))
466495
}
467496

468497
// TestFullChannelSlowAPMServer tests what happens when the APM Data channel is full and the APM server is slow
469498
// (send strategy : background)
470-
func (suite *MainUnitTestsSuite) TestFullChannelSlowAPMServer() {
499+
func TestFullChannelSlowAPMServer(t *testing.T) {
500+
newLogger(t, "trace")
501+
_, eventsChannel := newTestStructs(t)
502+
newMockApmServer(t)
503+
newMockLambdaServer(t, eventsChannel)
504+
471505
if err := os.Setenv("ELASTIC_APM_SEND_STRATEGY", "background"); err != nil {
472-
suite.T().Fail()
506+
t.Fail()
473507
}
474508

475509
eventsChain := []MockEvent{
476510
{Type: InvokeMultipleTransactionsOverload, APMServerBehavior: SlowResponse, ExecutionDuration: 0.01, Timeout: 5},
477511
}
478-
eventQueueGenerator(eventsChain, suite.eventsChannel)
479-
assert.NotPanics(suite.T(), main)
512+
eventQueueGenerator(eventsChain, eventsChannel)
513+
assert.NotPanics(t, main)
480514
// The test should not hang
481515
if err := os.Setenv("ELASTIC_APM_SEND_STRATEGY", "syncflush"); err != nil {
482-
suite.T().Fail()
516+
t.Fail()
483517
}
484518
}
485519

486520
// TestInfoRequest checks if the extension is able to retrieve APM server info (/ endpoint) (fast APM server, only one standard event)
487-
func (suite *MainUnitTestsSuite) TestInfoRequest() {
521+
func TestInfoRequest(t *testing.T) {
522+
newLogger(t, "trace")
523+
_, eventsChannel := newTestStructs(t)
524+
newMockApmServer(t)
525+
lambdaServerInternals := newMockLambdaServer(t, eventsChannel)
526+
488527
eventsChain := []MockEvent{
489528
{Type: InvokeStandardInfo, APMServerBehavior: TimelyResponse, ExecutionDuration: 1, Timeout: 5},
490529
}
491-
eventQueueGenerator(eventsChain, suite.eventsChannel)
492-
assert.NotPanics(suite.T(), main)
493-
assert.True(suite.T(), strings.Contains(suite.lambdaServerInternals.Data, "7814d524d3602e70b703539c57568cba6964fc20"))
530+
eventQueueGenerator(eventsChain, eventsChannel)
531+
assert.NotPanics(t, main)
532+
assert.True(t, strings.Contains(lambdaServerInternals.Data, "7814d524d3602e70b703539c57568cba6964fc20"))
494533
}
495534

496535
// TestInfoRequest checks if the extension times out when unable to retrieve APM server info (/ endpoint)
497-
func (suite *MainUnitTestsSuite) TestInfoRequestHangs() {
536+
func TestInfoRequestHangs(t *testing.T) {
537+
newLogger(t, "trace")
538+
_, eventsChannel := newTestStructs(t)
539+
apmServerInternals, _ := newMockApmServer(t)
540+
lambdaServerInternals := newMockLambdaServer(t, eventsChannel)
541+
498542
eventsChain := []MockEvent{
499543
{Type: InvokeStandardInfo, APMServerBehavior: Hangs, ExecutionDuration: 1, Timeout: 500},
500544
}
501-
eventQueueGenerator(eventsChain, suite.eventsChannel)
502-
assert.NotPanics(suite.T(), main)
503-
assert.False(suite.T(), strings.Contains(suite.lambdaServerInternals.Data, "7814d524d3602e70b703539c57568cba6964fc20"))
504-
suite.apmServerInternals.UnlockSignalChannel <- struct{}{}
545+
eventQueueGenerator(eventsChain, eventsChannel)
546+
assert.NotPanics(t, main)
547+
assert.False(t, strings.Contains(lambdaServerInternals.Data, "7814d524d3602e70b703539c57568cba6964fc20"))
548+
apmServerInternals.UnlockSignalChannel <- struct{}{}
505549
}

0 commit comments

Comments
 (0)