// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.

//go:build go1.16
// +build go1.16

package qbusiness

import (
	"bytes"
	"context"
	"io/ioutil"
	"net/http"
	"reflect"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/corehandlers"
	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/awstesting/unit"
	"github.com/aws/aws-sdk-go/private/protocol"
	"github.com/aws/aws-sdk-go/private/protocol/eventstream"
	"github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi"
	"github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamtest"
	"github.com/aws/aws-sdk-go/private/protocol/restjson"
)

var _ time.Time
var _ awserr.Error
var _ context.Context
var _ sync.WaitGroup
var _ strings.Reader

func TestChat_Read(t *testing.T) {
	expectEvents, eventMsgs := mockChatReadEvents()
	sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
		eventstreamtest.ServeEventStream{
			T:      t,
			Events: eventMsgs,
		},
		true,
	)
	if err != nil {
		t.Fatalf("expect no error, %v", err)
	}
	defer cleanupFn()

	svc := New(sess)
	resp, err := svc.Chat(nil)
	if err != nil {
		t.Fatalf("expect no error got, %v", err)
	}
	defer resp.GetStream().Close()

	var i int
	for event := range resp.GetStream().Events() {
		if event == nil {
			t.Errorf("%d, expect event, got nil", i)
		}
		if e, a := expectEvents[i], event; !reflect.DeepEqual(e, a) {
			t.Errorf("%d, expect %T %v, got %T %v", i, e, e, a, a)
		}
		i++
	}

	if err := resp.GetStream().Err(); err != nil {
		t.Errorf("expect no error, %v", err)
	}
}

func TestChat_ReadClose(t *testing.T) {
	_, eventMsgs := mockChatReadEvents()
	sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
		eventstreamtest.ServeEventStream{
			T:      t,
			Events: eventMsgs,
		},
		true,
	)
	if err != nil {
		t.Fatalf("expect no error, %v", err)
	}
	defer cleanupFn()

	svc := New(sess)
	resp, err := svc.Chat(nil)
	if err != nil {
		t.Fatalf("expect no error got, %v", err)
	}

	// Assert calling Err before close does not close the stream.
	resp.GetStream().Err()
	select {
	case _, ok := <-resp.GetStream().Events():
		if !ok {
			t.Fatalf("expect stream not to be closed, but was")
		}
	default:
	}

	resp.GetStream().Close()
	<-resp.GetStream().Events()

	if err := resp.GetStream().Err(); err != nil {
		t.Errorf("expect no error, %v", err)
	}
}

func TestChat_ReadUnknownEvent(t *testing.T) {
	expectEvents, eventMsgs := mockChatReadEvents()
	var eventOffset int

	unknownEvent := eventstream.Message{
		Headers: eventstream.Headers{
			eventstreamtest.EventMessageTypeHeader,
			{
				Name:  eventstreamapi.EventTypeHeader,
				Value: eventstream.StringValue("UnknownEventName"),
			},
		},
		Payload: []byte("some unknown event"),
	}

	eventMsgs = append(eventMsgs[:eventOffset],
		append([]eventstream.Message{unknownEvent}, eventMsgs[eventOffset:]...)...)

	expectEvents = append(expectEvents[:eventOffset],
		append([]ChatOutputStreamEvent{
			&ChatOutputStreamUnknownEvent{
				Type:    "UnknownEventName",
				Message: unknownEvent,
			},
		},
			expectEvents[eventOffset:]...)...)

	sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
		eventstreamtest.ServeEventStream{
			T:      t,
			Events: eventMsgs,
		},
		true,
	)
	if err != nil {
		t.Fatalf("expect no error, %v", err)
	}
	defer cleanupFn()

	svc := New(sess)
	resp, err := svc.Chat(nil)
	if err != nil {
		t.Fatalf("expect no error got, %v", err)
	}
	defer resp.GetStream().Close()

	var i int
	for event := range resp.GetStream().Events() {
		if event == nil {
			t.Errorf("%d, expect event, got nil", i)
		}
		if e, a := expectEvents[i], event; !reflect.DeepEqual(e, a) {
			t.Errorf("%d, expect %T %v, got %T %v", i, e, e, a, a)
		}
		i++
	}

	if err := resp.GetStream().Err(); err != nil {
		t.Errorf("expect no error, %v", err)
	}
}

