/*
 * Copyright 2021 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package server

import (
	"context"
	"encoding/binary"
	"strings"
	"sync/atomic"
	"testing"

	"github.com/cloudwego/gopkg/protocol/thrift"

	"github.com/cloudwego/kitex/internal/mocks"
	"github.com/cloudwego/kitex/internal/test"
	"github.com/cloudwego/kitex/pkg/remote/trans/invoke"
)

// TestInvokerCall tests Invoker, call Kitex server just like SDK.
func TestInvokerCall(t *testing.T) {
	var gotErr atomic.Value
	invoker := NewInvoker(WithMetaHandler(noopMetahandler{}), WithErrorHandler(func(ctx context.Context, err error) error {
		gotErr.Store(err)
		return err
	}))

	err := invoker.RegisterService(mocks.ServiceInfo(), mocks.MyServiceHandler())
	if err != nil {
		t.Fatal(err)
	}
	err = invoker.Init()
	if err != nil {
		t.Fatal(err)
	}

	args := mocks.NewMockArgs()

	// call success
	hl := make([]byte, 4)
	b, _ := thrift.MarshalFastMsg("mock", thrift.CALL, 0, args.(thrift.FastCodec))
	binary.BigEndian.PutUint32(hl, uint32(len(b)))
	msg := invoke.NewMessage(nil, nil)
	err = msg.SetRequestBytes(append(hl, b...))
	test.Assert(t, err == nil)
	err = invoker.Call(msg)
	if err != nil {
		t.Fatal(err)
	}
	b, err = msg.GetResponseBytes()
	if err != nil {
		t.Fatal(err)
	}
	test.Assert(t, len(b) > 0)
	test.Assert(t, gotErr.Load() == nil)

	// call fails
	hl = make([]byte, 4)
	b, _ = thrift.MarshalFastMsg("mockError", thrift.CALL, 0, args.(thrift.FastCodec))
	binary.BigEndian.PutUint32(hl, uint32(len(b)))
	msg = invoke.NewMessage(nil, nil)
	err = msg.SetRequestBytes(append(hl, b...))
	test.Assert(t, err == nil)
	err = invoker.Call(msg)
	if err != nil {
		t.Fatal(err)
	}
	test.Assert(t, strings.Contains(gotErr.Load().(error).Error(), "mockError"))
}

// TestInvokerInit test invoker init without service info, should fail
func TestInvokerInit(t *testing.T) {
	// test init without register server info
	invoker := NewInvoker()
	err := invoker.Init()
	test.Assert(t, err != nil)
	test.Assert(t, strings.Contains(err.Error(), "run: no service. Use RegisterService to set one"))

	err = invoker.RegisterService(mocks.ServiceInfo(), mocks.MyServiceHandler())
	test.Assert(t, err == nil)
}
