chore: import upstream snapshot with attribution
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,396 @@
|
||||
// Package test implements a testing framwork, and provides default tests.
|
||||
//
|
||||
// Deprecated: This package is deprecated in favor of go-micro.dev/v6/testing.
|
||||
// Use the testing.Harness for a cleaner, more maintainable approach.
|
||||
// See test/DEPRECATED.md for migration guide.
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"go-micro.dev/v6"
|
||||
"go-micro.dev/v6/client"
|
||||
"go-micro.dev/v6/debug/handler"
|
||||
|
||||
pb "go-micro.dev/v6/debug/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoTests returns no test params are set.
|
||||
ErrNoTests = errors.New("No tests to run, all values set to 0")
|
||||
testTopic = "Test-Topic"
|
||||
errorTopic = "Error-Topic"
|
||||
)
|
||||
|
||||
type parTest func(name string, c client.Client, p, s int, errChan chan error)
|
||||
type testFunc func(name string, c client.Client, errChan chan error)
|
||||
|
||||
// ServiceTestConfig allows you to easily test a service configuration by
|
||||
// running predefined tests against your custom service. You only need to
|
||||
// provide a function to create the service, and how many of which test you
|
||||
// want to run.
|
||||
//
|
||||
// The default tests provided, all running with separate parallel routines are:
|
||||
// - Sequential Call requests
|
||||
// - Bi-directional streaming
|
||||
// - Pub/Sub events brokering
|
||||
//
|
||||
// You can provide an array of parallel routines to run for the request and
|
||||
// stream tests. They will be run as matrix tests, so with each possible combination.
|
||||
// Thus, in total (p * seq) + (p * streams) tests will be run.
|
||||
type ServiceTestConfig struct {
|
||||
// Service name to use for the tests
|
||||
Name string
|
||||
// NewService function will be called to setup the new service.
|
||||
// It takes in a list of options, which by default will Context and an
|
||||
// AfterStart with channel to signal when the service has been started.
|
||||
NewService func(name string, opts ...micro.Option) (micro.Service, error)
|
||||
// Parallel is the number of prallell routines to use for the tests.
|
||||
Parallel []int
|
||||
// Sequential is the number of sequential requests to send per parallel process.
|
||||
Sequential []int
|
||||
// Streams is the nummber of streaming messages to send over the stream per routine.
|
||||
Streams []int
|
||||
// PubSub is the number of times to publish messages to the broker per routine.
|
||||
PubSub []int
|
||||
|
||||
mu sync.Mutex
|
||||
msgCount int
|
||||
}
|
||||
|
||||
// Run will start the benchmark tests.
|
||||
func (stc *ServiceTestConfig) Run(b *testing.B) {
|
||||
if err := stc.validate(); err != nil {
|
||||
b.Fatal("Failed to validate config", err)
|
||||
}
|
||||
|
||||
// Run routines with sequential requests
|
||||
stc.prepBench(b, "req", stc.runParSeqTest, stc.Sequential)
|
||||
|
||||
// Run routines with streams
|
||||
stc.prepBench(b, "streams", stc.runParStreamTest, stc.Streams)
|
||||
|
||||
// Run routines with pub/sub
|
||||
stc.prepBench(b, "pubsub", stc.runBrokerTest, stc.PubSub)
|
||||
}
|
||||
|
||||
// prepBench will prepare the benmark by setting the right parameters,
|
||||
// and invoking the test.
|
||||
func (stc *ServiceTestConfig) prepBench(b *testing.B, tName string, test parTest, seq []int) {
|
||||
par := stc.Parallel
|
||||
|
||||
// No requests needed
|
||||
if len(seq) == 0 || seq[0] == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, parallel := range par {
|
||||
for _, sequential := range seq {
|
||||
// Create the service name for the test
|
||||
name := fmt.Sprintf("%s.%dp-%d%s", stc.Name, parallel, sequential, tName)
|
||||
|
||||
// Run test with parallel routines making each sequential requests
|
||||
test := func(name string, c client.Client, errChan chan error) {
|
||||
test(name, c, parallel, sequential, errChan)
|
||||
}
|
||||
|
||||
benchmark := func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
stc.runBench(b, name, test)
|
||||
}
|
||||
|
||||
b.Logf("----------- STARTING TEST %s -----------", name)
|
||||
|
||||
// Run test, return if it fails
|
||||
if !b.Run(name, benchmark) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// runParSeqTest will make s sequential requests in p parallel routines.
|
||||
func (stc *ServiceTestConfig) runParSeqTest(name string, c client.Client, p, s int, errChan chan error) {
|
||||
testParallel(p, func() {
|
||||
// Make serial requests
|
||||
for z := 0; z < s; z++ {
|
||||
if err := testRequest(context.Background(), c, name); err != nil {
|
||||
errChan <- errors.Wrapf(err, "[%s] Request failed during testRequest", name)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Handle is used as a test handler.
|
||||
func (stc *ServiceTestConfig) Handle(ctx context.Context, msg *pb.HealthRequest) error {
|
||||
stc.mu.Lock()
|
||||
stc.msgCount++
|
||||
stc.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleError is used as a test handler.
|
||||
func (stc *ServiceTestConfig) HandleError(ctx context.Context, msg *pb.HealthRequest) error {
|
||||
return errors.New("dummy error")
|
||||
}
|
||||
|
||||
// runBrokerTest will publish messages to the broker to test pub/sub.
|
||||
func (stc *ServiceTestConfig) runBrokerTest(name string, c client.Client, p, s int, errChan chan error) {
|
||||
stc.msgCount = 0
|
||||
|
||||
testParallel(p, func() {
|
||||
for z := 0; z < s; z++ {
|
||||
msg := pb.BusMsg{Msg: "Hello from broker!"}
|
||||
if err := c.Publish(context.Background(), c.NewMessage(testTopic, &msg)); err != nil {
|
||||
errChan <- errors.Wrap(err, "failed to publish message to broker")
|
||||
return
|
||||
}
|
||||
|
||||
msg = pb.BusMsg{Msg: "Some message that will error"}
|
||||
if err := c.Publish(context.Background(), c.NewMessage(errorTopic, &msg)); err == nil {
|
||||
errChan <- errors.New("Publish is supposed to return an error, but got no error")
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if stc.msgCount != s*p {
|
||||
errChan <- fmt.Errorf("pub/sub does not work properly, invalid message count. Expected %d messaged, but received %d", s*p, stc.msgCount)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// runParStreamTest will start streaming, and send s messages parallel in p routines.
|
||||
func (stc *ServiceTestConfig) runParStreamTest(name string, c client.Client, p, s int, errChan chan error) {
|
||||
testParallel(p, func() {
|
||||
// Create a client service
|
||||
srv := pb.NewDebugService(name, c)
|
||||
|
||||
// Establish a connection to server over which we start streaming
|
||||
bus, err := srv.MessageBus(context.Background())
|
||||
if err != nil {
|
||||
errChan <- errors.Wrap(err, "failed to connect to message bus")
|
||||
return
|
||||
}
|
||||
|
||||
// Start streaming requests
|
||||
for z := 0; z < s; z++ {
|
||||
if err := bus.Send(&pb.BusMsg{Msg: "Hack the world!"}); err != nil {
|
||||
errChan <- errors.Wrap(err, "failed to send to stream")
|
||||
return
|
||||
}
|
||||
|
||||
msg, err := bus.Recv()
|
||||
if err != nil {
|
||||
errChan <- errors.Wrap(err, "failed to receive message from stream")
|
||||
return
|
||||
}
|
||||
|
||||
expected := "Request received!"
|
||||
if msg.Msg != expected {
|
||||
errChan <- fmt.Errorf("stream returned unexpected mesage. Expected '%s', but got '%s'", expected, msg.Msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// validate will make sure the provided test parameters are a legal combination.
|
||||
func (stc *ServiceTestConfig) validate() error {
|
||||
lp, lseq, lstr := len(stc.Parallel), len(stc.Sequential), len(stc.Streams)
|
||||
|
||||
if lp == 0 || (lseq == 0 && lstr == 0) {
|
||||
return ErrNoTests
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// runBench will create a service with the provided stc.NewService function,
|
||||
// and run a benchmark on the test function.
|
||||
func (stc *ServiceTestConfig) runBench(b *testing.B, name string, test testFunc) {
|
||||
b.StopTimer()
|
||||
|
||||
// Channel to signal service has started
|
||||
started := make(chan struct{})
|
||||
|
||||
// Context with cancel to stop the service
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
opts := []micro.Option{
|
||||
micro.Context(ctx),
|
||||
micro.AfterStart(func() error {
|
||||
started <- struct{}{}
|
||||
return nil
|
||||
}),
|
||||
}
|
||||
|
||||
// Create a new service per test
|
||||
service, err := stc.NewService(name, opts...)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create service: %v", err)
|
||||
}
|
||||
|
||||
// Register handler
|
||||
if err := pb.RegisterDebugHandler(service.Server(), handler.NewHandler(service.Client())); err != nil {
|
||||
b.Fatalf("failed to register handler during initial service setup: %v", err)
|
||||
}
|
||||
|
||||
o := service.Options()
|
||||
if err := o.Broker.Connect(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
// a := new(testService)
|
||||
if err := o.Server.Subscribe(o.Server.NewSubscriber(testTopic, stc.Handle)); err != nil {
|
||||
b.Fatalf("[%s] Failed to register subscriber: %v", name, err)
|
||||
}
|
||||
|
||||
if err := o.Server.Subscribe(o.Server.NewSubscriber(errorTopic, stc.HandleError)); err != nil {
|
||||
b.Fatalf("[%s] Failed to register subscriber: %v", name, err)
|
||||
}
|
||||
|
||||
b.Logf("# == [ Service ] ==================")
|
||||
b.Logf("# * Server: %s", o.Server.String())
|
||||
b.Logf("# * Client: %s", o.Client.String())
|
||||
b.Logf("# * Transport: %s", o.Transport.String())
|
||||
b.Logf("# * Broker: %s", o.Broker.String())
|
||||
b.Logf("# * Registry: %s", o.Registry.String())
|
||||
b.Logf("# * Auth: %s", o.Auth.String())
|
||||
b.Logf("# * Cache: %s", o.Cache.String())
|
||||
b.Logf("# ================================")
|
||||
|
||||
RunBenchmark(b, name, service, test, cancel, started)
|
||||
}
|
||||
|
||||
// RunBenchmark will run benchmarks on a provided service.
|
||||
//
|
||||
// A test function can be provided that will be fun b.N times.
|
||||
func RunBenchmark(b *testing.B, name string, service micro.Service, test testFunc,
|
||||
cancel context.CancelFunc, started chan struct{}) {
|
||||
b.StopTimer()
|
||||
|
||||
// Receive errors from routines on this channel
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
// Receive singal after service has shutdown
|
||||
done := make(chan struct{})
|
||||
|
||||
// Start the server
|
||||
go func() {
|
||||
b.Logf("[%s] Starting server for benchmark", name)
|
||||
|
||||
if err := service.Run(); err != nil {
|
||||
errChan <- errors.Wrapf(err, "[%s] Error occurred during service.Run", name)
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
sigTerm := make(chan struct{})
|
||||
|
||||
// Benchmark routine
|
||||
go func() {
|
||||
defer func() {
|
||||
b.StopTimer()
|
||||
|
||||
// Shutdown service
|
||||
b.Logf("[%s] Shutting down", name)
|
||||
cancel()
|
||||
|
||||
// Wait for service to be fully stopped
|
||||
<-done
|
||||
sigTerm <- struct{}{}
|
||||
}()
|
||||
|
||||
// Wait for service to start
|
||||
<-started
|
||||
|
||||
// Give the registry more time to setup
|
||||
time.Sleep(time.Second)
|
||||
|
||||
b.Logf("[%s] Server started", name)
|
||||
|
||||
// Make a test call to warm the cache
|
||||
for i := 0; i < 10; i++ {
|
||||
if err := testRequest(context.Background(), service.Client(), name); err != nil {
|
||||
errChan <- errors.Wrapf(err, "[%s] Failure during cache warmup testRequest", name)
|
||||
}
|
||||
}
|
||||
|
||||
// Check registration
|
||||
services, err := service.Options().Registry.GetService(name)
|
||||
if err != nil || len(services) == 0 {
|
||||
errChan <- fmt.Errorf("service registration must have failed (%d services found), unable to get service: %w", len(services), err)
|
||||
return
|
||||
}
|
||||
|
||||
// Start benchmark
|
||||
b.Logf("[%s] Starting benchtest", name)
|
||||
b.ResetTimer()
|
||||
b.StartTimer()
|
||||
|
||||
// Number of iterations
|
||||
for i := 0; i < b.N; i++ {
|
||||
test(name, service.Client(), errChan)
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for completion or catch any errors
|
||||
select {
|
||||
case err := <-errChan:
|
||||
b.Fatal(err)
|
||||
case <-sigTerm:
|
||||
b.Logf("[%s] Completed benchmark", name)
|
||||
}
|
||||
}
|
||||
|
||||
// testParallel will run the test function in p parallel routines.
|
||||
func testParallel(p int, test func()) {
|
||||
// Waitgroup to wait for requests to finish
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
// For concurrency
|
||||
for j := 0; j < p; j++ {
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
test()
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait for test completion
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// testRequest sends one test request.
|
||||
// It calls the Debug.Health endpoint, and validates if the response returned
|
||||
// contains the expected message.
|
||||
func testRequest(ctx context.Context, c client.Client, name string) error {
|
||||
req := c.NewRequest(
|
||||
name,
|
||||
"Debug.Health",
|
||||
new(pb.HealthRequest),
|
||||
)
|
||||
|
||||
rsp := new(pb.HealthResponse)
|
||||
|
||||
if err := c.Call(ctx, req, rsp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rsp.Status != "ok" {
|
||||
return errors.New("service response: " + rsp.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
// Package test provides utilities for testing micro services.
|
||||
//
|
||||
// Due to go-micro's global defaults, running multiple services in one process
|
||||
// requires careful isolation. This package provides helpers for the common case
|
||||
// of testing a single service.
|
||||
//
|
||||
// Basic usage:
|
||||
//
|
||||
// func TestUserService(t *testing.T) {
|
||||
// h := test.NewHarness(t)
|
||||
// defer h.Stop()
|
||||
//
|
||||
// // Register your service handler
|
||||
// h.Register(new(UsersHandler))
|
||||
//
|
||||
// // Start the harness
|
||||
// h.Start()
|
||||
//
|
||||
// // Call the service
|
||||
// var rsp UserResponse
|
||||
// err := h.Call("Users.Create", &CreateRequest{Name: "Alice"}, &rsp)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// }
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go-micro.dev/v6/broker"
|
||||
"go-micro.dev/v6/client"
|
||||
"go-micro.dev/v6/registry"
|
||||
"go-micro.dev/v6/server"
|
||||
"go-micro.dev/v6/transport"
|
||||
)
|
||||
|
||||
// Harness provides an in-process test environment for a micro service
|
||||
type Harness struct {
|
||||
t *testing.T
|
||||
name string
|
||||
handler interface{}
|
||||
registry registry.Registry
|
||||
transport transport.Transport
|
||||
broker broker.Broker
|
||||
server server.Server
|
||||
client client.Client
|
||||
started bool
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewHarness creates a new test harness
|
||||
func NewHarness(t *testing.T) *Harness {
|
||||
// Create isolated instances for testing
|
||||
reg := registry.NewMemoryRegistry()
|
||||
tr := transport.NewHTTPTransport()
|
||||
br := broker.NewMemoryBroker()
|
||||
|
||||
return &Harness{
|
||||
t: t,
|
||||
name: "test",
|
||||
registry: reg,
|
||||
transport: tr,
|
||||
broker: br,
|
||||
}
|
||||
}
|
||||
|
||||
// Name sets the service name (default: "test")
|
||||
func (h *Harness) Name(name string) *Harness {
|
||||
h.name = name
|
||||
return h
|
||||
}
|
||||
|
||||
// Register sets the handler for the service
|
||||
func (h *Harness) Register(handler interface{}) *Harness {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
if h.started {
|
||||
h.t.Fatal("cannot register handler after Start()")
|
||||
}
|
||||
|
||||
h.handler = handler
|
||||
return h
|
||||
}
|
||||
|
||||
// Start starts the service
|
||||
func (h *Harness) Start() {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
if h.started {
|
||||
return
|
||||
}
|
||||
|
||||
if h.handler == nil {
|
||||
h.t.Fatal("no handler registered, call Register() first")
|
||||
}
|
||||
|
||||
// Connect broker
|
||||
if err := h.broker.Connect(); err != nil {
|
||||
h.t.Fatalf("failed to connect broker: %v", err)
|
||||
}
|
||||
|
||||
// Create server with isolated transport
|
||||
h.server = server.NewServer(
|
||||
server.Name(h.name),
|
||||
server.Registry(h.registry),
|
||||
server.Transport(h.transport),
|
||||
server.Broker(h.broker),
|
||||
server.Address("127.0.0.1:0"),
|
||||
)
|
||||
|
||||
// Register handler
|
||||
if err := h.server.Handle(h.server.NewHandler(h.handler)); err != nil {
|
||||
h.t.Fatalf("failed to register handler: %v", err)
|
||||
}
|
||||
|
||||
// Start server
|
||||
if err := h.server.Start(); err != nil {
|
||||
h.t.Fatalf("failed to start server: %v", err)
|
||||
}
|
||||
|
||||
// Create client with same registry/transport
|
||||
h.client = client.NewClient(
|
||||
client.Registry(h.registry),
|
||||
client.Transport(h.transport),
|
||||
client.Broker(h.broker),
|
||||
client.RequestTimeout(5*time.Second),
|
||||
)
|
||||
|
||||
// Wait for registration
|
||||
h.waitForService()
|
||||
|
||||
h.started = true
|
||||
}
|
||||
|
||||
func (h *Harness) waitForService() {
|
||||
deadline := time.Now().Add(5 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
services, err := h.registry.GetService(h.name)
|
||||
if err == nil && len(services) > 0 && len(services[0].Nodes) > 0 {
|
||||
return
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
h.t.Fatalf("service %s did not register in time", h.name)
|
||||
}
|
||||
|
||||
// Stop stops the service
|
||||
func (h *Harness) Stop() {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
if h.server != nil {
|
||||
_ = h.server.Stop()
|
||||
}
|
||||
if h.broker != nil {
|
||||
_ = h.broker.Disconnect()
|
||||
}
|
||||
|
||||
h.started = false
|
||||
}
|
||||
|
||||
// Call invokes a service method
|
||||
func (h *Harness) Call(endpoint string, req, rsp interface{}) error {
|
||||
return h.CallContext(context.Background(), endpoint, req, rsp)
|
||||
}
|
||||
|
||||
// CallContext invokes a service method with context
|
||||
func (h *Harness) CallContext(ctx context.Context, endpoint string, req, rsp interface{}) error {
|
||||
if !h.started {
|
||||
return fmt.Errorf("harness not started, call Start() first")
|
||||
}
|
||||
|
||||
request := h.client.NewRequest(h.name, endpoint, req)
|
||||
return h.client.Call(ctx, request, rsp)
|
||||
}
|
||||
|
||||
// Client returns the test client for advanced usage
|
||||
func (h *Harness) Client() client.Client {
|
||||
return h.client
|
||||
}
|
||||
|
||||
// Server returns the test server for advanced usage
|
||||
func (h *Harness) Server() server.Server {
|
||||
return h.server
|
||||
}
|
||||
|
||||
// Registry returns the test registry for advanced usage
|
||||
func (h *Harness) Registry() registry.Registry {
|
||||
return h.registry
|
||||
}
|
||||
|
||||
// --- Assertions ---
|
||||
|
||||
// AssertServiceRunning checks that the service is registered
|
||||
func (h *Harness) AssertServiceRunning() {
|
||||
h.t.Helper()
|
||||
|
||||
services, err := h.registry.GetService(h.name)
|
||||
if err != nil {
|
||||
h.t.Errorf("service %s not found: %v", h.name, err)
|
||||
return
|
||||
}
|
||||
if len(services) == 0 || len(services[0].Nodes) == 0 {
|
||||
h.t.Errorf("service %s has no running instances", h.name)
|
||||
}
|
||||
}
|
||||
|
||||
// AssertCallSucceeds checks that a call succeeds
|
||||
func (h *Harness) AssertCallSucceeds(endpoint string, req, rsp interface{}) {
|
||||
h.t.Helper()
|
||||
|
||||
if err := h.Call(endpoint, req, rsp); err != nil {
|
||||
h.t.Errorf("call %s failed: %v", endpoint, err)
|
||||
}
|
||||
}
|
||||
|
||||
// AssertCallFails checks that a call fails
|
||||
func (h *Harness) AssertCallFails(endpoint string, req, rsp interface{}) {
|
||||
h.t.Helper()
|
||||
|
||||
if err := h.Call(endpoint, req, rsp); err == nil {
|
||||
h.t.Errorf("expected call %s to fail, but it succeeded", endpoint)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Simple test handler
|
||||
type GreeterHandler struct{}
|
||||
|
||||
type HelloRequest struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type HelloResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (g *GreeterHandler) Hello(ctx context.Context, req *HelloRequest, rsp *HelloResponse) error {
|
||||
rsp.Message = "Hello " + req.Name
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestHarnessBasic(t *testing.T) {
|
||||
h := NewHarness(t)
|
||||
defer h.Stop()
|
||||
|
||||
h.Name("greeter").Register(new(GreeterHandler))
|
||||
h.Start()
|
||||
|
||||
// Check service is running
|
||||
h.AssertServiceRunning()
|
||||
|
||||
// Make a call
|
||||
var rsp HelloResponse
|
||||
err := h.Call("GreeterHandler.Hello", &HelloRequest{Name: "World"}, &rsp)
|
||||
if err != nil {
|
||||
t.Fatalf("call failed: %v", err)
|
||||
}
|
||||
|
||||
if rsp.Message != "Hello World" {
|
||||
t.Errorf("expected 'Hello World', got '%s'", rsp.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHarnessCallBeforeStart(t *testing.T) {
|
||||
h := NewHarness(t)
|
||||
defer h.Stop()
|
||||
|
||||
h.Register(new(GreeterHandler))
|
||||
// Don't call Start()
|
||||
|
||||
var rsp HelloResponse
|
||||
err := h.Call("GreeterHandler.Hello", &HelloRequest{Name: "World"}, &rsp)
|
||||
if err == nil {
|
||||
t.Error("expected error when calling before Start()")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHarnessAssertCallSucceeds(t *testing.T) {
|
||||
h := NewHarness(t)
|
||||
defer h.Stop()
|
||||
|
||||
h.Name("greeter").Register(new(GreeterHandler))
|
||||
h.Start()
|
||||
|
||||
var rsp HelloResponse
|
||||
h.AssertCallSucceeds("GreeterHandler.Hello", &HelloRequest{Name: "Test"}, &rsp)
|
||||
|
||||
if rsp.Message != "Hello Test" {
|
||||
t.Errorf("expected 'Hello Test', got '%s'", rsp.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHarnessClientAndServer(t *testing.T) {
|
||||
h := NewHarness(t)
|
||||
defer h.Stop()
|
||||
|
||||
h.Name("greeter").Register(new(GreeterHandler))
|
||||
h.Start()
|
||||
|
||||
// Check we can access client and server
|
||||
if h.Client() == nil {
|
||||
t.Fatal("client is nil")
|
||||
}
|
||||
if h.Server() == nil {
|
||||
t.Fatal("server is nil")
|
||||
}
|
||||
if h.Registry() == nil {
|
||||
t.Fatal("registry is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHarnessWithContext(t *testing.T) {
|
||||
h := NewHarness(t)
|
||||
defer h.Stop()
|
||||
|
||||
h.Name("greeter").Register(new(GreeterHandler))
|
||||
h.Start()
|
||||
|
||||
ctx := context.Background()
|
||||
var rsp HelloResponse
|
||||
err := h.CallContext(ctx, "GreeterHandler.Hello", &HelloRequest{Name: "Context"}, &rsp)
|
||||
if err != nil {
|
||||
t.Fatalf("call with context failed: %v", err)
|
||||
}
|
||||
|
||||
if rsp.Message != "Hello Context" {
|
||||
t.Errorf("expected 'Hello Context', got '%s'", rsp.Message)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user