файл с функцией 
package event_group_lookup

import (
	"encoding/json"

	"go.avito.ru/bill/service-billing-processing/internal/domain"
	"go.avito.ru/bill/service-billing-processing/internal/generated/rpc/service/dto"
	"go.avito.ru/bill/service-billing-processing/internal/repository/entity"
	"go.avito.ru/bill/service-billing-processing/pkg/gtc"
)

func commonEventToHistoryEvent(e *entity.CommonEvent) (dto.HistoryEvent, error) {
	statusType, err := domain.StatusType{}.FromValue(gtc.Int16.ValueToPointer(int16(e.StatusID)))
	if err != nil {
		return dto.HistoryEvent{}, err
	}
	eventCtx, err := json.Marshal(e.Context)
	if err != nil {
		return dto.HistoryEvent{}, err
	}

	return dto.HistoryEvent{
		ExternalID:   e.ExternalID,
		OperationID:  gtc.Int64.SqlToPointer(e.OperationID),
		UserID:       e.UserID,
		Kind:         e.Kind,
		Status:       statusType.Alias(),
		ServiceID:    int64(e.ServiceID),
		AmountRUB:    e.AmountRUB,
		AmountBonus:  e.AmountBonus,
		MaxBonusRate: int64(e.MaxBonusRate),
		Context:      string(eventCtx),
		AppliedAt:    gtc.Time.SqlToPointer(e.AppliedAt),
		UpdatedAt:    e.UpdatedAt,
	}, nil
}


файл с логикой

package event_group_lookup

import (
	"context"

	rpcprotocol "go.avito.ru/gl/rpc-protocol"

	"go.avito.ru/bill/service-billing-processing/internal/domain"
	"go.avito.ru/bill/service-billing-processing/internal/generated/rpc/service/dto"
	"go.avito.ru/bill/service-billing-processing/internal/service"
	"go.avito.ru/bill/service-billing-processing/internal/service/model"
	"go.avito.ru/bill/service-billing-processing/pkg/logformatter"
)

type Handler struct {
	eventHistoryService service.EventHistory
}

func New(eventHistoryService service.EventHistory) *Handler {
	return &Handler{
		eventHistoryService: eventHistoryService,
	}
}

// Handle Поиск группы событий
func (h *Handler) Handle(ctx context.Context, in *dto.EventGroupLookupIn, out *dto.EventGroupLookupOut) error {
	if err := validate(in); err != nil {
		return err
	}

	var extIDArg *model.ExternalIDArg
	if in.ExternalID != nil {
		extIDArg = &model.ExternalIDArg{
			ExternalID: *in.ExternalID,
			Purchase: []domain.PurchaseType{
				domain.PurchaseCPA,
			},
			Kind: []domain.Kind{
				domain.WriteOffKind,
				domain.PartialRefundKind,
				domain.RefundKind,
				domain.AdvanceKind,
			},
		}
	}

	events, eventsError := h.eventHistoryService.Get(
		ctx,
		model.EventHistoryArgs{
			OperationID:   in.OperationID,
			ExternalIDArg: extIDArg,
		})
	if eventsError != nil {
		ctx = logformatter.WithField(ctx, "request.raw", in)
		logformatter.LogWithError(ctx, "error on get events history in handler", eventsError)
		return eventsError
	}

	out.Events = make([]dto.HistoryEvent, 0, len(events))
	for i := 0; i < len(events); i++ {
		event, err := commonEventToHistoryEvent(&events[i])
		if err != nil {
			ctx = logformatter.WithField(ctx, "request.raw", in)
			logformatter.LogWithError(ctx, "error on converting event into dto format", err)
			out.Events = nil
			return err
		}

		out.Events = append(out.Events, event)
	}

	return nil
}

// validate проверяет реквест на наличие базовых ошибок
func validate(in *dto.EventGroupLookupIn) error {
	if in.OperationID == nil && in.ExternalID == nil {
		return rpcprotocol.NewValidationError("at least one parameter must be set")
	}

	if in.ExternalID != nil && len(*in.ExternalID) == 0 {
		return rpcprotocol.NewValidationError("external ID must be not empty string")
	}

	if in.OperationID != nil && *in.OperationID < 0 {
		return rpcprotocol.NewValidationError("operation ID must be greater than zero")
	}

	return nil
}

файл с тестами
package event_group_lookup_test

