package xray

import (
	"context"
	"fmt"
	"testing"

	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
)

func TestError(t *testing.T) {
	err := Error("Test")
	stack := convertStack(err.StackTrace())

	assert.Equal(t, "Test", err.Error())
	assert.Equal(t, "Error", err.Type)
	assert.Equal(t, "TestError", stack[0].Label)
}

func TestErrorf(t *testing.T) {
	err := Errorf("Test")
	stack := convertStack(err.StackTrace())

	assert.Equal(t, "Test", err.Error())
	assert.Equal(t, "Error", err.Type)
	assert.Equal(t, "TestErrorf", stack[0].Label)
}

func TestPanic(t *testing.T) {
	var err *xrayError
	func() {
		defer func() {
			err = Panic(recover().(string))
		}()
		panic("Test")
	}()
	stack := convertStack(err.StackTrace())

	assert.Equal(t, "Test", err.Error())
	assert.Equal(t, "Panic", err.Type)
	assert.Equal(t, "TestPanic.func1", stack[0].Label)
	assert.Equal(t, "TestPanic", stack[1].Label)
}

func TestPanicf(t *testing.T) {
	var err *xrayError
	func() {
		defer func() {
			err = Panicf("%v", recover())
		}()
		panic("Test")
	}()
	stack := convertStack(err.StackTrace())

	assert.Equal(t, "Test", err.Error())
	assert.Equal(t, "Panic", err.Type)
	assert.Equal(t, "TestPanicf.func1", stack[0].Label)
	assert.Equal(t, "TestPanicf", stack[1].Label)
}

func TestException(t *testing.T) {
	Capture(context.Background(), "Exception", func(ctx context.Context) error {
		return func() error {
			return fmt.Errorf("Raw Error")
		}()
	})

	s, e := testDaemon.Recv()
	assert.NoError(t, e)

	ex := s.Cause.Exceptions[0]
	assert.Equal(t, "Raw Error", ex.Message)
	assert.Equal(t, "Error", ex.Type)
	assert.Equal(t, "Capture", ex.Stack[0].Label)
	assert.Equal(t, "code.justin.tv/foundation/xray/capture.go", ex.Stack[0].Path)
	assert.Equal(t, "TestException", ex.Stack[1].Label)
	assert.Equal(t, "code.justin.tv/foundation/xray/exception_test.go", ex.Stack[1].Path)
}

func TestExceptionWithStack(t *testing.T) {
	Capture(context.Background(), "Exception", func(ctx context.Context) error {
		return func() error {
			return errors.New("Traced Error")
		}()
	})

	s, e := testDaemon.Recv()
	assert.NoError(t, e)

	ex := s.Cause.Exceptions[0]
	assert.Equal(t, "Traced Error", ex.Message)
	assert.Equal(t, "Error", ex.Type)
	assert.Equal(t, "TestExceptionWithStack.func1.1", ex.Stack[0].Label)
	assert.Equal(t, "code.justin.tv/foundation/xray/exception_test.go", ex.Stack[0].Path)
	assert.Equal(t, "TestExceptionWithStack.func1", ex.Stack[1].Label)
	assert.Equal(t, "code.justin.tv/foundation/xray/exception_test.go", ex.Stack[1].Path)
	assert.Equal(t, "Capture", ex.Stack[2].Label)
	assert.Equal(t, "code.justin.tv/foundation/xray/capture.go", ex.Stack[2].Path)
	assert.Equal(t, "TestExceptionWithStack", ex.Stack[3].Label)
	assert.Equal(t, "code.justin.tv/foundation/xray/exception_test.go", ex.Stack[3].Path)
}

func TestExceptionPanic(t *testing.T) {
	func() {
		defer func() { recover() }()

		Capture(context.Background(), "Exception", func(ctx context.Context) error {
			return func() error {
				panic("Panic")
			}()
		})
	}()

	s, e := testDaemon.Recv()
	assert.NoError(t, e)

	ex := s.Cause.Exceptions[0]
	assert.Equal(t, "Panic", ex.Message)
	assert.Equal(t, "Panic", ex.Type)
	assert.Equal(t, "TestExceptionPanic.func1.2.1", ex.Stack[0].Label)
	assert.Equal(t, "code.justin.tv/foundation/xray/exception_test.go", ex.Stack[0].Path)
	assert.Equal(t, "TestExceptionPanic.func1.2", ex.Stack[1].Label)
	assert.Equal(t, "code.justin.tv/foundation/xray/exception_test.go", ex.Stack[1].Path)
	assert.Equal(t, "Capture", ex.Stack[2].Label)
	assert.Equal(t, "code.justin.tv/foundation/xray/capture.go", ex.Stack[2].Path)
	assert.Equal(t, "TestExceptionPanic.func1", ex.Stack[3].Label)
	assert.Equal(t, "code.justin.tv/foundation/xray/exception_test.go", ex.Stack[3].Path)
	assert.Equal(t, "TestExceptionPanic", ex.Stack[4].Label)
	assert.Equal(t, "code.justin.tv/foundation/xray/exception_test.go", ex.Stack[4].Path)
}
