feat: merge auth&tester

This commit is contained in:
Vyacheslav1557 2025-04-22 20:44:52 +05:00
parent 0a2dea6c23
commit 441af4c6a2
72 changed files with 4910 additions and 2378 deletions

View file

@ -0,0 +1,145 @@
package repository
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/pkg"
sq "github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"
)
type Repository struct {
db *sqlx.DB
}
func NewRepository(db *sqlx.DB) *Repository {
return &Repository{
db: db,
}
}
const CreateContestQuery = "INSERT INTO contests (title) VALUES ($1) RETURNING id"
func (r *Repository) CreateContest(ctx context.Context, title string) (int32, error) {
const op = "Repository.CreateContest"
rows, err := r.db.QueryxContext(ctx, CreateContestQuery, title)
if err != nil {
return 0, pkg.HandlePgErr(err, op)
}
defer rows.Close()
var id int32
rows.Next()
err = rows.Scan(&id)
if err != nil {
return 0, pkg.HandlePgErr(err, op)
}
return id, nil
}
const GetContestQuery = "SELECT * from contests WHERE id=$1 LIMIT 1"
func (r *Repository) GetContest(ctx context.Context, id int32) (*models.Contest, error) {
const op = "Repository.GetContest"
var contest models.Contest
err := r.db.GetContext(ctx, &contest, GetContestQuery, id)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
return &contest, nil
}
const (
UpdateContestQuery = "UPDATE contests SET title = COALESCE($1, title) WHERE id = $2"
)
func (r *Repository) UpdateContest(ctx context.Context, id int32, contestUpdate models.ContestUpdate) error {
const op = "Repository.UpdateContest"
_, err := r.db.ExecContext(ctx, UpdateContestQuery, contestUpdate.Title, id)
if err != nil {
return pkg.HandlePgErr(err, op)
}
return nil
}
const DeleteContestQuery = "DELETE FROM contests WHERE id=$1"
func (r *Repository) DeleteContest(ctx context.Context, id int32) error {
const op = "Repository.DeleteContest"
_, err := r.db.ExecContext(ctx, DeleteContestQuery, id)
if err != nil {
return pkg.HandlePgErr(err, op)
}
return nil
}
func buildListContestsQueries(filter models.ContestsFilter) (sq.SelectBuilder, sq.SelectBuilder) {
columns := []string{
"c.id",
"c.title",
"c.created_at",
"c.updated_at",
}
qb := sq.StatementBuilder.PlaceholderFormat(sq.Dollar).Select(columns...).From("contests c")
if filter.UserId != nil {
qb = qb.Join("participants p ON c.id = p.contest_id")
qb = qb.Where(sq.Eq{"p.user_id": *filter.UserId})
}
countQb := sq.Select("COUNT(*)").FromSelect(qb, "sub")
if filter.Order != nil && *filter.Order < 0 {
qb = qb.OrderBy("c.created_at DESC")
} else {
qb = qb.OrderBy("c.created_at ASC")
}
qb = qb.Limit(uint64(filter.PageSize)).Offset(uint64(filter.Offset()))
return qb, countQb
}
func (r *Repository) ListContests(ctx context.Context, filter models.ContestsFilter) (*models.ContestsList, error) {
const op = "Repository.ListContests"
baseQb, countQb := buildListContestsQueries(filter)
query, args, err := baseQb.ToSql()
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
var contests []*models.ContestsListItem
err = r.db.SelectContext(ctx, &contests, query, args...)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
query, args, err = countQb.ToSql()
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
var count int32
err = r.db.GetContext(ctx, &count, query, args...)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
return &models.ContestsList{
Contests: contests,
Pagination: models.Pagination{
Total: models.Total(count, filter.PageSize),
Page: filter.Page,
},
}, nil
}

View file