func BenchmarkChat_Read(b *testing.B) {
	_, eventMsgs := mockChatReadEvents()
	var buf bytes.Buffer
	encoder := eventstream.NewEncoder(&buf)
	for _, msg := range eventMsgs {
		if err := encoder.Encode(msg); err != nil {
			b.Fatalf("failed to encode message, %v", err)
		}
	}
	stream := &loopReader{source: bytes.NewReader(buf.Bytes())}

	sess := unit.Session
	svc := New(sess, &aws.Config{
		Endpoint:               aws.String("https://example.com"),
		DisableParamValidation: aws.Bool(true),
	})
	svc.Handlers.Send.Swap(corehandlers.SendHandler.Name,
		request.NamedHandler{Name: "mockSend",
			Fn: func(r *request.Request) {
				r.HTTPResponse = &http.Response{
					Status:     "200 OK",
					StatusCode: 200,
					Header:     http.Header{},
					Body:       ioutil.NopCloser(stream),
				}
			},
		},
	)

	resp, err := svc.Chat(nil)
	if err != nil {
		b.Fatalf("failed to create request, %v", err)
	}
	defer resp.GetStream().Close()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		if err = resp.GetStream().Err(); err != nil {
			b.Fatalf("expect no error, got %v", err)
		}
		event := <-resp.GetStream().Events()
		if event == nil {
			b.Fatalf("expect event, got nil, %v, %d", resp.GetStream().Err(), i)
		}
	}
}

