package goroutine

import (
	"context"
	"fmt"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

var (
	ctx = context.Background()
)

func TestSafeGo(t *testing.T) {
	Convey("TestSafeGo", t, func() {
		Convey("normal case", func() {
			So(func() {
				SafeGo(ctx, func() {})
			}, ShouldNotPanic)
		})
		Convey("should not panic when panic happen in fn", func() {
			So(func() {
				SafeGo(ctx, func() {
					panic("some panic")
				})
			}, ShouldNotPanic)
		})
	})
}

func TestRecover(t *testing.T) {
	Convey("TestRecover", t, func() {
		Convey("normal case", func() {
			So(func() {
				defer Recover(ctx)
				panic("some panic")
			}, ShouldNotPanic)
		})
	})
}

func TestConcurrent(t *testing.T) {
	Convey("TestConcurrent", t, func() {
		Convey("normal case", func() {
			err := Concurrent(ctx, func() error {
				return nil
			})
			So(err, ShouldBeNil)
		})
	})
}

func Test_callFn(t *testing.T) {
	Convey("Test_callFn", t, func() {
		Convey("should return nil if fn return nil", func() {
			err := callFn(func() error {
				return nil
			})
			So(err, ShouldBeNil)
		})
		Convey("should return err if fn return err", func() {
			err := callFn(func() error {
				return fmt.Errorf("err")
			})
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldEqual, "err")
		})
		Convey("should return err if panic happen in fn", func() {
			err := callFn(func() error {
				panic("panicked")
			})
			So(err, ShouldNotBeNil)
			So(err.Error(), ShouldEqual, "panic with: panicked")
		})
	})
}

func TestConcurrentWithLimit(t *testing.T) {
	Convey("TestConcurrentWithLimit", t, func() {

		Convey("should return nil when all fn return nil", func() {
			err := ConcurrentWithLimit(ctx, 2, func() error {
				return nil
			}, func() error {
				return nil
			})
			So(err, ShouldBeNil)
		})
		Convey("should return err if any fn return error", func() {
			err := ConcurrentWithLimit(ctx, 3, func() error {
				return nil
			}, func() error {
				return fmt.Errorf("err")
			}, func() error {
				return nil
			})
			So(err, ShouldNotBeNil)
		})
		Convey("should be ok if len(fns) > limit", func() {
			err := ConcurrentWithLimit(ctx, 1, func() error {
				return nil
			}, func() error {
				return fmt.Errorf("err")
			}, func() error {
				return nil
			})
			So(err, ShouldNotBeNil)

			err = ConcurrentWithLimit(ctx, 1, func() error {
				return nil
			}, func() error {
				return nil
			}, func() error {
				return nil
			})
			So(err, ShouldBeNil)
		})
		Convey("should be panic if limit is invalid", func() {
			So(func() {
				_ = ConcurrentWithLimit(ctx, 0, func() error { return nil })
			}, ShouldPanic)
		})
		Convey("should not panic if panic happen in some fn", func() {
			var err error
			So(func() {
				err = ConcurrentWithLimit(ctx, 1, func() error {
					panic("some panic")
				})
			}, ShouldNotPanic)
			So(err, ShouldNotBeNil)
		})
	})
}