@ -0,0 +1,116 @@
package repository_test
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/contests/repository"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"github.com/DATA-DOG/go-sqlmock"
"github.com/jmoiron/sqlx"
"github.com/stretchr/testify/assert"
"testing"
"time"
)
// setupTestDB creates a mocked sqlx.DB and sqlmock instance for testing.
func setupTestDB(t *testing.T) (*sqlx.DB, sqlmock.Sqlmock) {
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
assert.NoError(t, err)
sqlxDB := sqlx.NewDb(db, "sqlmock")
return sqlxDB, mock
}
func TestRepository_CreateContest(t *testing.T) {
db, mock := setupTestDB(t)
defer db.Close()
repo := repository.NewRepository(db)
t.Run("success", func(t *testing.T) {
ctx := context.Background()
contest := models.Contest{
Id: 1,
Title: "Test Contest",
}
mock.ExpectQuery(repository.CreateContestQuery).
WithArgs(contest.Title).
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(contest.Id))
id, err := repo.CreateContest(ctx, contest.Title)
assert.NoError(t, err)
assert.Equal(t, contest.Id, id)
})
}
func TestRepository_GetContest(t *testing.T) {
db, mock := setupTestDB(t)
defer db.Close()
repo := repository.NewRepository(db)
t.Run("success", func(t *testing.T) {
ctx := context.Background()
contest := models.Contest{
Id: 1,
Title: "Test Contest",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
mock.ExpectQuery(repository.GetContestQuery).
WithArgs(contest.Id).
WillReturnRows(sqlmock.NewRows([]string{"id", "title", "created_at", "updated_at"}).
AddRow(contest.Id, contest.Title, contest.CreatedAt, contest.UpdatedAt))
result, err := repo.GetContest(ctx, contest.Id)
assert.NoError(t, err)
assert.EqualExportedValues(t, &contest, result)
})
}
func TestRepository_UpdateContest(t *testing.T) {
db, mock := setupTestDB(t)
defer db.Close()
repo := repository.NewRepository(db)
t.Run("success", func(t *testing.T) {
ctx := context.Background()
var contestId int32 = 1
update := models.ContestUpdate{
Title: sp("Updated Contest"),
}
mock.ExpectExec(repository.UpdateContestQuery).
WithArgs(update.Title, contestId).
WillReturnResult(sqlmock.NewResult(0, 1))
err := repo.UpdateContest(ctx, contestId, update)
assert.NoError(t, err)
})
}
func TestRepository_DeleteContest(t *testing.T) {
db, mock := setupTestDB(t)
defer db.Close()
repo := repository.NewRepository(db)
t.Run("success", func(t *testing.T) {
ctx := context.Background()
mock.ExpectExec(repository.DeleteContestQuery).
WithArgs(1).
WillReturnResult(sqlmock.NewResult(0, 1))
err := repo.DeleteContest(ctx, 1)
assert.NoError(t, err)
})
}
func sp(s string) *string {
return &s
}

View file

@ -0,0 +1,161 @@
package repository
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/pkg"
)
const (
// state=5 - AC
ReadStatisticsQuery = `
SELECT t.id as task_id,
t.position,
COUNT(*) as total,
COUNT(CASE WHEN s.state = 5 THEN 1 END) as success
FROM tasks t LEFT JOIN solutions s ON t.id = s.task_id
WHERE t.contest_id = $1
GROUP BY t.id, t.position
ORDER BY t.position;
`
SolutionsQuery = `
WITH RankedSolutions AS (
SELECT
s.id,
s.participant_id,
p2.name as participant_name,
s.state,
s.score,
s.penalty,
s.time_stat,
s.memory_stat,
s.language,
s.task_id,
t.position as task_position,
p.title as task_title,
t.contest_id,
c.title as contest_title,
s.updated_at,
s.created_at,
ROW_NUMBER() OVER (
PARTITION BY s.task_id, s.participant_id
ORDER BY s.score DESC, s.created_at
) as rn
FROM solutions s
LEFT JOIN tasks t ON s.task_id = t.id
LEFT JOIN problems p ON t.problem_id = p.id
LEFT JOIN contests c ON t.contest_id = c.id
LEFT JOIN participants p2 on s.participant_id = p2.id
WHERE t.contest_id = $1
)
SELECT
rs.id,
rs.participant_id,
rs.participant_name,
rs.state,
rs.score,
rs.penalty,
rs.time_stat,
rs.memory_stat,
rs.language,
rs.task_id,
rs.task_position,
rs.task_title,
rs.contest_id,
rs.contest_title,
rs.updated_at,
rs.created_at
FROM RankedSolutions rs
WHERE rs.rn = 1`
ParticipantsQuery = `
WITH Attempts AS (
SELECT
s.participant_id,
s.task_id,
COUNT(*) FILTER (WHERE s.state != 5 AND s.created_at < (
SELECT MIN(s2.created_at)
FROM solutions s2
WHERE s2.participant_id = s.participant_id
AND s2.task_id = s.task_id
AND s2.state = 5
)) as failed_attempts,
MIN(CASE WHEN s.state = 5 THEN s.penalty END) as success_penalty
FROM solutions s JOIN tasks t ON t.id = s.task_id
WHERE t.contest_id = $1
GROUP BY s.participant_id, s.task_id
)
SELECT
p.id,
p.name,
COUNT(DISTINCT CASE WHEN a.success_penalty IS NOT NULL THEN a.task_id END) as solved_in_total,
COALESCE(SUM(a.failed_attempts), 0) * $2 + COALESCE(SUM(a.success_penalty), 0) as penalty_in_total
FROM participants p LEFT JOIN Attempts a ON a.participant_id = p.id
WHERE p.contest_id = $1
GROUP BY p.id, p.name
`
)
func (r *Repository) GetMonitor(ctx context.Context, contestId int32, penalty int32) (*models.Monitor, error) {
const op = "Repository.GetMonitor"
rows, err := r.db.QueryxContext(ctx, ReadStatisticsQuery, contestId)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
defer rows.Close()
var monitor models.Monitor
for rows.Next() {
var stat models.ProblemStatSummary
err = rows.StructScan(&stat)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
monitor.Summary = append(monitor.Summary, &stat)
}
var solutions []*models.SolutionsListItem
err = r.db.SelectContext(ctx, &solutions, SolutionsQuery, contestId)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
rows3, err := r.db.QueryxContext(ctx, ParticipantsQuery, contestId, penalty)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
defer rows3.Close()
solutionsMap := make(map[int32][]*models.SolutionsListItem)
for _, solution := range solutions {
solutionsMap[solution.ParticipantId] = append(solutionsMap[solution.ParticipantId], solution)
}
for rows3.Next() {
var stat models.ParticipantsStat
err = rows3.StructScan(&stat)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
if sols, ok := solutionsMap[stat.Id]; ok {
stat.Solutions = sols
}
monitor.Participants = append(monitor.Participants, &stat)
}
return &monitor, nil
}

