Compare commits

...

4 commits

7 changed files with 215 additions and 0 deletions

View file

@ -10,4 +10,8 @@ type ContestHandlers interface {
CreateContest(ctx context.Context, req *contestv1.CreateContestRequest) (*contestv1.CreateContestResponse, error) CreateContest(ctx context.Context, req *contestv1.CreateContestRequest) (*contestv1.CreateContestResponse, error)
ReadContest(ctx context.Context, req *contestv1.ReadContestRequest) (*contestv1.ReadContestResponse, error) ReadContest(ctx context.Context, req *contestv1.ReadContestRequest) (*contestv1.ReadContestResponse, error)
DeleteContest(ctx context.Context, req *contestv1.DeleteContestRequest) (*emptypb.Empty, error) DeleteContest(ctx context.Context, req *contestv1.DeleteContestRequest) (*emptypb.Empty, error)
AddTask(ctx context.Context, req *contestv1.AddTaskRequest) (*contestv1.AddTaskResponse, error)
DeleteTask(ctx context.Context, req *contestv1.DeleteTaskRequest) (*emptypb.Empty, error)
AddParticipant(ctx context.Context, req *contestv1.AddParticipantRequest) (*contestv1.AddParticipantResponse, error)
DeleteParticipant(ctx context.Context, req *contestv1.DeleteParticipantRequest) (*emptypb.Empty, error)
} }

View file

@ -49,3 +49,35 @@ func (h *ContestHandlers) DeleteContest(ctx context.Context, req *contestv1.Dele
} }
return &emptypb.Empty{}, nil return &emptypb.Empty{}, nil
} }
func (h *ContestHandlers) AddTask(ctx context.Context, req *contestv1.AddTaskRequest) (*contestv1.AddTaskResponse, error) {
id, err := h.contestUC.AddTask(ctx, req.GetContestId(), req.GetProblemId())
if err != nil {
return nil, err
}
return &contestv1.AddTaskResponse{Id: id}, nil
}
func (h *ContestHandlers) DeleteTask(ctx context.Context, req *contestv1.DeleteTaskRequest) (*emptypb.Empty, error) {
err := h.contestUC.DeleteTask(ctx, req.GetTaskId())
if err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}
func (h *ContestHandlers) AddParticipant(ctx context.Context, req *contestv1.AddParticipantRequest) (*contestv1.AddParticipantResponse, error) {
id, err := h.contestUC.AddParticipant(ctx, req.GetContestId(), req.GetUserId())
if err != nil {
return nil, err
}
return &contestv1.AddParticipantResponse{Id: id}, nil
}
func (h *ContestHandlers) DeleteParticipant(ctx context.Context, req *contestv1.DeleteParticipantRequest) (*emptypb.Empty, error) {
err := h.contestUC.DeleteParticipant(ctx, req.GetParticipantId())
if err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}

View file

@ -9,4 +9,8 @@ type ContestRepository interface {
CreateContest(ctx context.Context, title string) (int32, error) CreateContest(ctx context.Context, title string) (int32, error)
ReadContestById(ctx context.Context, id int32) (*models.Contest, error) ReadContestById(ctx context.Context, id int32) (*models.Contest, error)
DeleteContest(ctx context.Context, id int32) error DeleteContest(ctx context.Context, id int32) error
AddTask(ctx context.Context, contestId int32, taskId int32) (int32, error)
DeleteTask(ctx context.Context, taskId int32) error
AddParticipant(ctx context.Context, contestId int32, userId int32) (int32, error)
DeleteParticipant(ctx context.Context, participantId int32) error
} }

View file

