mirror of
https://github.com/lorsanstand/HomeOps-Hub.git
synced 2026-06-19 14:25:16 +03:00
feat: new agent connections test and refactoring tests
This commit is contained in:
@@ -2,6 +2,7 @@ package connection_manager
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -18,6 +19,10 @@ type streamMock struct {
|
|||||||
sendCh chan *pb.ServerCommandRequest
|
sendCh chan *pb.ServerCommandRequest
|
||||||
closeCh chan struct{}
|
closeCh chan struct{}
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
mu sync.Mutex
|
||||||
|
sendErr error
|
||||||
|
recvErr error
|
||||||
|
closeOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *streamMock) Context() context.Context {
|
func (f *streamMock) Context() context.Context {
|
||||||
@@ -25,11 +30,23 @@ func (f *streamMock) Context() context.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *streamMock) Send(request *pb.ServerCommandRequest) error {
|
func (f *streamMock) Send(request *pb.ServerCommandRequest) error {
|
||||||
|
f.mu.Lock()
|
||||||
|
err := f.sendErr
|
||||||
|
f.mu.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
f.sendCh <- request
|
f.sendCh <- request
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *streamMock) Recv() (*pb.AgentEvent, error) {
|
func (f *streamMock) Recv() (*pb.AgentEvent, error) {
|
||||||
|
f.mu.Lock()
|
||||||
|
recvErr := f.recvErr
|
||||||
|
f.mu.Unlock()
|
||||||
|
if recvErr != nil {
|
||||||
|
return nil, recvErr
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case msg, ok := <-f.recvCh:
|
case msg, ok := <-f.recvCh:
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -44,101 +61,153 @@ func (f *streamMock) Recv() (*pb.AgentEvent, error) {
|
|||||||
func (f *streamMock) Close() error {
|
func (f *streamMock) Close() error {
|
||||||
select {
|
select {
|
||||||
case f.closeCh <- struct{}{}:
|
case f.closeCh <- struct{}{}:
|
||||||
close(f.recvCh)
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
f.closeOnce.Do(func() {
|
||||||
|
close(f.recvCh)
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type heartBeatMock struct {
|
type heartBeatMock struct {
|
||||||
|
mu sync.Mutex
|
||||||
countUse int
|
countUse int
|
||||||
doneCh chan struct{}
|
doneCh chan struct{}
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *heartBeatMock) CreateHeartbeat(ctx context.Context, heartbeat domainHub.CreateHeartbeatModel) error {
|
func (h *heartBeatMock) CreateHeartbeat(ctx context.Context, heartbeat domainHub.CreateHeartbeatModel) error {
|
||||||
|
h.mu.Lock()
|
||||||
h.countUse += 1
|
h.countUse += 1
|
||||||
|
err := h.err
|
||||||
|
h.mu.Unlock()
|
||||||
select {
|
select {
|
||||||
case h.doneCh <- struct{}{}:
|
case h.doneCh <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
type statusMock struct {
|
type statusMock struct {
|
||||||
|
mu sync.Mutex
|
||||||
online bool
|
online bool
|
||||||
doneCh chan struct{}
|
doneCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusMock) Offline() {
|
func (s *statusMock) Offline() {
|
||||||
|
s.mu.Lock()
|
||||||
s.online = false
|
s.online = false
|
||||||
|
s.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusMock) Online() {
|
func (s *statusMock) Online() {
|
||||||
|
s.mu.Lock()
|
||||||
s.online = true
|
s.online = true
|
||||||
|
s.mu.Unlock()
|
||||||
select {
|
select {
|
||||||
case s.doneCh <- struct{}{}:
|
case s.doneCh <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAgentConnection_Heartbeat(t *testing.T) {
|
func (s *statusMock) IsOnline() bool {
|
||||||
// Создаем вся поля для Agent Connection
|
s.mu.Lock()
|
||||||
// Нужно как то вынести в отдельную функцию
|
defer s.mu.Unlock()
|
||||||
sendStream := make(chan *pb.ServerCommandRequest, 1)
|
return s.online
|
||||||
recvStream := make(chan *pb.AgentEvent)
|
}
|
||||||
|
|
||||||
|
type agentTestHarness struct {
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
stream *streamMock
|
||||||
|
heartbeat *heartBeatMock
|
||||||
|
status *statusMock
|
||||||
|
agent *AgentConnection
|
||||||
|
recvCh chan *pb.AgentEvent
|
||||||
|
sendCh chan *pb.ServerCommandRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAgentTestHarness(t *testing.T, heartbeatTimeoutMS int) *agentTestHarness {
|
||||||
|
t.Helper()
|
||||||
|
sendStream := make(chan *pb.ServerCommandRequest, 4)
|
||||||
|
recvStream := make(chan *pb.AgentEvent, 4)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
stream := streamMock{recvCh: recvStream, sendCh: sendStream, ctx: ctx, closeCh: make(chan struct{}, 1)}
|
stream := &streamMock{recvCh: recvStream, sendCh: sendStream, ctx: ctx, closeCh: make(chan struct{}, 1)}
|
||||||
heartbeat := heartBeatMock{doneCh: make(chan struct{}, 1)}
|
heartbeat := &heartBeatMock{doneCh: make(chan struct{}, 2)}
|
||||||
status := statusMock{doneCh: make(chan struct{}, 1)}
|
status := &statusMock{doneCh: make(chan struct{}, 2)}
|
||||||
|
|
||||||
agent := newAgentConnection("123", &stream, &heartbeat, &status, 5000, zerolog.New(nil))
|
agent := newAgentConnection("123", stream, heartbeat, status, heartbeatTimeoutMS, zerolog.New(nil))
|
||||||
go agent.Listen()
|
|
||||||
|
|
||||||
recvStream <- &pb.AgentEvent{AgentId: "agent-1", Event: &pb.AgentEvent_Heartbeat{
|
t.Cleanup(func() {
|
||||||
|
cancel()
|
||||||
|
})
|
||||||
|
|
||||||
|
return &agentTestHarness{
|
||||||
|
ctx: ctx, cancel: cancel, stream: stream, heartbeat: heartbeat, status: status,
|
||||||
|
agent: agent, recvCh: recvStream, sendCh: sendStream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitFor(t *testing.T, ch <-chan struct{}, timeout time.Duration, message string) {
|
||||||
|
t.Helper()
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
case <-time.After(timeout):
|
||||||
|
t.Fatal(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForClose(t *testing.T, closeCh <-chan struct{}, timeout time.Duration) {
|
||||||
|
t.Helper()
|
||||||
|
select {
|
||||||
|
case <-closeCh:
|
||||||
|
case <-time.After(timeout):
|
||||||
|
t.Fatal("timeout waiting for close")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func commandResponseEvent(requestID, output string) *pb.AgentEvent {
|
||||||
|
return &pb.AgentEvent{
|
||||||
|
AgentId: "agent-1",
|
||||||
|
Event: &pb.AgentEvent_CommandResponse{
|
||||||
|
CommandResponse: &pb.CommandResponse{
|
||||||
|
RequestId: requestID,
|
||||||
|
Success: true,
|
||||||
|
Output: output,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgentConnection_Heartbeat(t *testing.T) {
|
||||||
|
h := newAgentTestHarness(t, 5000)
|
||||||
|
go h.agent.Listen()
|
||||||
|
|
||||||
|
h.recvCh <- &pb.AgentEvent{AgentId: "agent-1", Event: &pb.AgentEvent_Heartbeat{
|
||||||
Heartbeat: &pb.Heartbeat{
|
Heartbeat: &pb.Heartbeat{
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
Metrics: &pb.SystemMetrics{CpuUsage: 0.5, MemoryUsage: 0.3, DiskUsage: 0.7},
|
Metrics: &pb.SystemMetrics{CpuUsage: 0.5, MemoryUsage: 0.3, DiskUsage: 0.7},
|
||||||
}}}
|
}}}
|
||||||
|
|
||||||
select {
|
waitFor(t, h.heartbeat.doneCh, 500*time.Millisecond, "timeout waiting for heartbeat")
|
||||||
case <-heartbeat.doneCh:
|
waitFor(t, h.status.doneCh, 500*time.Millisecond, "timeout waiting for status online")
|
||||||
case <-time.After(500 * time.Millisecond):
|
|
||||||
t.Fatal("timeout waiting for heartbeat")
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
h.heartbeat.mu.Lock()
|
||||||
case <-status.doneCh:
|
count := h.heartbeat.countUse
|
||||||
case <-time.After(500 * time.Millisecond):
|
h.heartbeat.mu.Unlock()
|
||||||
t.Fatal("timeout waiting for status online")
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, heartbeat.countUse, 1)
|
assert.Equal(t, count, 1)
|
||||||
assert.Equal(t, status.online, true)
|
assert.Equal(t, h.status.IsOnline(), true)
|
||||||
|
|
||||||
cancel()
|
h.cancel()
|
||||||
|
waitForClose(t, h.stream.closeCh, 500*time.Millisecond)
|
||||||
select {
|
assert.Equal(t, h.status.IsOnline(), false)
|
||||||
case <-stream.closeCh:
|
|
||||||
case <-time.After(500 * time.Millisecond):
|
|
||||||
t.Fatal("timeout waiting for close")
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, status.online, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAgentConnection_Execute(t *testing.T) {
|
func TestAgentConnection_Execute(t *testing.T) {
|
||||||
sendStream := make(chan *pb.ServerCommandRequest, 1)
|
h := newAgentTestHarness(t, 5000)
|
||||||
recvStream := make(chan *pb.AgentEvent)
|
go h.agent.Listen()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
t.Cleanup(cancel)
|
|
||||||
|
|
||||||
stream := streamMock{recvCh: recvStream, sendCh: sendStream, ctx: ctx}
|
|
||||||
heartbeat := heartBeatMock{doneCh: make(chan struct{}, 1)}
|
|
||||||
status := statusMock{doneCh: make(chan struct{}, 1)}
|
|
||||||
|
|
||||||
agent := newAgentConnection("123", &stream, &heartbeat, &status, 5000, zerolog.New(nil))
|
|
||||||
go agent.Listen()
|
|
||||||
|
|
||||||
// Данные для проверки
|
// Данные для проверки
|
||||||
requestID := make(chan domainHub.AgentResponse)
|
requestID := make(chan domainHub.AgentResponse)
|
||||||
@@ -146,7 +215,7 @@ func TestAgentConnection_Execute(t *testing.T) {
|
|||||||
name := "test name"
|
name := "test name"
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
response, _ := agent.Execute(ctx, domainHub.AgentRequest{
|
response, _ := h.agent.Execute(h.ctx, domainHub.AgentRequest{
|
||||||
Name: name,
|
Name: name,
|
||||||
Args: nil,
|
Args: nil,
|
||||||
TimeOut: 0,
|
TimeOut: 0,
|
||||||
@@ -155,10 +224,10 @@ func TestAgentConnection_Execute(t *testing.T) {
|
|||||||
requestID <- response
|
requestID <- response
|
||||||
}()
|
}()
|
||||||
|
|
||||||
request := <-sendStream
|
request := <-h.sendCh
|
||||||
assert.Equal(t, name, request.Name)
|
assert.Equal(t, name, request.Name)
|
||||||
|
|
||||||
recvStream <- &pb.AgentEvent{AgentId: "agent-1", Event: &pb.AgentEvent_CommandResponse{
|
h.recvCh <- &pb.AgentEvent{AgentId: "agent-1", Event: &pb.AgentEvent_CommandResponse{
|
||||||
CommandResponse: &pb.CommandResponse{
|
CommandResponse: &pb.CommandResponse{
|
||||||
RequestId: request.RequestId,
|
RequestId: request.RequestId,
|
||||||
Success: true,
|
Success: true,
|
||||||
@@ -174,29 +243,19 @@ func TestAgentConnection_Execute(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Написать тест когда heartbeat не приходит и все закрывается
|
|
||||||
func TestAgentConnection_HeartbeatTimeout(t *testing.T) {
|
func TestAgentConnection_HeartbeatTimeout(t *testing.T) {
|
||||||
sendStream := make(chan *pb.ServerCommandRequest, 1)
|
h := newAgentTestHarness(t, 200)
|
||||||
recvStream := make(chan *pb.AgentEvent)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
t.Cleanup(cancel)
|
|
||||||
|
|
||||||
stream := streamMock{recvCh: recvStream, sendCh: sendStream, ctx: ctx, closeCh: make(chan struct{}, 1)}
|
|
||||||
heartbeat := heartBeatMock{doneCh: make(chan struct{}, 1)}
|
|
||||||
status := statusMock{doneCh: make(chan struct{}, 1)}
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
agent := newAgentConnection("123", &stream, &heartbeat, &status, 200, zerolog.New(nil))
|
|
||||||
|
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
go func() {
|
go func() {
|
||||||
err := agent.Listen()
|
err := h.agent.Listen()
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
_, err := agent.Execute(ctx, domainHub.AgentRequest{
|
_, err := h.agent.Execute(h.ctx, domainHub.AgentRequest{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Args: nil,
|
Args: nil,
|
||||||
TimeOut: 0,
|
TimeOut: 0,
|
||||||
@@ -207,11 +266,168 @@ func TestAgentConnection_HeartbeatTimeout(t *testing.T) {
|
|||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
select {
|
waitForClose(t, h.stream.closeCh, 500*time.Millisecond)
|
||||||
case <-stream.closeCh:
|
|
||||||
case <-time.After(500 * time.Millisecond):
|
|
||||||
t.Fatal("timeout waiting for close")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Написать тест при закрытии соединения Execute завершается
|
func TestAgentConnection_ConnectionClose(t *testing.T) {
|
||||||
|
h := newAgentTestHarness(t, 5000)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
wg.Add(2)
|
||||||
|
go func() {
|
||||||
|
err := h.agent.Listen()
|
||||||
|
assert.ErrorIs(t, err, context.Canceled)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
_, err := h.agent.Execute(context.Background(), domainHub.AgentRequest{
|
||||||
|
Name: "test",
|
||||||
|
Args: nil,
|
||||||
|
TimeOut: 0,
|
||||||
|
})
|
||||||
|
assert.ErrorIs(t, err, ConnectionCloseErr)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
h.cancel()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
waitForClose(t, h.stream.closeCh, 500*time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgentConnection_ExecuteClose(t *testing.T) {
|
||||||
|
h := newAgentTestHarness(t, 5000)
|
||||||
|
ctxExecute, cancelExecute := context.WithCancel(context.Background())
|
||||||
|
t.Cleanup(cancelExecute)
|
||||||
|
|
||||||
|
executeCh := make(chan struct{})
|
||||||
|
go h.agent.Listen()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
_, err := h.agent.Execute(ctxExecute, domainHub.AgentRequest{
|
||||||
|
Name: "test",
|
||||||
|
Args: nil,
|
||||||
|
TimeOut: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.ErrorIs(t, err, context.Canceled)
|
||||||
|
executeCh <- struct{}{}
|
||||||
|
}()
|
||||||
|
|
||||||
|
cancelExecute()
|
||||||
|
waitFor(t, executeCh, 500*time.Millisecond, "timeout waiting for execute close")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgentConnection_ListenEOF(t *testing.T) {
|
||||||
|
h := newAgentTestHarness(t, 5000)
|
||||||
|
h.stream.Close()
|
||||||
|
|
||||||
|
err := h.agent.Listen()
|
||||||
|
assert.NilError(t, err)
|
||||||
|
waitForClose(t, h.stream.closeCh, 500*time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgentConnection_ListenRecvError(t *testing.T) {
|
||||||
|
h := newAgentTestHarness(t, 5000)
|
||||||
|
|
||||||
|
recvErr := errors.New("recv failure")
|
||||||
|
h.stream.mu.Lock()
|
||||||
|
h.stream.recvErr = recvErr
|
||||||
|
h.stream.mu.Unlock()
|
||||||
|
|
||||||
|
err := h.agent.Listen()
|
||||||
|
assert.ErrorIs(t, err, recvErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgentConnection_ExecuteSendError(t *testing.T) {
|
||||||
|
h := newAgentTestHarness(t, 5000)
|
||||||
|
h.stream.mu.Lock()
|
||||||
|
h.stream.sendErr = errors.New("send failure")
|
||||||
|
h.stream.mu.Unlock()
|
||||||
|
|
||||||
|
_, err := h.agent.Execute(h.ctx, domainHub.AgentRequest{Name: "test"})
|
||||||
|
assert.ErrorContains(t, err, "execute command")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgentConnection_ExecuteContextCanceled(t *testing.T) {
|
||||||
|
h := newAgentTestHarness(t, 5000)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
_, err := h.agent.Execute(ctx, domainHub.AgentRequest{Name: "test"})
|
||||||
|
assert.ErrorIs(t, err, context.Canceled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgentConnection_ExecuteConnectionCanceled(t *testing.T) {
|
||||||
|
h := newAgentTestHarness(t, 5000)
|
||||||
|
h.cancel()
|
||||||
|
|
||||||
|
_, err := h.agent.Execute(context.Background(), domainHub.AgentRequest{Name: "test"})
|
||||||
|
assert.ErrorIs(t, err, ConnectionCloseErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgentConnection_UnknownResponseID(t *testing.T) {
|
||||||
|
h := newAgentTestHarness(t, 5000)
|
||||||
|
go h.agent.Listen()
|
||||||
|
|
||||||
|
h.recvCh <- &pb.AgentEvent{AgentId: "agent-1", Event: &pb.AgentEvent_CommandResponse{
|
||||||
|
CommandResponse: &pb.CommandResponse{
|
||||||
|
RequestId: "unknown",
|
||||||
|
Success: true,
|
||||||
|
Output: "ok",
|
||||||
|
}}}
|
||||||
|
|
||||||
|
h.cancel()
|
||||||
|
waitForClose(t, h.stream.closeCh, 500*time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgentConnection_HeartbeatErrorDoesNotStop(t *testing.T) {
|
||||||
|
h := newAgentTestHarness(t, 5000)
|
||||||
|
h.heartbeat.mu.Lock()
|
||||||
|
h.heartbeat.err = errors.New("db error")
|
||||||
|
h.heartbeat.mu.Unlock()
|
||||||
|
|
||||||
|
go h.agent.Listen()
|
||||||
|
h.recvCh <- &pb.AgentEvent{AgentId: "agent-1", Event: &pb.AgentEvent_Heartbeat{
|
||||||
|
Heartbeat: &pb.Heartbeat{
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
Metrics: &pb.SystemMetrics{CpuUsage: 0.2, MemoryUsage: 0.1, DiskUsage: 0.3},
|
||||||
|
}}}
|
||||||
|
|
||||||
|
waitFor(t, h.heartbeat.doneCh, 500*time.Millisecond, "timeout waiting for heartbeat")
|
||||||
|
h.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgentConnection_ConcurrentExecute(t *testing.T) {
|
||||||
|
h := newAgentTestHarness(t, 5000)
|
||||||
|
go h.agent.Listen()
|
||||||
|
|
||||||
|
responses := make(chan domainHub.AgentResponse, 2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
resp, _ := h.agent.Execute(h.ctx, domainHub.AgentRequest{Name: "cmd-1"})
|
||||||
|
responses <- resp
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
resp, _ := h.agent.Execute(h.ctx, domainHub.AgentRequest{Name: "cmd-2"})
|
||||||
|
responses <- resp
|
||||||
|
}()
|
||||||
|
|
||||||
|
first := <-h.sendCh
|
||||||
|
second := <-h.sendCh
|
||||||
|
|
||||||
|
// ответы приходят в обратном порядке
|
||||||
|
h.recvCh <- commandResponseEvent(second.RequestId, "second")
|
||||||
|
h.recvCh <- commandResponseEvent(first.RequestId, "first")
|
||||||
|
|
||||||
|
resp1 := <-responses
|
||||||
|
resp2 := <-responses
|
||||||
|
|
||||||
|
assert.Assert(t, resp1.Output == "first" || resp1.Output == "second")
|
||||||
|
assert.Assert(t, resp2.Output == "first" || resp2.Output == "second")
|
||||||
|
assert.Assert(t, resp1.Output != resp2.Output)
|
||||||
|
|
||||||
|
h.cancel()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user