func mockChatReadEvents() (
	[]ChatOutputStreamEvent,
	[]eventstream.Message,
) {
	expectEvents := []ChatOutputStreamEvent{
		&ActionReviewEvent{
			ConversationId: aws.String("string value goes here"),
			Payload: map[string]*ActionReviewPayloadField{
				"a": {
					AllowedFormat: aws.String("string value goes here"),
					AllowedValues: []*ActionReviewPayloadFieldAllowedValue{
						{},
						{},
						{},
					},
					DisplayDescription: aws.String("string value goes here"),
					DisplayName:        aws.String("string value goes here"),
					DisplayOrder:       aws.Int64(123),
					Required:           aws.Bool(true),
					Type:               aws.String("string value goes here"),
				},
				"b": {
					AllowedFormat: aws.String("string value goes here"),
					AllowedValues: []*ActionReviewPayloadFieldAllowedValue{
						{},
						{},
						{},
					},
					DisplayDescription: aws.String("string value goes here"),
					DisplayName:        aws.String("string value goes here"),
					DisplayOrder:       aws.Int64(123),
					Required:           aws.Bool(true),
					Type:               aws.String("string value goes here"),
				},
				"c": {
					AllowedFormat: aws.String("string value goes here"),
					AllowedValues: []*ActionReviewPayloadFieldAllowedValue{
						{},
						{},
						{},
					},
					DisplayDescription: aws.String("string value goes here"),
					DisplayName:        aws.String("string value goes here"),
					DisplayOrder:       aws.Int64(123),
					Required:           aws.Bool(true),
					Type:               aws.String("string value goes here"),
				},
			},
			PayloadFieldNameSeparator: aws.String("string value goes here"),
			PluginId:                  aws.String("string value goes here"),
			PluginType:                aws.String("string value goes here"),
			SystemMessageId:           aws.String("string value goes here"),
			UserMessageId:             aws.String("string value goes here"),
		},
		&AuthChallengeRequestEvent{
			AuthorizationUrl: aws.String("string value goes here"),
		},
		&FailedAttachmentEvent{
			Attachment: &AttachmentOutput_{
				Error: &ErrorDetail{
					ErrorCode:    aws.String("string value goes here"),
					ErrorMessage: aws.String("string value goes here"),
				},
				Name:   aws.String("string value goes here"),
				Status: aws.String("string value goes here"),
			},
			ConversationId:  aws.String("string value goes here"),
			SystemMessageId: aws.String("string value goes here"),
			UserMessageId:   aws.String("string value goes here"),
		},
		&MetadataEvent{
			ConversationId:   aws.String("string value goes here"),
			FinalTextMessage: aws.String("string value goes here"),
			SourceAttributions: []*SourceAttribution{
				{
					CitationNumber: aws.Int64(123),
					Snippet:        aws.String("string value goes here"),
					TextMessageSegments: []*TextSegment{
						{
							BeginOffset: aws.Int64(123),
							EndOffset:   aws.Int64(123),
							SnippetExcerpt: &SnippetExcerpt{
								Text: aws.String("string value goes here"),
							},
						},
						{
							BeginOffset: aws.Int64(123),
							EndOffset:   aws.Int64(123),
							SnippetExcerpt: &SnippetExcerpt{
								Text: aws.String("string value goes here"),
							},
						},
						{
							BeginOffset: aws.Int64(123),
							EndOffset:   aws.Int64(123),
							SnippetExcerpt: &SnippetExcerpt{
								Text: aws.String("string value goes here"),
							},
						},
					},
					Title:     aws.String("string value goes here"),
					UpdatedAt: aws.Time(time.Unix(1396594860, 0).UTC()),
					Url:       aws.String("string value goes here"),
				},
				{
					CitationNumber: aws.Int64(123),
					Snippet:        aws.String("string value goes here"),
					TextMessageSegments: []*TextSegment{
						{
							BeginOffset: aws.Int64(123),
							EndOffset:   aws.Int64(123),
							SnippetExcerpt: &SnippetExcerpt{
								Text: aws.String("string value goes here"),
							},
						},
						{
							BeginOffset: aws.Int64(123),
							EndOffset:   aws.Int64(123),
							SnippetExcerpt: &SnippetExcerpt{
								Text: aws.String("string value goes here"),
							},
						},
						{
							BeginOffset: aws.Int64(123),
							EndOffset:   aws.Int64(123),
							SnippetExcerpt: &SnippetExcerpt{
								Text: aws.String("string value goes here"),
							},
						},
					},
					Title:     aws.String("string value goes here"),
					UpdatedAt: aws.Time(time.Unix(1396594860, 0).UTC()),
					Url:       aws.String("string value goes here"),
				},
				{
					CitationNumber: aws.Int64(123),
					Snippet:        aws.String("string value goes here"),
					TextMessageSegments: []*TextSegment{
						{
							BeginOffset: aws.Int64(123),
							EndOffset:   aws.Int64(123),
							SnippetExcerpt: &SnippetExcerpt{
								Text: aws.String("string value goes here"),
							},
						},
						{
							BeginOffset: aws.Int64(123),
							EndOffset:   aws.Int64(123),
							SnippetExcerpt: &SnippetExcerpt{
								Text: aws.String("string value goes here"),
							},
						},
						{
							BeginOffset: aws.Int64(123),
							EndOffset:   aws.Int64(123),
							SnippetExcerpt: &SnippetExcerpt{
								Text: aws.String("string value goes here"),
							},
						},
					},
					Title:     aws.String("string value goes here"),
					UpdatedAt: aws.Time(time.Unix(1396594860, 0).UTC()),
					Url:       aws.String("string value goes here"),
				},
			},
			SystemMessageId: aws.String("string value goes here"),
			UserMessageId:   aws.String("string value goes here"),
		},
		&TextOutputEvent{
			ConversationId:  aws.String("string value goes here"),
			SystemMessage:   aws.String("string value goes here"),
			SystemMessageId: aws.String("string value goes here"),
			UserMessageId:   aws.String("string value goes here"),
		},
	}

	var marshalers request.HandlerList
	marshalers.PushBackNamed(restjson.BuildHandler)
	payloadMarshaler := protocol.HandlerPayloadMarshal{
		Marshalers: marshalers,
	}
	_ = payloadMarshaler

	eventMsgs := []eventstream.Message{
		{
			Headers: eventstream.Headers{
				eventstreamtest.EventMessageTypeHeader,
				{
					Name:  eventstreamapi.EventTypeHeader,
					Value: eventstream.StringValue("actionReviewEvent"),
				},
			},
			Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[0]),
		},
		{
			Headers: eventstream.Headers{
				eventstreamtest.EventMessageTypeHeader,
				{
					Name:  eventstreamapi.EventTypeHeader,
					Value: eventstream.StringValue("authChallengeRequestEvent"),
				},
			},
			Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[1]),
		},
		{
			Headers: eventstream.Headers{
				eventstreamtest.EventMessageTypeHeader,
				{
					Name:  eventstreamapi.EventTypeHeader,
					Value: eventstream.StringValue("failedAttachmentEvent"),
				},
			},
			Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[2]),
		},
		{
			Headers: eventstream.Headers{
				eventstreamtest.EventMessageTypeHeader,
				{
					Name:  eventstreamapi.EventTypeHeader,
					Value: eventstream.StringValue("metadataEvent"),
				},
			},
			Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[3]),
		},
		{
			Headers: eventstream.Headers{
				eventstreamtest.EventMessageTypeHeader,
				{
					Name:  eventstreamapi.EventTypeHeader,
					Value: eventstream.StringValue("textEvent"),
				},
			},
			Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[4]),
		},
	}

	return expectEvents, eventMsgs
}

