From 8bc0a3ecfd34c504bcea89d3c775c7d8d3cead9f Mon Sep 17 00:00:00 2001
From: Lyuben Penkovski <lyuben.penkovski@vereign.com>
Date: Fri, 13 May 2022 15:32:33 +0300
Subject: [PATCH] Task service unit tests

* Create task unit tests
* Retrieve task results unit tests
---
 internal/service/task/service.go              |  13 +-
 internal/service/task/service_test.go         | 266 ++++++++++++++
 internal/service/task/taskfakes/fake_cache.go | 123 +++++++
 internal/service/task/taskfakes/fake_queue.go | 345 ++++++++++++++++++
 .../service/task/taskfakes/fake_storage.go    | 281 ++++++++++++++
 5 files changed, 1026 insertions(+), 2 deletions(-)
 create mode 100644 internal/service/task/service_test.go
 create mode 100644 internal/service/task/taskfakes/fake_cache.go
 create mode 100644 internal/service/task/taskfakes/fake_queue.go
 create mode 100644 internal/service/task/taskfakes/fake_storage.go

diff --git a/internal/service/task/service.go b/internal/service/task/service.go
index 098c7bb..a4ea25a 100644
--- a/internal/service/task/service.go
+++ b/internal/service/task/service.go
@@ -13,6 +13,10 @@ import (
 	goatask "code.vereign.com/gaiax/tsa/task/gen/task"
 )
 
