package main import ( "errors" "testing" "go.uber.org/fx" ) // errTestHook is the error returned by the test hooks that fail. var errTestHook = errors.New("test hook failed") // TestRunAppExitCode checks the exit code runApp returns: the one a // shutdown request carries, 0 for a request without one (as for SIGINT or // SIGTERM), and 1 when the app fails to start or to stop. func TestRunAppExitCode(t *testing.T) { t.Parallel() cases := []struct { name string hook func(shutdowner fx.Shutdowner) fx.Hook want int }{ { name: "shutdown requested with exit code 1", hook: func(shutdowner fx.Shutdowner) fx.Hook { return fx.StartHook(func() error { return shutdowner.Shutdown(fx.ExitCode(1)) }) }, want: 1, }, { name: "shutdown requested without an exit code", hook: func(shutdowner fx.Shutdowner) fx.Hook { return fx.StartHook(func() error { return shutdowner.Shutdown() }) }, want: 0, }, { name: "start fails", hook: func(fx.Shutdowner) fx.Hook { return fx.StartHook(func() error { return errTestHook }) }, want: 1, }, { name: "stop fails", hook: func(shutdowner fx.Shutdowner) fx.Hook { return fx.StartStopHook( func() error { return shutdowner.Shutdown() }, func() error { return errTestHook }, ) }, want: 1, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { t.Parallel() app := fx.New( fx.NopLogger, fx.Invoke(func(lc fx.Lifecycle, shutdowner fx.Shutdowner) { lc.Append(tc.hook(shutdowner)) }), ) got := runApp(app) t.Logf("runApp() = %d", got) if got != tc.want { t.Errorf("runApp() = %d, want %d", got, tc.want) } }) } }