@ -68,6 +68,65 @@ func (r *ContestRepository) DeleteContest(ctx context.Context, id int32) error {
return nil return nil
} }
const addTaskQuery = "INSERT INTO task (problem_id, contest_id, position) VALUES (?, ?,COALESCE(SELECT MAX(position) FROM task WHERE contest_id = ? ,0) + 1) RETURNING id"
func (r *ContestRepository) AddTask(ctx context.Context, contestId int32, problem_id int32) (int32, error) {
query := r.db.Rebind(addTaskQuery)
rows, err := r.db.QueryxContext(ctx, query, problem_id, contestId, contestId)
if err != nil {
return 0, handlePgErr(err)
}
defer rows.Close()
var id int32
rows.Next()
err = rows.Scan(&id)
if err != nil {
return 0, handlePgErr(err)
}
return id, nil
}
const deleteTaskQuery = "DELETE FROM task WHERE id=?"
func (r *ContestRepository) DeleteTask(ctx context.Context, taskId int32) error {
query := r.db.Rebind(deleteTaskQuery)
_, err := r.db.ExecContext(ctx, query, taskId)
if err != nil {
return handlePgErr(err)
}
return nil
}
const addParticipantQuery = "INSERT INTO participant (user_id ,contest_id, name) VALUES (?, ?, ?) RETURNING id"
func (r *ContestRepository) AddParticipant(ctx context.Context, contestId int32, userId int32) (int32, error) {
query := r.db.Rebind(addParticipantQuery)
name := ""
rows, err := r.db.QueryxContext(ctx, query, contestId, userId, name)
if err != nil {
return 0, handlePgErr(err)
}
defer rows.Close()
var id int32
rows.Next()
err = rows.Scan(&id)
if err != nil {
return 0, err
}
return id, nil
}
const deleteParticipantQuery = "DELETE FROM participant WHERE id=?"
func (r *ContestRepository) DeleteParticipant(ctx context.Context, participantId int32) error {
query := r.db.Rebind(deleteParticipantQuery)
_, err := r.db.ExecContext(ctx, query, participantId)
if err != nil {
return handlePgErr(err)
}
return nil
}
func handlePgErr(err error) error { func handlePgErr(err error) error {
var pgErr *pgconn.PgError var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) { if !errors.As(err, &pgErr) {

View file

@ -56,3 +56,99 @@ func TestContestRepository_DeleteContest(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
}) })
} }
func TestContestRepository_AddTask(t *testing.T) {
t.Parallel()
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()
sqlxDB := sqlx.NewDb(db, "sqlmock")
defer sqlxDB.Close()
contestRepo := NewContestRepository(sqlxDB, zap.NewNop())
t.Run("valid task additional", func(t *testing.T) {
taskId := int32(1)
contestId := int32(1)
rows := sqlmock.NewRows([]string{"id"}).AddRow(1)
mock.ExpectQuery(sqlxDB.Rebind(addTaskQuery)).WithArgs(taskId, contestId, contestId).WillReturnRows(rows)
id, err := contestRepo.AddTask(context.Background(), contestId, taskId)
require.NoError(t, err)
require.Equal(t, int32(1), id)
})
}
func TestContestRepository_DeleteTask(t *testing.T) {
t.Parallel()
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()
sqlxDB := sqlx.NewDb(db, "sqlmock")
defer sqlxDB.Close()
contestRepo := NewContestRepository(sqlxDB, zap.NewNop())
t.Run("valid task deletion", func(t *testing.T) {
id := int32(1)
rows := sqlmock.NewResult(1, 1)
mock.ExpectExec(sqlxDB.Rebind(deleteTaskQuery)).WithArgs(id).WillReturnResult(rows)
err = contestRepo.DeleteTask(context.Background(), id)
require.NoError(t, err)
})
}
func TestContestRepository_AddParticipant(t *testing.T) {
t.Parallel()
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()
sqlxDB := sqlx.NewDb(db, "sqlmock")
defer sqlxDB.Close()
contestRepo := NewContestRepository(sqlxDB, zap.NewNop())
t.Run("valid participant addition", func(t *testing.T) {
contestId := int32(1)
userId := int32(1)
name := ""
rows := sqlmock.NewRows([]string{"id"}).AddRow(1)
mock.ExpectQuery(sqlxDB.Rebind(addParticipantQuery)).WithArgs(contestId, userId, name).WillReturnRows(rows)
id, err := contestRepo.AddParticipant(context.Background(), contestId, userId)
require.NoError(t, err)
require.Equal(t, int32(1), id)
})
}
func TestContestRepository_DeleteParticipant(t *testing.T) {
t.Parallel()
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
require.NoError(t, err)
defer db.Close()
sqlxDB := sqlx.NewDb(db, "sqlmock")
defer sqlxDB.Close()
contestRepo := NewContestRepository(sqlxDB, zap.NewNop())
t.Run("valid participant deletion", func(t *testing.T) {
id := int32(1)
rows := sqlmock.NewResult(1, 1)
mock.ExpectExec(sqlxDB.Rebind(deleteParticipantQuery)).WithArgs(id).WillReturnResult(rows)
err = contestRepo.DeleteParticipant(context.Background(), id)
require.NoError(t, err)
})
}

View file

@ -9,4 +9,8 @@ type ContestUseCase interface {
CreateContest(ctx context.Context, title string) (int32, error) CreateContest(ctx context.Context, title string) (int32, error)
ReadContestById(ctx context.Context, id int32) (*models.Contest, error) ReadContestById(ctx context.Context, id int32) (*models.Contest, error)
DeleteContest(ctx context.Context, id int32) error DeleteContest(ctx context.Context, id int32) error
AddTask(ctx context.Context, contestId int32, taskId int32) (int32, error)
DeleteTask(ctx context.Context, taskId int32) error
AddParticipant(ctx context.Context, contestId int32, userId int32) (int32, error)
DeleteParticipant(ctx context.Context, participantId int32) error
} }

View file

@ -29,3 +29,19 @@ func (uc *ContestUseCase) ReadContestById(ctx context.Context, id int32) (*model
func (uc *ContestUseCase) DeleteContest(ctx context.Context, id int32) error { func (uc *ContestUseCase) DeleteContest(ctx context.Context, id int32) error {
return uc.contestRepo.DeleteContest(ctx, id) return uc.contestRepo.DeleteContest(ctx, id)
} }
func (uc *ContestUseCase) AddTask(ctx context.Context, contestId int32, taskId int32) (id int32, err error) {
return uc.contestRepo.AddTask(ctx, contestId, taskId)
}
func (uc *ContestUseCase) DeleteTask(ctx context.Context, taskId int32) error {
return uc.contestRepo.DeleteTask(ctx, taskId)
}
func (uc *ContestUseCase) AddParticipant(ctx context.Context, contestId int32, userId int32) (id int32, err error) {
return uc.contestRepo.AddParticipant(ctx, contestId, userId)
}
func (uc *ContestUseCase) DeleteParticipant(ctx context.Context, participantId int32) error {
return uc.contestRepo.DeleteParticipant(ctx, participantId)
}