type loopReader struct {
	source *bytes.Reader
}

func (c *loopReader) Read(p []byte) (int, error) {
	if c.source.Len() == 0 {
		c.source.Seek(0, 0)
	}

	return c.source.Read(p)
}

func TestChat_Write(t *testing.T) {
	clientEvents, expectedClientEvents := mockChatWriteEvents()

	sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
		&eventstreamtest.ServeEventStream{
			T:             t,
			ClientEvents:  expectedClientEvents,
			BiDirectional: true,
		},
		true)
	defer cleanupFn()

	svc := New(sess)
	resp, err := svc.Chat(nil)
	if err != nil {
		t.Fatalf("expect no error, got %v", err)
	}

	stream := resp.GetStream()

	for _, event := range clientEvents {
		err = stream.Send(context.Background(), event)
		if err != nil {
			t.Fatalf("expect no error, got %v", err)
		}
	}

	if err := stream.Close(); err != nil {
		t.Errorf("expect no error, got %v", err)
	}
}

func TestChat_WriteClose(t *testing.T) {
	sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
		eventstreamtest.ServeEventStream{T: t, BiDirectional: true},
		true,
	)
	if err != nil {
		t.Fatalf("expect no error, %v", err)
	}
	defer cleanupFn()

	svc := New(sess)
	resp, err := svc.Chat(nil)
	if err != nil {
		t.Fatalf("expect no error got, %v", err)
	}

	// Assert calling Err before close does not close the stream.
	resp.GetStream().Err()

	err = resp.GetStream().Send(context.Background(), &AttachmentInputEvent{})
	if err != nil {
		t.Fatalf("expect no error, got %v", err)
	}

	resp.GetStream().Close()

	if err := resp.GetStream().Err(); err != nil {
		t.Errorf("expect no error, %v", err)
	}
}

func TestChat_WriteError(t *testing.T) {
	sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
		eventstreamtest.ServeEventStream{
			T:               t,
			BiDirectional:   true,
			ForceCloseAfter: time.Millisecond * 500,
		},
		true,
	)
	if err != nil {
		t.Fatalf("expect no error, %v", err)
	}
	defer cleanupFn()

	svc := New(sess)
	resp, err := svc.Chat(nil)
	if err != nil {
		t.Fatalf("expect no error got, %v", err)
	}

	defer resp.GetStream().Close()

	for {
		err = resp.GetStream().Send(context.Background(), &AttachmentInputEvent{})
		if err != nil {
			if strings.Contains("unable to send event", err.Error()) {
				t.Errorf("expected stream closed error, got %v", err)
			}
			break
		}
	}
}