import (
	"context"
	"database/sql"
	"encoding/json"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	rpcprotocol "go.avito.ru/gl/rpc-protocol"

	"go.avito.ru/bill/service-billing-processing/internal/domain"
	"go.avito.ru/bill/service-billing-processing/internal/generated/rpc/service/dto"
	"go.avito.ru/bill/service-billing-processing/internal/repository/entity"
	"go.avito.ru/bill/service-billing-processing/internal/rpc/event_group_lookup"
	"go.avito.ru/bill/service-billing-processing/internal/service/model"
	mocks "go.avito.ru/bill/service-billing-processing/mocks/service"
	"go.avito.ru/bill/service-billing-processing/pkg/gtc"
)

// tagID 19172 handler получения истории вернул ошибку валидации для operationID
func TestHandler_Handle_ReturnsValidationErrorForOperationID(t *testing.T) {
	t.Parallel()

	var operationID int64 = -1234566
	eventHistoryService := new(mocks.EventHistory)
	h := event_group_lookup.New(eventHistoryService)
	in := dto.EventGroupLookupIn{
		OperationID: &operationID,
		ExternalID:  nil,
	}
	out := dto.EventGroupLookupOut{}
	err := h.Handle(context.Background(), &in, &out)

	validationError := rpcprotocol.NewValidationError("")
	assert.Error(t, err)
	assert.ErrorAs(t, err, &validationError)
	assert.ErrorContains(t, err, "operation ID must be greater than zero")
	eventHistoryService.AssertExpectations(t)
}

// tagID 19172 handler получения истории вернул ошибку валидации для externalID
func TestHandler_Handle_ReturnsValidationErrorForExternalID(t *testing.T) {
	t.Parallel()

	var externalId string = ""
	eventHistoryService := new(mocks.EventHistory)
	h := event_group_lookup.New(eventHistoryService)
	in := dto.EventGroupLookupIn{
		OperationID: nil,
		ExternalID:  &externalId,
	}
	out := dto.EventGroupLookupOut{}
	err := h.Handle(context.Background(), &in, &out)

	validationError := rpcprotocol.NewValidationError("")
	assert.Error(t, err)
	assert.ErrorAs(t, err, &validationError)
	assert.ErrorContains(t, err, "external ID must be not empty string")
	eventHistoryService.AssertExpectations(t)
}

// tagID 19172 handler получения истории вернул ошибку валидации для пустых параметров
func TestHandler_Handle_ReturnsValidationErrorForEmptyParams(t *testing.T) {
	t.Parallel()

	eventHistoryService := new(mocks.EventHistory)
	h := event_group_lookup.New(eventHistoryService)
	in := dto.EventGroupLookupIn{
		OperationID: nil,
		ExternalID:  nil,
	}
	out := dto.EventGroupLookupOut{}
	err := h.Handle(context.Background(), &in, &out)

	validationError := rpcprotocol.NewValidationError("")
	assert.Error(t, err)
	assert.ErrorAs(t, err, &validationError)
	assert.ErrorContains(t, err, "at least one parameter must be set")
	eventHistoryService.AssertExpectations(t)
}

// tagID 19172 handler успешно вернул события
func TestHandler_Handle_Success(t *testing.T) {
	t.Parallel()

	ctx := context.Background()
	var operationID int64 = 1234566
	var externalID string = "7890"
	event1 := entity.CommonEvent{
		ExternalID:   externalID,
		Purchase:     "cpa",
		Kind:         domain.WriteOffKind.String(),
		HoldKey:      "cpa_123",
		OperationID:  sql.NullInt64{Int64: 0, Valid: false},
		UserID:       123,
		StatusID:     domain.StatusCompleted.Int8Value(),
		ServiceID:    int16(domain.ServiceID_CPA),
		AmountRUB:    100,
		AmountBonus:  222,
		MaxBonusRate: 99,
		Context: entity.Context{
			VerticalID: gtc.Int16.ValueToPointer(1),
		},
		AppliedAt: sql.NullTime{Time: time.Now(), Valid: true},
		UpdatedAt: time.Now(),
	}
	event2 := entity.CommonEvent{
		ExternalID:   externalID,
		Purchase:     "cpa",
		Kind:         domain.RefundKind.String(),
		HoldKey:      "cpa_123",
		OperationID:  sql.NullInt64{Int64: 1234567890, Valid: true},
		UserID:       123,
		StatusID:     domain.StatusRefunded.Int8Value(),
		ServiceID:    int16(domain.ServiceID_CPA),
		AmountRUB:    300,
		AmountBonus:  0,
		MaxBonusRate: 99,
		Context: entity.Context{
			VerticalID: gtc.Int16.ValueToPointer(2),
		},
		AppliedAt: sql.NullTime{Time: time.Now(), Valid: true},
		UpdatedAt: time.Now(),
	}

	events := []entity.CommonEvent{event1, event2}

	eventHistoryService := new(mocks.EventHistory)
	eventHistoryService.EXPECT().
		Get(ctx, model.EventHistoryArgs{
			OperationID: &operationID,
			ExternalIDArg: &model.ExternalIDArg{
				ExternalID: externalID,
				Purchase: []domain.PurchaseType{
					domain.PurchaseCPA,
				},
				Kind: []domain.Kind{
					domain.WriteOffKind,
					domain.PartialRefundKind,
					domain.RefundKind,
					domain.AdvanceKind,
				},
			},
		}).
		Return(events, nil).
		Once()

	in := dto.EventGroupLookupIn{
		OperationID: &operationID,
		ExternalID:  &externalID,
	}
	out := dto.EventGroupLookupOut{}

	h := event_group_lookup.New(eventHistoryService)
	err := h.Handle(ctx, &in, &out)
	assert.Nil(t, err, "handler returned unexpected error")

	historyEvents := make([]dto.HistoryEvent, 2)
	historyEvents[0], _ = commonEventToHistoryEvent(&event1)
	historyEvents[1], _ = commonEventToHistoryEvent(&event2)
	assert.Equal(t, out.Events, historyEvents)
	eventHistoryService.AssertExpectations(t)
}