View file

@ -0,0 +1 @@
package repository

View file

@ -0,0 +1,126 @@
package repository
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/pkg"
)
const GetParticipantIdQuery = "SELECT id FROM participants WHERE user_id=$1 AND contest_id=$2 LIMIT 1"
func (r *Repository) GetParticipantId(ctx context.Context, contestId int32, userId int32) (int32, error) {
const op = "Repository.GetParticipantId"
var participantId int32
err := r.db.GetContext(ctx, &participantId, GetParticipantIdQuery, userId, contestId)
if err != nil {
return 0, pkg.HandlePgErr(err, op)
}
return participantId, nil
}
const GetParticipantId2Query = "SELECT p.id FROM participants p JOIN tasks t ON p.contest_id=t.contest_id WHERE user_id=$1 AND t.id=$2 LIMIT 1"
func (r *Repository) GetParticipantId2(ctx context.Context, taskId int32, userId int32) (int32, error) {
const op = "Repository.GetParticipantId2"
var participantId int32
err := r.db.GetContext(ctx, &participantId, GetParticipantId2Query, userId, taskId)
if err != nil {
return 0, pkg.HandlePgErr(err, op)
}
return participantId, nil
}
const GetParticipantId3Query = "SELECT participant_id FROM solutions WHERE id=$1 LIMIT 1"
func (r *Repository) GetParticipantId3(ctx context.Context, solutionId int32) (int32, error) {
const op = "Repository.GetParticipantId3"
var participantId int32
err := r.db.GetContext(ctx, &participantId, GetParticipantId3Query, solutionId)
if err != nil {
return 0, pkg.HandlePgErr(err, op)
}
return participantId, nil
}
const CreateParticipantQuery = "INSERT INTO participants (user_id, contest_id, name) VALUES ($1, $2, $3) RETURNING id"
func (r *Repository) CreateParticipant(ctx context.Context, contestId int32, userId int32) (int32, error) {
const op = "Repository.CreateParticipant"
name := ""
rows, err := r.db.QueryxContext(ctx, CreateParticipantQuery, userId, contestId, name)
if err != nil {
return 0, pkg.HandlePgErr(err, op)
}
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 participants WHERE id=$1"
const (
UpdateParticipantQuery = "UPDATE participants SET name = COALESCE($1, name) WHERE id = $2"
)
func (r *Repository) UpdateParticipant(ctx context.Context, id int32, participantUpdate models.ParticipantUpdate) error {
const op = "Repository.UpdateParticipant"
_, err := r.db.ExecContext(ctx, UpdateParticipantQuery, participantUpdate.Name, id)
if err != nil {
return pkg.HandlePgErr(err, op)
}
return nil
}
func (r *Repository) DeleteParticipant(ctx context.Context, participantId int32) error {
const op = "Repository.DeleteParticipant"
_, err := r.db.ExecContext(ctx, DeleteParticipantQuery, participantId)
if err != nil {
return pkg.HandlePgErr(err, op)
}
return nil
}
const (
ReadParticipantsListQuery = `SELECT id, user_id, name, created_at, updated_at FROM participants WHERE contest_id = $1 LIMIT $2 OFFSET $3`
CountParticipantsQuery = "SELECT COUNT(*) FROM participants WHERE contest_id = $1"
)
func (r *Repository) ListParticipants(ctx context.Context, filter models.ParticipantsFilter) (*models.ParticipantsList, error) {
const op = "Repository.ReadParticipants"
var participants []*models.ParticipantsListItem
err := r.db.SelectContext(ctx, &participants,
ReadParticipantsListQuery, filter.ContestId, filter.PageSize, filter.Offset())
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
var count int32
err = r.db.GetContext(ctx, &count, CountParticipantsQuery, filter.ContestId)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
return &models.ParticipantsList{
Participants: participants,
Pagination: models.Pagination{
Total: models.Total(count, filter.PageSize),
Page: filter.Page,
},
}, nil
}

View file

@ -0,0 +1,51 @@
package repository_test
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/contests/repository"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"testing"
)
func TestRepository_CreateParticipant(t *testing.T) {
db, mock := setupTestDB(t)
defer db.Close()
repo := repository.NewRepository(db)
t.Run("success", func(t *testing.T) {
var (
expectedId int32 = 1
userId int32 = 2
contestId int32 = 3
)
ctx := context.Background()
mock.ExpectQuery(repository.CreateParticipantQuery).
WithArgs(userId, contestId).
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(expectedId))
id, err := repo.CreateParticipant(ctx, contestId, userId)
assert.NoError(t, err)
assert.Equal(t, expectedId, id)
})
}
func TestRepository_DeleteParticipant(t *testing.T) {
db, mock := setupTestDB(t)
defer db.Close()
repo := repository.NewRepository(db)
t.Run("success", func(t *testing.T) {
ctx := context.Background()
var participantId int32 = 1
mock.ExpectExec(repository.DeleteParticipantQuery).
WithArgs(participantId).WillReturnResult(sqlmock.NewResult(0, 1))
err := repo.DeleteParticipant(ctx, participantId)
assert.NoError(t, err)
})
}