+//go:generate counterfeiter . Storage
+//go:generate counterfeiter . Queue
+//go:generate counterfeiter . Cache
+
 // Storage for retrieving predefined task templates.
 type Storage interface {
 	TaskTemplate(ctx context.Context, taskName string) (*Task, error)
@@ -51,6 +55,10 @@ func New(template Storage, queue Queue, cache Cache, logger *zap.Logger) *Servic
 
 // Create a new task and put it in a Queue for later execution.
 func (s *Service) Create(ctx context.Context, req *goatask.CreateRequest) (res *goatask.CreateResult, err error) {
+	if req.TaskName == "" {
+		return nil, errors.New(errors.BadRequest, "missing taskName")
+	}
+
 	logger := s.logger.With(zap.String("taskName", req.TaskName))
 
 	// get predefined task definition from storage
@@ -105,9 +113,10 @@ func (s *Service) TaskResult(ctx context.Context, req *goatask.TaskResultRequest
 	if task == nil {
 		task, err = s.storage.Task(ctx, req.TaskID)
 		if err != nil {
-			if !errors.Is(errors.NotFound, err) {
-				logger.Error("error getting task from history collection", zap.Error(err))
+			if errors.Is(errors.NotFound, err) {
+				return nil, errors.New("task is not found", err)
 			}
+			logger.Error("error getting task from history collection", zap.Error(err))
 			return nil, err
 		}
 	}
diff --git a/internal/service/task/service_test.go b/internal/service/task/service_test.go
new file mode 100644
index 0000000..7005142
--- /dev/null
+++ b/internal/service/task/service_test.go
@@ -0,0 +1,266 @@
+package task_test
+
+import (
+	"context"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"go.uber.org/zap"
+
+	"code.vereign.com/gaiax/tsa/golib/errors"
+	"code.vereign.com/gaiax/tsa/golib/ptr"
+	goatask "code.vereign.com/gaiax/tsa/task/gen/task"
+	"code.vereign.com/gaiax/tsa/task/internal/service/task"
+	"code.vereign.com/gaiax/tsa/task/internal/service/task/taskfakes"
+)
+
+func TestNew(t *testing.T) {
+	svc := task.New(nil, nil, nil, zap.NewNop())
+	assert.Implements(t, (*goatask.Service)(nil), svc)
+}
+
+func TestService_Create(t *testing.T) {
+	tests := []struct {
+		name    string
+		req     *goatask.CreateRequest
+		storage *taskfakes.FakeStorage
+		queue   *taskfakes.FakeQueue
+		cache   *taskfakes.FakeCache
+
+		errkind errors.Kind
+		errtext string
+	}{
+		{
+			name:    "empty task name",
+			req:     &goatask.CreateRequest{},
+			errkind: errors.BadRequest,
+			errtext: "missing taskName",
+		},
+		{
+			name: "task template not found",
+			req:  &goatask.CreateRequest{TaskName: "taskname"},
+			storage: &taskfakes.FakeStorage{
+				TaskTemplateStub: func(ctx context.Context, taskName string) (*task.Task, error) {
+					return nil, errors.New(errors.NotFound)
+				},
+			},
+			errkind: errors.NotFound,
+			errtext: "not found",
+		},
+		{
+			name: "fail to add task to queue",
+			req:  &goatask.CreateRequest{TaskName: "taskname"},
+			storage: &taskfakes.FakeStorage{
+				TaskTemplateStub: func(ctx context.Context, taskName string) (*task.Task, error) {
+					return &task.Task{}, nil
+				},
+			},
+			queue: &taskfakes.FakeQueue{
+				AddStub: func(ctx context.Context, t *task.Task) error {
+					return errors.New("some error")
+				},
+			},
+			errkind: errors.Unknown,
+			errtext: "some error",
+		},
+		{
+			name: "successfully add task to queue",
+			req:  &goatask.CreateRequest{TaskName: "taskname"},
+			storage: &taskfakes.FakeStorage{
+				TaskTemplateStub: func(ctx context.Context, taskName string) (*task.Task, error) {
+					return &task.Task{}, nil
+				},
+			},
+			queue: &taskfakes.FakeQueue{
+				AddStub: func(ctx context.Context, t *task.Task) error {
+					return nil
+				},
+			},
+		},
+		{
+			name: "successfully add task to queue with namespace and scope",
+			req: &goatask.CreateRequest{
+				TaskName:       "taskname",
+				CacheNamespace: ptr.String("login"),
+				CacheScope:     ptr.String("user"),
+			},
+			storage: &taskfakes.FakeStorage{
+				TaskTemplateStub: func(ctx context.Context, taskName string) (*task.Task, error) {
+					return &task.Task{}, nil
+				},
+			},
+			queue: &taskfakes.FakeQueue{
+				AddStub: func(ctx context.Context, t *task.Task) error {
+					return nil
+				},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			svc := task.New(test.storage, test.queue, test.cache, zap.NewNop())
+			res, err := svc.Create(context.Background(), test.req)
+			if err != nil {
+				assert.NotEmpty(t, test.errtext)
+				e, ok := err.(*errors.Error)
+				assert.True(t, ok)
+				assert.Equal(t, test.errkind, e.Kind)
+				assert.Contains(t, e.Error(), test.errtext)
+				assert.Nil(t, res)
+			} else {
+				assert.Empty(t, test.errtext)
+				assert.NotNil(t, res)
+				assert.NotEmpty(t, res.TaskID)
+			}
+		})
+	}
+}
+
+func TestService_TaskResult(t *testing.T) {
+	tests := []struct {
+		name    string
+		req     *goatask.TaskResultRequest
+		storage *taskfakes.FakeStorage
+		cache   *taskfakes.FakeCache
+
+		res     interface{}
+		errkind errors.Kind
+		errtext string
+	}{
+		{
+			name:    "missing taskID",
+			req:     &goatask.TaskResultRequest{},
+			errkind: errors.BadRequest,
+			errtext: "missing taskID",
+		},
+		{
+			name: "error getting task history from storage",
+			req:  &goatask.TaskResultRequest{TaskID: "123"},
+			storage: &taskfakes.FakeStorage{
+				TaskHistoryStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return nil, errors.New("some error")
+				},
+			},
+			errkind: errors.Unknown,
+			errtext: "some error",
+		},
+		{
+			name: "task not found in history and fail to retrieve it from tasks queue collection too",
+			req:  &goatask.TaskResultRequest{TaskID: "123"},
+			storage: &taskfakes.FakeStorage{
+				TaskHistoryStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return nil, errors.New(errors.NotFound)
+				},
+				TaskStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return nil, errors.New("another error")
+				},
+			},
+			errkind: errors.Unknown,
+			errtext: "another error",
+		},
+		{
+			name: "task not found neither in history nor in tasks queue collection",
+			req:  &goatask.TaskResultRequest{TaskID: "123"},
+			storage: &taskfakes.FakeStorage{
+				TaskHistoryStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return nil, errors.New(errors.NotFound)
+				},
+				TaskStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return nil, errors.New(errors.NotFound)
+				},
+			},
+			errkind: errors.NotFound,
+			errtext: "task is not found",
+		},
+		{
+			name: "task is not yet completed",
+			req:  &goatask.TaskResultRequest{TaskID: "123"},
+			storage: &taskfakes.FakeStorage{
+				TaskHistoryStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return nil, errors.New(errors.NotFound)
+				},
+				TaskStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return &task.Task{State: task.Pending}, nil
+				},
+			},
+			errkind: errors.NotFound,
+			errtext: "no result, task is not completed",
+		},
+		{
+			name: "error getting task result from cache",
+			req:  &goatask.TaskResultRequest{TaskID: "123"},
+			storage: &taskfakes.FakeStorage{
+				TaskHistoryStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return nil, errors.New(errors.NotFound)
+				},
+				TaskStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return &task.Task{State: task.Done}, nil
+				},
+			},
+			cache: &taskfakes.FakeCache{
+				GetStub: func(ctx context.Context, key string, ns string, scope string) ([]byte, error) {
+					return nil, errors.New("cache error")
+				},
+			},
+			errkind: errors.Unknown,
+			errtext: "cache error",
+		},
+		{
+			name: "getting invalid JSON result from cache",
+			req:  &goatask.TaskResultRequest{TaskID: "123"},
+			storage: &taskfakes.FakeStorage{
+				TaskHistoryStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return nil, errors.New(errors.NotFound)
+				},
+				TaskStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return &task.Task{State: task.Done}, nil
+				},
+			},
+			cache: &taskfakes.FakeCache{
+				GetStub: func(ctx context.Context, key string, ns string, scope string) ([]byte, error) {
+					return []byte("asdfads"), nil
+				},
+			},
+			errkind: errors.Unknown,
+			errtext: "error decoding result from cache",
+		},
+		{
+			name: "get task result successfully",
+			req:  &goatask.TaskResultRequest{TaskID: "123"},
+			storage: &taskfakes.FakeStorage{
+				TaskHistoryStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return nil, errors.New(errors.NotFound)
+				},
+				TaskStub: func(ctx context.Context, taskID string) (*task.Task, error) {
+					return &task.Task{State: task.Done}, nil
+				},
+			},
+			cache: &taskfakes.FakeCache{
+				GetStub: func(ctx context.Context, key string, ns string, scope string) ([]byte, error) {
+					return []byte(`{"result":"success"}`), nil
+				},
+			},
+			res: map[string]interface{}{"result": "success"},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			svc := task.New(test.storage, nil, test.cache, zap.NewNop())
+			res, err := svc.TaskResult(context.Background(), test.req)
+			if err != nil {
+				assert.NotEmpty(t, test.errtext)
+				e, ok := err.(*errors.Error)
+				assert.True(t, ok)
+				assert.Equal(t, test.errkind, e.Kind)
+				assert.Contains(t, e.Error(), test.errtext)
+				assert.Nil(t, res)
+			} else {
+				assert.Empty(t, test.errtext)
+				assert.NotNil(t, res)
+				assert.Equal(t, test.res, res)
+			}
+		})
+	}
+}
diff --git a/internal/service/task/taskfakes/fake_cache.go b/internal/service/task/taskfakes/fake_cache.go
new file mode 100644
index 0000000..d08e54e
--- /dev/null
+++ b/internal/service/task/taskfakes/fake_cache.go
@@ -0,0 +1,123 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package taskfakes
+
+import (
+	"context"
+	"sync"
+
+	"code.vereign.com/gaiax/tsa/task/internal/service/task"
+)
+
+type FakeCache struct {
+	GetStub        func(context.Context, string, string, string) ([]byte, error)
+	getMutex       sync.RWMutex
+	getArgsForCall []struct {
+		arg1 context.Context
+		arg2 string
+		arg3 string
+		arg4 string
+	}
+	getReturns struct {
+		result1 []byte
+		result2 error
+	}
+	getReturnsOnCall map[int]struct {
+		result1 []byte
+		result2 error
+	}
+	invocations      map[string][][]interface{}
+	invocationsMutex sync.RWMutex
+}
+
+func (fake *FakeCache) Get(arg1 context.Context, arg2 string, arg3 string, arg4 string) ([]byte, error) {
+	fake.getMutex.Lock()
+	ret, specificReturn := fake.getReturnsOnCall[len(fake.getArgsForCall)]
+	fake.getArgsForCall = append(fake.getArgsForCall, struct {
+		arg1 context.Context
+		arg2 string
+		arg3 string
+		arg4 string
+	}{arg1, arg2, arg3, arg4})
+	stub := fake.GetStub
+	fakeReturns := fake.getReturns
+	fake.recordInvocation("Get", []interface{}{arg1, arg2, arg3, arg4})
+	fake.getMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2, arg3, arg4)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *FakeCache) GetCallCount() int {
+	fake.getMutex.RLock()
+	defer fake.getMutex.RUnlock()
+	return len(fake.getArgsForCall)
+}
+
+func (fake *FakeCache) GetCalls(stub func(context.Context, string, string, string) ([]byte, error)) {
+	fake.getMutex.Lock()
+	defer fake.getMutex.Unlock()
+	fake.GetStub = stub
+}
+
+func (fake *FakeCache) GetArgsForCall(i int) (context.Context, string, string, string) {
+	fake.getMutex.RLock()
+	defer fake.getMutex.RUnlock()
+	argsForCall := fake.getArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
+}
+
+func (fake *FakeCache) GetReturns(result1 []byte, result2 error) {
+	fake.getMutex.Lock()
+	defer fake.getMutex.Unlock()
+	fake.GetStub = nil
+	fake.getReturns = struct {
+		result1 []byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeCache) GetReturnsOnCall(i int, result1 []byte, result2 error) {
+	fake.getMutex.Lock()
+	defer fake.getMutex.Unlock()
+	fake.GetStub = nil
+	if fake.getReturnsOnCall == nil {
+		fake.getReturnsOnCall = make(map[int]struct {
+			result1 []byte
+			result2 error
+		})
+	}
+	fake.getReturnsOnCall[i] = struct {
+		result1 []byte
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeCache) Invocations() map[string][][]interface{} {
+	fake.invocationsMutex.RLock()
+	defer fake.invocationsMutex.RUnlock()
+	fake.getMutex.RLock()
+	defer fake.getMutex.RUnlock()
+	copiedInvocations := map[string][][]interface{}{}
+	for key, value := range fake.invocations {
+		copiedInvocations[key] = value
+	}
+	return copiedInvocations
+}
+
+func (fake *FakeCache) recordInvocation(key string, args []interface{}) {
+	fake.invocationsMutex.Lock()
+	defer fake.invocationsMutex.Unlock()
+	if fake.invocations == nil {
+		fake.invocations = map[string][][]interface{}{}
+	}
+	if fake.invocations[key] == nil {
+		fake.invocations[key] = [][]interface{}{}
+	}
+	fake.invocations[key] = append(fake.invocations[key], args)
+}
+
+var _ task.Cache = new(FakeCache)
diff --git a/internal/service/task/taskfakes/fake_queue.go b/internal/service/task/taskfakes/fake_queue.go
new file mode 100644
index 0000000..3366db2
--- /dev/null
+++ b/internal/service/task/taskfakes/fake_queue.go
@@ -0,0 +1,345 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package taskfakes
+
+import (
+	"context"
+	"sync"
+
+	"code.vereign.com/gaiax/tsa/task/internal/service/task"
+)
+
+type FakeQueue struct {
+	AckStub        func(context.Context, *task.Task) error
+	ackMutex       sync.RWMutex
+	ackArgsForCall []struct {
+		arg1 context.Context
+		arg2 *task.Task
+	}
+	ackReturns struct {
+		result1 error
+	}
+	ackReturnsOnCall map[int]struct {
+		result1 error
+	}
+	AddStub        func(context.Context, *task.Task) error
+	addMutex       sync.RWMutex
+	addArgsForCall []struct {
+		arg1 context.Context
+		arg2 *task.Task
+	}
+	addReturns struct {
+		result1 error
+	}
+	addReturnsOnCall map[int]struct {
+		result1 error
+	}
+	PollStub        func(context.Context) (*task.Task, error)
+	pollMutex       sync.RWMutex
+	pollArgsForCall []struct {
+		arg1 context.Context
+	}
+	pollReturns struct {
+		result1 *task.Task
+		result2 error
+	}
+	pollReturnsOnCall map[int]struct {
+		result1 *task.Task
+		result2 error
+	}
+	UnackStub        func(context.Context, *task.Task) error
+	unackMutex       sync.RWMutex
+	unackArgsForCall []struct {
+		arg1 context.Context
+		arg2 *task.Task
+	}
+	unackReturns struct {
+		result1 error
+	}
+	unackReturnsOnCall map[int]struct {
+		result1 error
+	}
+	invocations      map[string][][]interface{}
+	invocationsMutex sync.RWMutex
+}
+
+func (fake *FakeQueue) Ack(arg1 context.Context, arg2 *task.Task) error {
+	fake.ackMutex.Lock()
+	ret, specificReturn := fake.ackReturnsOnCall[len(fake.ackArgsForCall)]
+	fake.ackArgsForCall = append(fake.ackArgsForCall, struct {
+		arg1 context.Context
+		arg2 *task.Task
+	}{arg1, arg2})
+	stub := fake.AckStub
+	fakeReturns := fake.ackReturns
+	fake.recordInvocation("Ack", []interface{}{arg1, arg2})
+	fake.ackMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeQueue) AckCallCount() int {
+	fake.ackMutex.RLock()
+	defer fake.ackMutex.RUnlock()
+	return len(fake.ackArgsForCall)
+}
+
+func (fake *FakeQueue) AckCalls(stub func(context.Context, *task.Task) error) {
+	fake.ackMutex.Lock()
+	defer fake.ackMutex.Unlock()
+	fake.AckStub = stub
+}
+
+func (fake *FakeQueue) AckArgsForCall(i int) (context.Context, *task.Task) {
+	fake.ackMutex.RLock()
+	defer fake.ackMutex.RUnlock()
+	argsForCall := fake.ackArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *FakeQueue) AckReturns(result1 error) {
+	fake.ackMutex.Lock()
+	defer fake.ackMutex.Unlock()
+	fake.AckStub = nil
+	fake.ackReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeQueue) AckReturnsOnCall(i int, result1 error) {
+	fake.ackMutex.Lock()
+	defer fake.ackMutex.Unlock()
+	fake.AckStub = nil
+	if fake.ackReturnsOnCall == nil {
+		fake.ackReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.ackReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeQueue) Add(arg1 context.Context, arg2 *task.Task) error {
+	fake.addMutex.Lock()
+	ret, specificReturn := fake.addReturnsOnCall[len(fake.addArgsForCall)]
+	fake.addArgsForCall = append(fake.addArgsForCall, struct {
+		arg1 context.Context
+		arg2 *task.Task
+	}{arg1, arg2})
+	stub := fake.AddStub
+	fakeReturns := fake.addReturns
+	fake.recordInvocation("Add", []interface{}{arg1, arg2})
+	fake.addMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeQueue) AddCallCount() int {
+	fake.addMutex.RLock()
+	defer fake.addMutex.RUnlock()
+	return len(fake.addArgsForCall)
+}
+
+func (fake *FakeQueue) AddCalls(stub func(context.Context, *task.Task) error) {
+	fake.addMutex.Lock()
+	defer fake.addMutex.Unlock()
+	fake.AddStub = stub
+}
+
+func (fake *FakeQueue) AddArgsForCall(i int) (context.Context, *task.Task) {
+	fake.addMutex.RLock()
+	defer fake.addMutex.RUnlock()
+	argsForCall := fake.addArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *FakeQueue) AddReturns(result1 error) {
+	fake.addMutex.Lock()
+	defer fake.addMutex.Unlock()
+	fake.AddStub = nil
+	fake.addReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeQueue) AddReturnsOnCall(i int, result1 error) {
+	fake.addMutex.Lock()
+	defer fake.addMutex.Unlock()
+	fake.AddStub = nil
+	if fake.addReturnsOnCall == nil {
+		fake.addReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.addReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeQueue) Poll(arg1 context.Context) (*task.Task, error) {
+	fake.pollMutex.Lock()
+	ret, specificReturn := fake.pollReturnsOnCall[len(fake.pollArgsForCall)]
+	fake.pollArgsForCall = append(fake.pollArgsForCall, struct {
+		arg1 context.Context
+	}{arg1})
+	stub := fake.PollStub
+	fakeReturns := fake.pollReturns
+	fake.recordInvocation("Poll", []interface{}{arg1})
+	fake.pollMutex.Unlock()
+	if stub != nil {
+		return stub(arg1)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *FakeQueue) PollCallCount() int {
+	fake.pollMutex.RLock()
+	defer fake.pollMutex.RUnlock()
+	return len(fake.pollArgsForCall)
+}
+
+func (fake *FakeQueue) PollCalls(stub func(context.Context) (*task.Task, error)) {
+	fake.pollMutex.Lock()
+	defer fake.pollMutex.Unlock()
+	fake.PollStub = stub
+}
+
+func (fake *FakeQueue) PollArgsForCall(i int) context.Context {
+	fake.pollMutex.RLock()
+	defer fake.pollMutex.RUnlock()
+	argsForCall := fake.pollArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *FakeQueue) PollReturns(result1 *task.Task, result2 error) {
+	fake.pollMutex.Lock()
+	defer fake.pollMutex.Unlock()
+	fake.PollStub = nil
+	fake.pollReturns = struct {
+		result1 *task.Task
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeQueue) PollReturnsOnCall(i int, result1 *task.Task, result2 error) {
+	fake.pollMutex.Lock()
+	defer fake.pollMutex.Unlock()
+	fake.PollStub = nil
+	if fake.pollReturnsOnCall == nil {
+		fake.pollReturnsOnCall = make(map[int]struct {
+			result1 *task.Task
+			result2 error
+		})
+	}
+	fake.pollReturnsOnCall[i] = struct {
+		result1 *task.Task
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeQueue) Unack(arg1 context.Context, arg2 *task.Task) error {
+	fake.unackMutex.Lock()
+	ret, specificReturn := fake.unackReturnsOnCall[len(fake.unackArgsForCall)]
+	fake.unackArgsForCall = append(fake.unackArgsForCall, struct {
+		arg1 context.Context
+		arg2 *task.Task
+	}{arg1, arg2})
+	stub := fake.UnackStub
+	fakeReturns := fake.unackReturns
+	fake.recordInvocation("Unack", []interface{}{arg1, arg2})
+	fake.unackMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeQueue) UnackCallCount() int {
+	fake.unackMutex.RLock()
+	defer fake.unackMutex.RUnlock()
+	return len(fake.unackArgsForCall)
+}
+
+func (fake *FakeQueue) UnackCalls(stub func(context.Context, *task.Task) error) {
+	fake.unackMutex.Lock()
+	defer fake.unackMutex.Unlock()
+	fake.UnackStub = stub
+}
+
+func (fake *FakeQueue) UnackArgsForCall(i int) (context.Context, *task.Task) {
+	fake.unackMutex.RLock()
+	defer fake.unackMutex.RUnlock()
+	argsForCall := fake.unackArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *FakeQueue) UnackReturns(result1 error) {
+	fake.unackMutex.Lock()
+	defer fake.unackMutex.Unlock()
+	fake.UnackStub = nil
+	fake.unackReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeQueue) UnackReturnsOnCall(i int, result1 error) {
+	fake.unackMutex.Lock()
+	defer fake.unackMutex.Unlock()
+	fake.UnackStub = nil
+	if fake.unackReturnsOnCall == nil {
+		fake.unackReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.unackReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeQueue) Invocations() map[string][][]interface{} {
+	fake.invocationsMutex.RLock()
+	defer fake.invocationsMutex.RUnlock()
+	fake.ackMutex.RLock()
+	defer fake.ackMutex.RUnlock()
+	fake.addMutex.RLock()
+	defer fake.addMutex.RUnlock()
+	fake.pollMutex.RLock()
+	defer fake.pollMutex.RUnlock()
+	fake.unackMutex.RLock()
+	defer fake.unackMutex.RUnlock()
+	copiedInvocations := map[string][][]interface{}{}
+	for key, value := range fake.invocations {
+		copiedInvocations[key] = value
+	}
+	return copiedInvocations
+}
+
+func (fake *FakeQueue) recordInvocation(key string, args []interface{}) {
+	fake.invocationsMutex.Lock()
+	defer fake.invocationsMutex.Unlock()
+	if fake.invocations == nil {
+		fake.invocations = map[string][][]interface{}{}
+	}
+	if fake.invocations[key] == nil {
+		fake.invocations[key] = [][]interface{}{}
+	}
+	fake.invocations[key] = append(fake.invocations[key], args)
+}
+
+var _ task.Queue = new(FakeQueue)
diff --git a/internal/service/task/taskfakes/fake_storage.go b/internal/service/task/taskfakes/fake_storage.go
new file mode 100644
index 0000000..1570d8b
--- /dev/null
+++ b/internal/service/task/taskfakes/fake_storage.go
@@ -0,0 +1,281 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package taskfakes
+
+import (
+	"context"
+	"sync"
+
+	"code.vereign.com/gaiax/tsa/task/internal/service/task"
+)
+
+type FakeStorage struct {
+	TaskStub        func(context.Context, string) (*task.Task, error)
+	taskMutex       sync.RWMutex
+	taskArgsForCall []struct {
+		arg1 context.Context
+		arg2 string
+	}
+	taskReturns struct {
+		result1 *task.Task
+		result2 error
+	}
+	taskReturnsOnCall map[int]struct {
+		result1 *task.Task
+		result2 error
+	}
+	TaskHistoryStub        func(context.Context, string) (*task.Task, error)
+	taskHistoryMutex       sync.RWMutex
+	taskHistoryArgsForCall []struct {
+		arg1 context.Context
+		arg2 string
+	}
+	taskHistoryReturns struct {
+		result1 *task.Task
+		result2 error
+	}
+	taskHistoryReturnsOnCall map[int]struct {
+		result1 *task.Task
+		result2 error
+	}
+	TaskTemplateStub        func(context.Context, string) (*task.Task, error)
+	taskTemplateMutex       sync.RWMutex
+	taskTemplateArgsForCall []struct {
+		arg1 context.Context
+		arg2 string
+	}
+	taskTemplateReturns struct {
+		result1 *task.Task
+		result2 error
+	}
+	taskTemplateReturnsOnCall map[int]struct {
+		result1 *task.Task
+		result2 error
+	}
+	invocations      map[string][][]interface{}
+	invocationsMutex sync.RWMutex
+}
+
+func (fake *FakeStorage) Task(arg1 context.Context, arg2 string) (*task.Task, error) {
+	fake.taskMutex.Lock()
+	ret, specificReturn := fake.taskReturnsOnCall[len(fake.taskArgsForCall)]
+	fake.taskArgsForCall = append(fake.taskArgsForCall, struct {
+		arg1 context.Context
+		arg2 string
+	}{arg1, arg2})
+	stub := fake.TaskStub
+	fakeReturns := fake.taskReturns
+	fake.recordInvocation("Task", []interface{}{arg1, arg2})
+	fake.taskMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *FakeStorage) TaskCallCount() int {
+	fake.taskMutex.RLock()
+	defer fake.taskMutex.RUnlock()
+	return len(fake.taskArgsForCall)
+}
+
+func (fake *FakeStorage) TaskCalls(stub func(context.Context, string) (*task.Task, error)) {
+	fake.taskMutex.Lock()
+	defer fake.taskMutex.Unlock()
+	fake.TaskStub = stub
+}
+
+func (fake *FakeStorage) TaskArgsForCall(i int) (context.Context, string) {
+	fake.taskMutex.RLock()
+	defer fake.taskMutex.RUnlock()
+	argsForCall := fake.taskArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *FakeStorage) TaskReturns(result1 *task.Task, result2 error) {
+	fake.taskMutex.Lock()
+	defer fake.taskMutex.Unlock()
+	fake.TaskStub = nil
+	fake.taskReturns = struct {
+		result1 *task.Task
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeStorage) TaskReturnsOnCall(i int, result1 *task.Task, result2 error) {
+	fake.taskMutex.Lock()
+	defer fake.taskMutex.Unlock()
+	fake.TaskStub = nil
+	if fake.taskReturnsOnCall == nil {
+		fake.taskReturnsOnCall = make(map[int]struct {
+			result1 *task.Task
+			result2 error
+		})
+	}
+	fake.taskReturnsOnCall[i] = struct {
+		result1 *task.Task
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeStorage) TaskHistory(arg1 context.Context, arg2 string) (*task.Task, error) {
+	fake.taskHistoryMutex.Lock()
+	ret, specificReturn := fake.taskHistoryReturnsOnCall[len(fake.taskHistoryArgsForCall)]
+	fake.taskHistoryArgsForCall = append(fake.taskHistoryArgsForCall, struct {
+		arg1 context.Context
+		arg2 string
+	}{arg1, arg2})
+	stub := fake.TaskHistoryStub
+	fakeReturns := fake.taskHistoryReturns
+	fake.recordInvocation("TaskHistory", []interface{}{arg1, arg2})
+	fake.taskHistoryMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *FakeStorage) TaskHistoryCallCount() int {
+	fake.taskHistoryMutex.RLock()
+	defer fake.taskHistoryMutex.RUnlock()
+	return len(fake.taskHistoryArgsForCall)
+}
+
+func (fake *FakeStorage) TaskHistoryCalls(stub func(context.Context, string) (*task.Task, error)) {
+	fake.taskHistoryMutex.Lock()
+	defer fake.taskHistoryMutex.Unlock()
+	fake.TaskHistoryStub = stub
+}
+
+func (fake *FakeStorage) TaskHistoryArgsForCall(i int) (context.Context, string) {
+	fake.taskHistoryMutex.RLock()
+	defer fake.taskHistoryMutex.RUnlock()
+	argsForCall := fake.taskHistoryArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *FakeStorage) TaskHistoryReturns(result1 *task.Task, result2 error) {
+	fake.taskHistoryMutex.Lock()
+	defer fake.taskHistoryMutex.Unlock()
+	fake.TaskHistoryStub = nil
+	fake.taskHistoryReturns = struct {
+		result1 *task.Task
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeStorage) TaskHistoryReturnsOnCall(i int, result1 *task.Task, result2 error) {
+	fake.taskHistoryMutex.Lock()
+	defer fake.taskHistoryMutex.Unlock()
+	fake.TaskHistoryStub = nil
+	if fake.taskHistoryReturnsOnCall == nil {
+		fake.taskHistoryReturnsOnCall = make(map[int]struct {
+			result1 *task.Task
+			result2 error
+		})
+	}
+	fake.taskHistoryReturnsOnCall[i] = struct {
+		result1 *task.Task
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeStorage) TaskTemplate(arg1 context.Context, arg2 string) (*task.Task, error) {
+	fake.taskTemplateMutex.Lock()
+	ret, specificReturn := fake.taskTemplateReturnsOnCall[len(fake.taskTemplateArgsForCall)]
+	fake.taskTemplateArgsForCall = append(fake.taskTemplateArgsForCall, struct {
+		arg1 context.Context
+		arg2 string
+	}{arg1, arg2})
+	stub := fake.TaskTemplateStub
+	fakeReturns := fake.taskTemplateReturns
+	fake.recordInvocation("TaskTemplate", []interface{}{arg1, arg2})
+	fake.taskTemplateMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *FakeStorage) TaskTemplateCallCount() int {
+	fake.taskTemplateMutex.RLock()
+	defer fake.taskTemplateMutex.RUnlock()
+	return len(fake.taskTemplateArgsForCall)
+}
+
+func (fake *FakeStorage) TaskTemplateCalls(stub func(context.Context, string) (*task.Task, error)) {
+	fake.taskTemplateMutex.Lock()
+	defer fake.taskTemplateMutex.Unlock()
+	fake.TaskTemplateStub = stub
+}
+
+func (fake *FakeStorage) TaskTemplateArgsForCall(i int) (context.Context, string) {
+	fake.taskTemplateMutex.RLock()
+	defer fake.taskTemplateMutex.RUnlock()
+	argsForCall := fake.taskTemplateArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *FakeStorage) TaskTemplateReturns(result1 *task.Task, result2 error) {
+	fake.taskTemplateMutex.Lock()
+	defer fake.taskTemplateMutex.Unlock()
+	fake.TaskTemplateStub = nil
+	fake.taskTemplateReturns = struct {
+		result1 *task.Task
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeStorage) TaskTemplateReturnsOnCall(i int, result1 *task.Task, result2 error) {
+	fake.taskTemplateMutex.Lock()
+	defer fake.taskTemplateMutex.Unlock()
+	fake.TaskTemplateStub = nil
+	if fake.taskTemplateReturnsOnCall == nil {
+		fake.taskTemplateReturnsOnCall = make(map[int]struct {
+			result1 *task.Task
+			result2 error
+		})
+	}
+	fake.taskTemplateReturnsOnCall[i] = struct {
+		result1 *task.Task
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeStorage) Invocations() map[string][][]interface{} {
+	fake.invocationsMutex.RLock()
+	defer fake.invocationsMutex.RUnlock()
+	fake.taskMutex.RLock()
+	defer fake.taskMutex.RUnlock()
+	fake.taskHistoryMutex.RLock()
+	defer fake.taskHistoryMutex.RUnlock()
+	fake.taskTemplateMutex.RLock()
+	defer fake.taskTemplateMutex.RUnlock()
+	copiedInvocations := map[string][][]interface{}{}
+	for key, value := range fake.invocations {
+		copiedInvocations[key] = value
+	}
+	return copiedInvocations
+}
+
+func (fake *FakeStorage) recordInvocation(key string, args []interface{}) {
+	fake.invocationsMutex.Lock()
+	defer fake.invocationsMutex.Unlock()
+	if fake.invocations == nil {
+		fake.invocations = map[string][][]interface{}{}
+	}
+	if fake.invocations[key] == nil {
+		fake.invocations[key] = [][]interface{}{}
+	}
+	fake.invocations[key] = append(fake.invocations[key], args)
+}
+
+var _ task.Storage = new(FakeStorage)
-- 
GitLab