// tagID 19172 handler вернул ошибку при конвертации событий в формат ответа
func TestHandler_Handle_ErrorOnConvert(t *testing.T) {
	t.Parallel()

	ctx := context.Background()
	var operationID int64 = 1234566
	event1 := entity.CommonEvent{
		ExternalID:   "1",
		Purchase:     "cpa",
		Kind:         domain.WriteOffKind.String(),
		HoldKey:      "cpa_123",
		OperationID:  sql.NullInt64{Int64: 0, Valid: false},
		UserID:       123,
		StatusID:     domain.StatusCompleted.Int8Value(),
		ServiceID:    int16(domain.ServiceID_CPA),
		AmountRUB:    300,
		AmountBonus:  0,
		MaxBonusRate: 99,
		Context: entity.Context{
			VerticalID: gtc.Int16.ValueToPointer(1),
		},
		AppliedAt: sql.NullTime{Time: time.Now(), Valid: true},
		UpdatedAt: time.Now(),
	}
	event2 := entity.CommonEvent{
		ExternalID:   "1",
		Purchase:     "cpa",
		Kind:         domain.RefundKind.String(),
		HoldKey:      "cpa_123",
		OperationID:  sql.NullInt64{Int64: 1234567890, Valid: true},
		UserID:       123,
		StatusID:     int8(-1),
		ServiceID:    int16(domain.ServiceID_CPA),
		AmountRUB:    300,
		AmountBonus:  0,
		MaxBonusRate: 99,
		Context: entity.Context{
			VerticalID: gtc.Int16.ValueToPointer(2),
		},
		AppliedAt: sql.NullTime{Time: time.Now(), Valid: true},
		UpdatedAt: time.Now(),
	}
	events := []entity.CommonEvent{event1, event2}

	eventHistoryService := new(mocks.EventHistory)
	eventHistoryService.EXPECT().
		Get(ctx, model.EventHistoryArgs{
			OperationID:   &operationID,
			ExternalIDArg: nil,
		}).
		Return(events, nil).
		Once()

	in := dto.EventGroupLookupIn{
		OperationID: &operationID,
		ExternalID:  nil,
	}
	out := dto.EventGroupLookupOut{}

	h := event_group_lookup.New(eventHistoryService)
	err := h.Handle(ctx, &in, &out)
	assert.ErrorContains(t, err, "status type not supported:")
	assert.Nil(t, out.Events, "events returned any value on error")
	eventHistoryService.AssertExpectations(t)
}

func commonEventToHistoryEvent(e *entity.CommonEvent) (dto.HistoryEvent, error) {
	statusType, err := domain.StatusType{}.FromValue(gtc.Int16.ValueToPointer(int16(e.StatusID)))
	if err != nil {
		return dto.HistoryEvent{}, err
	}
	eventCtx, err := json.Marshal(e.Context)
	if err != nil {
		return dto.HistoryEvent{}, err
	}

	return dto.HistoryEvent{
		ExternalID:   e.ExternalID,
		OperationID:  gtc.Int64.SqlToPointer(e.OperationID),
		UserID:       e.UserID,
		Kind:         e.Kind,
		Status:       statusType.Alias(),
		ServiceID:    int64(e.ServiceID),
		AmountRUB:    e.AmountRUB,
		AmountBonus:  e.AmountBonus,
		MaxBonusRate: int64(e.MaxBonusRate),
		Context:      string(eventCtx),
		AppliedAt:    gtc.Time.SqlToPointer(e.AppliedAt),
		UpdatedAt:    e.UpdatedAt,
	}, nil
}

дополнить тесты на одинаковый externalID + разные kind