View file

@ -0,0 +1,222 @@
package repository
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/pkg"
sq "github.com/Masterminds/squirrel"
)
const (
GetSolutionQuery = "SELECT * FROM solutions WHERE id = $1"
)
func (r *Repository) GetSolution(ctx context.Context, id int32) (*models.Solution, error) {
const op = "Repository.GetSolution"
var solution models.Solution
err := r.db.GetContext(ctx, &solution, GetSolutionQuery, id)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
return &solution, nil
}
const (
CreateSolutionQuery = `INSERT INTO solutions (task_id, participant_id, language, penalty, solution)
VALUES ($1, $2, $3, $4, $5)
RETURNING id`
)
func (r *Repository) CreateSolution(ctx context.Context, creation *models.SolutionCreation) (int32, error) {
const op = "Repository.CreateSolution"
rows, err := r.db.QueryxContext(ctx,
CreateSolutionQuery,
creation.TaskId,
creation.ParticipantId,
creation.Language,
creation.Penalty,
creation.Solution,
)
if err != nil {
return 0, pkg.HandlePgErr(err, op)
}
defer rows.Close()
var id int32
rows.Next()
err = rows.Scan(&id)
if err != nil {
return 0, pkg.HandlePgErr(err, op)
}
return id, nil
}
func buildListSolutionsQueries(filter models.SolutionsFilter) (sq.SelectBuilder, sq.SelectBuilder) {
columns := []string{
"s.id",
"s.participant_id",
"p2.name AS participant_name",
"s.state",
"s.score",
"s.penalty",
"s.time_stat",
"s.memory_stat",
"s.language",
"s.task_id",
"t.position AS task_position",
"p.title AS task_title",
"t.contest_id",
"c.title",
"s.updated_at",
"s.created_at",
}
qb := sq.StatementBuilder.PlaceholderFormat(sq.Dollar).Select(columns...).
From("solutions s").
LeftJoin("tasks t ON s.task_id = t.id").
LeftJoin("problems p ON t.problem_id = p.id").
LeftJoin("contests c ON t.contest_id = c.id").
LeftJoin("participants p2 ON s.participant_id = p2.id")
if filter.ContestId != nil {
qb = qb.Where(sq.Eq{"s.contest_id": *filter.ContestId})
}
if filter.ParticipantId != nil {
qb = qb.Where(sq.Eq{"s.participant_id": *filter.ParticipantId})
}
if filter.TaskId != nil {
qb = qb.Where(sq.Eq{"s.task_id": *filter.TaskId})
}
if filter.Language != nil {
qb = qb.Where(sq.Eq{"s.language": *filter.Language})
}
if filter.State != nil {
qb = qb.Where(sq.Eq{"s.state": *filter.State})
}
countQb := sq.Select("COUNT(*)").FromSelect(qb, "sub")
if filter.Order != nil && *filter.Order < 0 {
qb = qb.OrderBy("s.id DESC")
} else {
qb = qb.OrderBy("s.id ASC")
}
qb = qb.Limit(uint64(filter.PageSize)).Offset(uint64(filter.Offset()))
return qb, countQb
}
func (r *Repository) ListSolutions(ctx context.Context, filter models.SolutionsFilter) (*models.SolutionsList, error) {
const op = "ContestRepository.ListSolutions"
baseQb, countQb := buildListSolutionsQueries(filter)
query, args, err := countQb.ToSql()
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
var totalCount int32
err = r.db.GetContext(ctx, &totalCount, query, args...)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
query, args, err = baseQb.ToSql()
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
rows, err := r.db.QueryxContext(ctx, query, args...)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
defer rows.Close()
solutions := make([]*models.SolutionsListItem, 0)
for rows.Next() {
var solution models.SolutionsListItem
err = rows.StructScan(&solution)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
solutions = append(solutions, &solution)
}
if err = rows.Err(); err != nil {
return nil, pkg.HandlePgErr(err, op)
}
return &models.SolutionsList{
Solutions: solutions,
Pagination: models.Pagination{
Total: models.Total(totalCount, filter.PageSize),
Page: filter.Page,
},
}, nil
}
const (
// state=5 - AC
GetBestSolutions = `
WITH contest_tasks AS (
SELECT t.id AS task_id,
t.position AS task_position,
t.contest_id,
t.problem_id,
t.created_at,
t.updated_at,
p.title AS task_title,
c.title AS contest_title
FROM tasks t
LEFT JOIN problems p ON p.id = t.problem_id
LEFT JOIN contests c ON c.id = t.contest_id
WHERE t.contest_id = ?
),
best_solutions AS (
SELECT DISTINCT ON (s.task_id)
*
FROM solutions s
WHERE s.participant_id = ?
ORDER BY s.task_id, s.score DESC, s.created_at DESC
)
SELECT
s.id,
s.participant_id,
p.name AS participant_name,
s.state,
s.score,
s.penalty,
s.time_stat,
s.memory_stat,
s.language,
ct.task_id,
ct.task_position,
ct.task_title,
ct.contest_id,
ct.contest_title,
s.updated_at,
s.created_at
FROM contest_tasks ct
LEFT JOIN best_solutions s ON s.task_id = ct.task_id
LEFT JOIN participants p ON p.id = s.participant_id WHERE s.id IS NOT NULL
ORDER BY ct.task_position
`
)
func (r *Repository) GetBestSolutions(ctx context.Context, contestId int32, participantId int32) ([]*models.SolutionsListItem, error) {
const op = "Repository.GetBestSolutions"
var solutions []*models.SolutionsListItem
query := r.db.Rebind(GetBestSolutions)
err := r.db.SelectContext(ctx, &solutions, query, contestId, participantId)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
return solutions, nil
}

