tryvium-travels/memongo

nullpointer panic when running go test ./...

Closed this issue · 9 comments

Hi,

first of all, thanks for keeping this project alive, it helps a lot. I have some issues when running tests in a CI pipeline (or just with go test ./... on my machine - sometimes (but not every time) the tests fail with a nil pointer exception in memongo.go:192:

panic: runtime error: invalid memory address or nil pointer dereference
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x8c7ee6]

goroutine 1 [running]:
github.com/tryvium-travels/memongo.(*Server).Stop(0x0)
	//home/user/go/pkg/mod/github.com/tryvium-travels/memongo@v0.3.2/memongo.go:192 +0x46
panic({0xc831c0, 0x144b6f0})
	/usr/lib/go/src/runtime/panic.go:1047 +0x266
github.com/tryvium-travels/memongo.(*Server).URI(...)
	//home/user/go/pkg/mod/github.com/tryvium-travels/memongo@v0.3.2/memongo.go:181
myproject/pkg/store/mongo_test.TestMain(0x0)
	/home/user/myproject/src/pkg/store/mongo/mongo_test.go:39 +0x1ee
main.main()
	_testmain.go:101 +0x365

I tried downgrading to v0.3.2 yesterday, which worked - yesterday. Today I have the same error so downgrading does nothing I suppose. I cannot tackle this because as I said, sometimes it works.

System is (locally) an up-to-date arch linux box with mongod v4.4.12 installed, the tests are run with a func TestMain(m *testing.M), where the server gets started.

It all seems to work fine when running the tests directly (i.e. from the VSCode window).

I appreciate any hints on this. If you need more info, please say so. Thanks!

Need more info, but let me try to understand what's happening

the nil pointer you are getting means that when you call .Stop() method the server is still nil.

Can you share at least parts of your TestMain so we can understand better?

wow, that was quick. :) sure, here is the TestMain:

func TestMain(m *testing.M) {
	cfg := mongo.Config{ Port: 30256 }
	server, err := mongo.NewMemongoServer(cfg)
	if err != nil {
		log.Fatal(err)
	}
	defer server.Stop()
	cfg.Uri = server.URI()

	db, err := mongo.New(cfg)
	if err != nil {
		log.Fatal(err)
	}

	os.Exit(m.Run())
}

This works fine when called directly from the editor. It breaks however on go test ./.... There are two different tests where this gets used, both have the same main. NewMemongoServer is just a wrapper for different OSes to download the correct version. mongo.New() then just connects to the given server.

Edit: more info: The same error happens when I leave out defer Stop().

Do you happen by any chance to stop your memongo servers outside this main files?

No, not that I know of. I just run the full testing. Just for the sake of it I removed the Stop() call in both tests, but that gives another nil pointer exception on memongo.(*Server):

(Note: I upped this to 0.4.0 again)

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0xc105eb]

goroutine 1 [running]:
github.com/tryvium-travels/memongo.(*Server).URI(...)
	/home/myuser/go/pkg/mod/github.com/tryvium-travels/memongo@v0.4.0/memongo.go:204

Further trying to understand

Can you share one of the test files?

Also check if you are assigning nil values somewhere in your servers

An additional note from me would be to use the testing suite from testify repository instead of a Test main because it better handles concurrency

ok so I debugged this until memongo.StartWithOptions, which returns a nil server and a nil error when running go test ./....

The options used on my machine here are:

&memongo.Options{
  ShouldUseReplica:false, 
  Port:30256, 
  CachePath:"", 
  MongoVersion:"4.4.12", 
  DownloadURL:"", 
  MongodBin:"/usr/bin/mongod", 
  Logger:(*log.Logger)(nil), 
  LogLevel:0, 
  StartupTimeout:10000000000, 
  Auth:false,
}

Here is an example test file:


import (
	"context"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
	"myproject/pkg/models"
	"myproject/pkg/store/mongo"
)

var coreTest *Core
var defaultProject *models.Project

func TestMain(m *testing.M) {
	cfg := mongo.Config{
		Port:       30256,
		ObjectsDB:  "objectsDB",
		ProjectsDB: "projectsDB",
	}

	server, err := mongo.NewMemongoServer(cfg)
	if err != nil {
		log.Fatal(err)
	}
	defer server.Stop()
	cfg.Uri = server.URI()

	db, err := mongo.New(cfg)
	if err != nil {
		log.Fatal(err)
	}

	coreTest = New(db)
	defaultProject, err = coreTest.CreateProject(context.Background(), &models.Project{Name: "default"})
	if err != nil {
		log.Fatal(err)
	}

	os.Exit(m.Run())
}

func Test_CreateProject(t *testing.T) {
	ctx := context.Background()

	tests := []struct {
		name  string
		inp   *models.Project
		want  *models.Project
		fails bool
	}{
		{
			"creates a project",
			&models.Project{Name: "create"},
			&models.Project{Name: "create"},
			false,
		},
		{
			"fails on existing",
			&models.Project{Name: "create"},
			nil,
			true,
		},
		{
			"fails on validate",
			&models.Project{},
			nil,
			true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			res, err := coreTest.CreateProject(ctx, tt.inp)
			if tt.fails {
				assert.Error(t, err)
				return
			}

			assert.NoError(t, err)
			assert.Equal(t, res.Name, tt.want.Name)
			assert.NoError(t, res.ID.Valid())
		})
	}
}

Edit: The server gets started like this:

// defaultPath is /usr/bin/mongod
if _, err = os.Stat(defaultPath); err == nil {
		return memongo.StartWithOptions(&memongo.Options{
			MongoVersion:   version,
			MongodBin:      defaultPath,
			Port:           port,
			StartupTimeout: 10 * time.Second,
		})
	}

another update: if I force to run the tests not in parallel (go test -p 1 ./...), it works just fine.

There is then indeed an error in how you do tests, nothing related to memongo project

Here, let me help

Use stretchr/testify/suite packages for your tests and setup SetupTest() and TeardownTest() methods properly and it will probably solve your problem

Alright, thanks for your input. Closing this as it apparently works as intended.

Cheers!