func TestChat_ReadWrite(t *testing.T) {
	expectedServiceEvents, serviceEvents := mockChatReadEvents()
	clientEvents, expectedClientEvents := mockChatWriteEvents()

	sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
		&eventstreamtest.ServeEventStream{
			T:             t,
			ClientEvents:  expectedClientEvents,
			Events:        serviceEvents,
			BiDirectional: true,
		},
		true)
	defer cleanupFn()

	svc := New(sess)
	resp, err := svc.Chat(nil)
	if err != nil {
		t.Fatalf("expect no error, got %v", err)
	}

	stream := resp.GetStream()
	defer stream.Close()

	var wg sync.WaitGroup

	wg.Add(1)
	go func() {
		defer wg.Done()
		var i int
		for event := range resp.GetStream().Events() {
			if event == nil {
				t.Errorf("%d, expect event, got nil", i)
			}
			if e, a := expectedServiceEvents[i], event; !reflect.DeepEqual(e, a) {
				t.Errorf("%d, expect %T %v, got %T %v", i, e, e, a, a)
			}
			i++
		}
	}()

	for _, event := range clientEvents {
		err = stream.Send(context.Background(), event)
		if err != nil {
			t.Errorf("expect no error, got %v", err)
		}
	}

	resp.GetStream().Close()

	wg.Wait()

	if err := resp.GetStream().Err(); err != nil {
		t.Errorf("expect no error, %v", err)
	}
}