View file

@ -0,0 +1 @@
package repository

View file

@ -0,0 +1,101 @@
package repository
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"git.sch9.ru/new_gate/ms-tester/pkg"
)
const CreateTaskQuery = `INSERT INTO tasks (problem_id, contest_id, position)
VALUES ($1, $2, COALESCE((SELECT MAX(position) FROM tasks WHERE contest_id = $2), 0) + 1)
RETURNING id
`
func (r *Repository) CreateTask(ctx context.Context, contestId int32, problemId int32) (int32, error) {
const op = "Repository.AddTask"
rows, err := r.db.QueryxContext(ctx, CreateTaskQuery, problemId, contestId)
if err != nil {
return 0, pkg.HandlePgErr(err, op)
}
defer rows.Close()
var id int32
rows.Next()
err = rows.Scan(&id)
if err != nil {
return 0, pkg.HandlePgErr(err, op)
}
return id, nil
}
const DeleteTaskQuery = "DELETE FROM tasks WHERE id=$1"
func (r *Repository) DeleteTask(ctx context.Context, taskId int32) error {
const op = "Repository.DeleteTask"
_, err := r.db.ExecContext(ctx, DeleteTaskQuery, taskId)
if err != nil {
return pkg.HandlePgErr(err, op)
}
return nil
}
const GetTasksQuery = `SELECT tasks.id,
problem_id,
contest_id,
position,
title,
memory_limit,
time_limit,
tasks.created_at,
tasks.updated_at
FROM tasks
INNER JOIN problems ON tasks.problem_id = problems.id
WHERE contest_id = $1 ORDER BY position`
func (r *Repository) GetTasks(ctx context.Context, contestId int32) ([]*models.TasksListItem, error) {
const op = "Repository.ReadTasks"
var tasks []*models.TasksListItem
err := r.db.SelectContext(ctx, &tasks, GetTasksQuery, contestId)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
return tasks, nil
}
const (
GetTaskQuery = `
SELECT
t.id,
t.position,
p.title,
p.time_limit,
p.memory_limit,
t.problem_id,
t.contest_id,
p.legend_html,
p.input_format_html,
p.output_format_html,
p.notes_html,
p.scoring_html,
t.created_at,
t.updated_at
FROM tasks t
LEFT JOIN problems p ON t.problem_id = p.id
WHERE t.id = ?
`
)
func (r *Repository) GetTask(ctx context.Context, id int32) (*models.Task, error) {
const op = "Repository.ReadTask"
query := r.db.Rebind(GetTaskQuery)
var task models.Task
err := r.db.GetContext(ctx, &task, query, id)
if err != nil {
return nil, pkg.HandlePgErr(err, op)
}
return &task, nil
}

View file

@ -0,0 +1,51 @@
package repository_test
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/contests/repository"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"testing"
)
func TestRepository_CreateTask(t *testing.T) {
db, mock := setupTestDB(t)
defer db.Close()
repo := repository.NewRepository(db)
t.Run("success", func(t *testing.T) {
var (
expectedId int32 = 1
problemId int32 = 2
contestId int32 = 3
)
ctx := context.Background()
mock.ExpectQuery(repository.CreateTaskQuery).
WithArgs(problemId, contestId).
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(expectedId))
id, err := repo.CreateTask(ctx, contestId, problemId)
assert.NoError(t, err)
assert.Equal(t, expectedId, id)
})
}
func TestRepository_DeleteTask(t *testing.T) {
db, mock := setupTestDB(t)
defer db.Close()
repo := repository.NewRepository(db)
t.Run("success", func(t *testing.T) {
ctx := context.Background()
mock.ExpectExec(repository.DeleteTaskQuery).
WithArgs(1).
WillReturnResult(sqlmock.NewResult(0, 1))
err := repo.DeleteTask(ctx, 1)
assert.NoError(t, err)
})
}