package app import ( "bytes" "context" "errors" "io" "testing" "go.uber.org/fx" ) var errTestStopFailed = errors.New("test stop hook failed") // TestRunAppPrintsStopErrorOnce checks that when a stop hook fails, runApp // prints the hook's error once and returns exit status 1. func TestRunAppPrintsStopErrorOnce(t *testing.T) { t.Parallel() app := fx.New( fx.NopLogger, fx.Invoke(func(lifecycle fx.Lifecycle, shutdowner fx.Shutdowner) { lifecycle.Append(fx.Hook{ // Ask to shut down as soon as the app has started, so // that runApp goes on to stop it. OnStart: func(context.Context) error { return shutdowner.Shutdown() }, OnStop: func(context.Context) error { return errTestStopFailed }, }) }), ) var stderr bytes.Buffer exitCode := runApp(app, &stderr) if exitCode != 1 { t.Errorf("runApp() = %d, want 1", exitCode) } want := errTestStopFailed.Error() + "\n" if stderr.String() != want { t.Errorf("stderr = %q, want %q", stderr.String(), want) } } // TestRunAppReturnsShutdownExitCode checks that runApp returns the exit // code a shutdown request names. func TestRunAppReturnsShutdownExitCode(t *testing.T) { t.Parallel() app := fx.New( fx.NopLogger, fx.Invoke(func(lifecycle fx.Lifecycle, shutdowner fx.Shutdowner) { lifecycle.Append(fx.Hook{ OnStart: func(context.Context) error { return shutdowner.Shutdown(fx.ExitCode(3)) }, }) }), ) exitCode := runApp(app, io.Discard) if exitCode != 3 { t.Errorf("runApp() = %d, want 3", exitCode) } }