func mockChatWriteEvents() (
	[]ChatInputStreamEvent,
	[]eventstream.Message,
) {
	inputEvents := []ChatInputStreamEvent{
		&AttachmentInputEvent{
			Attachment: &AttachmentInput_{
				Data: []byte("blob value goes here"),
				Name: aws.String("string value goes here"),
			},
		},
		&AuthChallengeResponseEvent{
			ResponseMap: map[string]*string{
				"a": aws.String("string value goes here"),
				"b": aws.String("string value goes here"),
				"c": aws.String("string value goes here"),
			},
		},
		&ConfigurationEvent{
			AttributeFilter: &AttributeFilter{
				AndAllFilters: []*AttributeFilter{},
				ContainsAll: &DocumentAttribute{
					Name: aws.String("string value goes here"),
					Value: &DocumentAttributeValue{
						DateValue: aws.Time(time.Unix(1396594860, 0).UTC()),
						LongValue: aws.Int64(1234),
						StringListValue: []*string{
							aws.String("string value goes here"),
							aws.String("string value goes here"),
							aws.String("string value goes here"),
						},
						StringValue: aws.String("string value goes here"),
					},
				},
				ContainsAny: &DocumentAttribute{
					Name: aws.String("string value goes here"),
					Value: &DocumentAttributeValue{
						DateValue: aws.Time(time.Unix(1396594860, 0).UTC()),
						LongValue: aws.Int64(1234),
						StringListValue: []*string{
							aws.String("string value goes here"),
							aws.String("string value goes here"),
							aws.String("string value goes here"),
						},
						StringValue: aws.String("string value goes here"),
					},
				},
				EqualsTo: &DocumentAttribute{
					Name: aws.String("string value goes here"),
					Value: &DocumentAttributeValue{
						DateValue: aws.Time(time.Unix(1396594860, 0).UTC()),
						LongValue: aws.Int64(1234),
						StringListValue: []*string{
							aws.String("string value goes here"),
							aws.String("string value goes here"),
							aws.String("string value goes here"),
						},
						StringValue: aws.String("string value goes here"),
					},
				},
				GreaterThan: &DocumentAttribute{
					Name: aws.String("string value goes here"),
					Value: &DocumentAttributeValue{
						DateValue: aws.Time(time.Unix(1396594860, 0).UTC()),
						LongValue: aws.Int64(1234),
						StringListValue: []*string{
							aws.String("string value goes here"),
							aws.String("string value goes here"),
							aws.String("string value goes here"),
						},
						StringValue: aws.String("string value goes here"),
					},
				},
				GreaterThanOrEquals: &DocumentAttribute{
					Name: aws.String("string value goes here"),
					Value: &DocumentAttributeValue{
						DateValue: aws.Time(time.Unix(1396594860, 0).UTC()),
						LongValue: aws.Int64(1234),
						StringListValue: []*string{
							aws.String("string value goes here"),
							aws.String("string value goes here"),
							aws.String("string value goes here"),
						},
						StringValue: aws.String("string value goes here"),
					},
				},
				LessThan: &DocumentAttribute{
					Name: aws.String("string value goes here"),
					Value: &DocumentAttributeValue{
						DateValue: aws.Time(time.Unix(1396594860, 0).UTC()),
						LongValue: aws.Int64(1234),
						StringListValue: []*string{
							aws.String("string value goes here"),
							aws.String("string value goes here"),
							aws.String("string value goes here"),
						},
						StringValue: aws.String("string value goes here"),
					},
				},
				LessThanOrEquals: &DocumentAttribute{
					Name: aws.String("string value goes here"),
					Value: &DocumentAttributeValue{
						DateValue: aws.Time(time.Unix(1396594860, 0).UTC()),
						LongValue: aws.Int64(1234),
						StringListValue: []*string{
							aws.String("string value goes here"),
							aws.String("string value goes here"),
							aws.String("string value goes here"),
						},
						StringValue: aws.String("string value goes here"),
					},
				},
				NotFilter:    nil,
				OrAllFilters: []*AttributeFilter{},
			},
			ChatMode: aws.String("string value goes here"),
			ChatModeConfiguration: &ChatModeConfiguration{
				PluginConfiguration: &PluginConfiguration{
					PluginId: aws.String("string value goes here"),
				},
			},
		},
		&EndOfInputEvent{},
		&TextInputEvent{
			UserMessage: aws.String("string value goes here"),
		},
	}

	var marshalers request.HandlerList
	marshalers.PushBackNamed(restjson.BuildHandler)
	payloadMarshaler := protocol.HandlerPayloadMarshal{
		Marshalers: marshalers,
	}
	_ = payloadMarshaler

	eventMsgs := []eventstream.Message{
		{
			Headers: eventstream.Headers{
				eventstreamtest.EventMessageTypeHeader,
				{
					Name:  eventstreamapi.EventTypeHeader,
					Value: eventstream.StringValue("attachmentEvent"),
				},
			},
			Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, inputEvents[0]),
		},
		{
			Headers: eventstream.Headers{
				eventstreamtest.EventMessageTypeHeader,
				{
					Name:  eventstreamapi.EventTypeHeader,
					Value: eventstream.StringValue("authChallengeResponseEvent"),
				},
			},
			Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, inputEvents[1]),
		},
		{
			Headers: eventstream.Headers{
				eventstreamtest.EventMessageTypeHeader,
				{
					Name:  eventstreamapi.EventTypeHeader,
					Value: eventstream.StringValue("configurationEvent"),
				},
			},
			Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, inputEvents[2]),
		},
		{
			Headers: eventstream.Headers{
				eventstreamtest.EventMessageTypeHeader,
				{
					Name:  eventstreamapi.EventTypeHeader,
					Value: eventstream.StringValue("endOfInputEvent"),
				},
			},
		},
		{
			Headers: eventstream.Headers{
				eventstreamtest.EventMessageTypeHeader,
				{
					Name:  eventstreamapi.EventTypeHeader,
					Value: eventstream.StringValue("textEvent"),
				},
			},
			Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, inputEvents[4]),
		},
	}

	return inputEvents, eventMsgs
}
