test(tester): fix tests
This commit is contained in:
parent
2ab7a16ddf
commit
4fb0b80f24
6 changed files with 1500 additions and 181 deletions
5
go.mod
5
go.mod
|
@ -4,11 +4,13 @@ go 1.23.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||||
|
github.com/Masterminds/squirrel v1.5.4
|
||||||
github.com/gofiber/fiber/v2 v2.52.6
|
github.com/gofiber/fiber/v2 v2.52.6
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.1
|
github.com/golang-jwt/jwt/v4 v4.5.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/ilyakaznacheev/cleanenv v1.5.0
|
github.com/ilyakaznacheev/cleanenv v1.5.0
|
||||||
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
|
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.27
|
||||||
github.com/oapi-codegen/runtime v1.1.1
|
github.com/oapi-codegen/runtime v1.1.1
|
||||||
github.com/open-policy-agent/opa v1.2.0
|
github.com/open-policy-agent/opa v1.2.0
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0
|
github.com/rabbitmq/amqp091-go v1.10.0
|
||||||
|
@ -35,10 +37,11 @@ require (
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
github.com/klauspost/compress v1.17.11 // indirect
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||||
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
|
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/client_golang v1.21.0 // indirect
|
github.com/prometheus/client_golang v1.21.0 // indirect
|
||||||
|
|
7
go.sum
7
go.sum
|
@ -5,6 +5,8 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8
|
||||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||||
|
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
|
||||||
|
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
||||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||||
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
|
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
|
||||||
github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
|
github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
|
||||||
|
@ -97,6 +99,10 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
||||||
|
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||||
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
||||||
|
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
@ -146,6 +152,7 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
|
|
@ -2,10 +2,9 @@ package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"git.sch9.ru/new_gate/ms-tester/internal/models"
|
"git.sch9.ru/new_gate/ms-tester/internal/models"
|
||||||
|
sq "github.com/Masterminds/squirrel"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContestRepository struct {
|
type ContestRepository struct {
|
||||||
|
@ -172,7 +171,7 @@ const (
|
||||||
func (r *ContestRepository) ListContests(ctx context.Context, filter models.ContestsFilter) (*models.ContestsList, error) {
|
func (r *ContestRepository) ListContests(ctx context.Context, filter models.ContestsFilter) (*models.ContestsList, error) {
|
||||||
const op = "ContestRepository.ReadTasks"
|
const op = "ContestRepository.ReadTasks"
|
||||||
|
|
||||||
var contests []*models.ContestsListItem
|
contests := make([]*models.ContestsListItem, 0)
|
||||||
query := r.db.Rebind(readContestsListQuery)
|
query := r.db.Rebind(readContestsListQuery)
|
||||||
err := r.db.SelectContext(ctx, &contests, query, filter.PageSize, filter.Offset())
|
err := r.db.SelectContext(ctx, &contests, query, filter.PageSize, filter.Offset())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -207,7 +206,7 @@ func (r *ContestRepository) ListParticipants(ctx context.Context, filter models.
|
||||||
filter.PageSize = 1
|
filter.PageSize = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
var participants []*models.ParticipantsListItem
|
participants := make([]*models.ParticipantsListItem, 0)
|
||||||
query := r.db.Rebind(readParticipantsListQuery)
|
query := r.db.Rebind(readParticipantsListQuery)
|
||||||
err := r.db.SelectContext(ctx, &participants, query, filter.ContestId, filter.PageSize, filter.Offset())
|
err := r.db.SelectContext(ctx, &participants, query, filter.ContestId, filter.PageSize, filter.Offset())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -313,93 +312,94 @@ func (r *ContestRepository) CreateSolution(ctx context.Context, creation *models
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildListSolutionsQueries builds two SQL queries: one for selecting solutions
|
||||||
|
// and another for counting them. The first query selects all columns that are
|
||||||
|
// needed for the solutions list, including the task and contest titles, and
|
||||||
|
// the participant name. The second query counts the number of solutions that
|
||||||
|
// match the filter.
|
||||||
|
//
|
||||||
|
// The caller is responsible for executing the queries and processing the
|
||||||
|
// results.
|
||||||
|
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.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("s.contest_id = ?", *filter.ContestId)
|
||||||
|
}
|
||||||
|
if filter.ParticipantId != nil {
|
||||||
|
qb = qb.Where("s.participant_id = ?", *filter.ParticipantId)
|
||||||
|
}
|
||||||
|
if filter.TaskId != nil {
|
||||||
|
qb = qb.Where("s.task_id = ?", *filter.TaskId)
|
||||||
|
}
|
||||||
|
if filter.Language != nil {
|
||||||
|
qb = qb.Where("s.language = ?", *filter.Language)
|
||||||
|
}
|
||||||
|
if filter.State != nil {
|
||||||
|
qb = qb.Where("s.state = ?", *filter.State)
|
||||||
|
}
|
||||||
|
|
||||||
|
countQb := sq.Select("COUNT(*)").FromSelect(qb, "sub")
|
||||||
|
|
||||||
|
if filter.Order != nil && *filter.Order < 0 {
|
||||||
|
qb = qb.OrderBy("s.id DESC")
|
||||||
|
}
|
||||||
|
|
||||||
|
qb = qb.Limit(uint64(filter.PageSize)).Offset(uint64(filter.Offset()))
|
||||||
|
|
||||||
|
return qb, countQb
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ContestRepository) ListSolutions(ctx context.Context, filter models.SolutionsFilter) (*models.SolutionsList, error) {
|
func (r *ContestRepository) ListSolutions(ctx context.Context, filter models.SolutionsFilter) (*models.SolutionsList, error) {
|
||||||
const op = "ContestRepository.ListSolutions"
|
const op = "ContestRepository.ListSolutions"
|
||||||
|
|
||||||
baseQuery := `
|
baseQb, countQb := buildListSolutionsQueries(filter)
|
||||||
SELECT s.id,
|
|
||||||
|
|
||||||
s.participant_id,
|
query, args, err := countQb.ToSql()
|
||||||
p2.name as participant_name,
|
if err != nil {
|
||||||
|
return nil, handlePgErr(err, op)
|
||||||
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
|
|
||||||
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 1=1
|
|
||||||
`
|
|
||||||
|
|
||||||
var conditions []string
|
|
||||||
var args []interface{}
|
|
||||||
|
|
||||||
if filter.ContestId != nil {
|
|
||||||
conditions = append(conditions, "s.contest_id = ?")
|
|
||||||
args = append(args, *filter.ContestId)
|
|
||||||
}
|
}
|
||||||
if filter.ParticipantId != nil {
|
|
||||||
conditions = append(conditions, "s.participant_id = ?")
|
|
||||||
args = append(args, *filter.ParticipantId)
|
|
||||||
}
|
|
||||||
if filter.TaskId != nil {
|
|
||||||
conditions = append(conditions, "s.task_id = ?")
|
|
||||||
args = append(args, *filter.TaskId)
|
|
||||||
}
|
|
||||||
if filter.Language != nil {
|
|
||||||
conditions = append(conditions, "s.language = ?")
|
|
||||||
args = append(args, *filter.Language)
|
|
||||||
}
|
|
||||||
if filter.State != nil {
|
|
||||||
conditions = append(conditions, "s.state = ?")
|
|
||||||
args = append(args, *filter.State)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(conditions) > 0 {
|
|
||||||
baseQuery += " AND " + strings.Join(conditions, " AND ")
|
|
||||||
}
|
|
||||||
|
|
||||||
if filter.Order != nil {
|
|
||||||
orderDirection := "ASC"
|
|
||||||
if *filter.Order < 0 {
|
|
||||||
orderDirection = "DESC"
|
|
||||||
}
|
|
||||||
baseQuery += fmt.Sprintf(" ORDER BY s.id %s", orderDirection)
|
|
||||||
}
|
|
||||||
|
|
||||||
countQuery := "SELECT COUNT(*) FROM (" + baseQuery + ") as count_table"
|
|
||||||
var totalCount int32
|
var totalCount int32
|
||||||
err := r.db.QueryRowxContext(ctx, r.db.Rebind(countQuery), args...).Scan(&totalCount)
|
err = r.db.QueryRowxContext(ctx, r.db.Rebind(query), args...).Scan(&totalCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, handlePgErr(err, op)
|
return nil, handlePgErr(err, op)
|
||||||
}
|
}
|
||||||
|
|
||||||
offset := (filter.Page - 1) * filter.PageSize
|
query, args, err = baseQb.ToSql()
|
||||||
baseQuery += " LIMIT ? OFFSET ?"
|
if err != nil {
|
||||||
args = append(args, filter.PageSize, offset)
|
return nil, handlePgErr(err, op)
|
||||||
|
}
|
||||||
rows, err := r.db.QueryxContext(ctx, r.db.Rebind(baseQuery), args...)
|
rows, err := r.db.QueryxContext(ctx, r.db.Rebind(query), args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, handlePgErr(err, op)
|
return nil, handlePgErr(err, op)
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var solutions []*models.SolutionsListItem
|
solutions := make([]*models.SolutionsListItem, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var solution models.SolutionsListItem
|
var solution models.SolutionsListItem
|
||||||
err = rows.StructScan(&solution)
|
err = rows.StructScan(&solution)
|
||||||
|
@ -551,7 +551,7 @@ WITH Attempts AS (
|
||||||
MIN(CASE WHEN s.state = 5 THEN s.penalty END) as success_penalty
|
MIN(CASE WHEN s.state = 5 THEN s.penalty END) as success_penalty
|
||||||
FROM solutions s
|
FROM solutions s
|
||||||
JOIN tasks t ON t.id = s.task_id
|
JOIN tasks t ON t.id = s.task_id
|
||||||
WHERE t.contest_id = :contest_id
|
WHERE t.contest_id = ?
|
||||||
GROUP BY s.participant_id, s.task_id
|
GROUP BY s.participant_id, s.task_id
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -559,11 +559,11 @@ SELECT
|
||||||
p.name,
|
p.name,
|
||||||
COUNT(DISTINCT CASE WHEN a.success_penalty IS NOT NULL THEN a.task_id END) as solved_in_total,
|
COUNT(DISTINCT CASE WHEN a.success_penalty IS NOT NULL THEN a.task_id END) as solved_in_total,
|
||||||
COALESCE(SUM(CASE WHEN a.success_penalty IS NOT NULL
|
COALESCE(SUM(CASE WHEN a.success_penalty IS NOT NULL
|
||||||
THEN a.failed_attempts * :penalty + a.success_penalty
|
THEN a.failed_attempts * ? + a.success_penalty
|
||||||
ELSE 0 END), 0) as penalty_in_total
|
ELSE 0 END), 0) as penalty_in_total
|
||||||
FROM participants p
|
FROM participants p
|
||||||
LEFT JOIN Attempts a ON a.participant_id = p.id
|
LEFT JOIN Attempts a ON a.participant_id = p.id
|
||||||
WHERE p.contest_id = :contest_id
|
WHERE p.contest_id = ?
|
||||||
GROUP BY p.id, p.name
|
GROUP BY p.id, p.name
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
@ -596,10 +596,7 @@ func (r *ContestRepository) ReadMonitor(ctx context.Context, contestId int32) (*
|
||||||
|
|
||||||
penalty := int32(20) // FIXME
|
penalty := int32(20) // FIXME
|
||||||
namedQuery := r.db.Rebind(participantsQuery)
|
namedQuery := r.db.Rebind(participantsQuery)
|
||||||
rows3, err := r.db.NamedQueryContext(ctx, namedQuery, map[string]interface{}{
|
rows3, err := r.db.QueryxContext(ctx, namedQuery, contestId, penalty, contestId)
|
||||||
"contest_id": contestId,
|
|
||||||
"penalty": penalty,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, handlePgErr(err, op)
|
return nil, handlePgErr(err, op)
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,57 +2,153 @@ package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"git.sch9.ru/new_gate/ms-tester/internal/models"
|
||||||
"github.com/DATA-DOG/go-sqlmock"
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/zap"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestProblemRepository_CreateProblem(t *testing.T) {
|
type problemTestFixture struct {
|
||||||
t.Parallel()
|
db *sql.DB
|
||||||
|
sqlxDB *sqlx.DB
|
||||||
|
mock sqlmock.Sqlmock
|
||||||
|
problemRepo *ProblemRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProblemTestFixture(t *testing.T) *problemTestFixture {
|
||||||
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
|
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
sqlxDB := sqlx.NewDb(db, "sqlmock")
|
sqlxDB := sqlx.NewDb(db, "sqlmock")
|
||||||
defer sqlxDB.Close()
|
repo := NewProblemRepository(sqlxDB)
|
||||||
|
|
||||||
problemRepo := NewProblemRepository(sqlxDB, zap.NewNop())
|
return &problemTestFixture{
|
||||||
|
db: db,
|
||||||
|
sqlxDB: sqlxDB,
|
||||||
|
mock: mock,
|
||||||
|
problemRepo: repo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup closes database connections
|
||||||
|
func (tf *problemTestFixture) cleanup() {
|
||||||
|
tf.db.Close()
|
||||||
|
tf.sqlxDB.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProblemRepository_CreateProblem(t *testing.T) {
|
||||||
|
tf := newProblemTestFixture(t)
|
||||||
|
defer tf.cleanup()
|
||||||
|
|
||||||
t.Run("valid problem creation", func(t *testing.T) {
|
t.Run("valid problem creation", func(t *testing.T) {
|
||||||
title := "Problem title"
|
title := "Test Problem"
|
||||||
|
|
||||||
rows := sqlmock.NewRows([]string{"id"}).AddRow(1)
|
rows := sqlmock.NewRows([]string{"id"}).AddRow(1)
|
||||||
|
|
||||||
mock.ExpectQuery(sqlxDB.Rebind(createProblemQuery)).WithArgs(title).WillReturnRows(rows)
|
tf.mock.ExpectQuery(tf.sqlxDB.Rebind(createProblemQuery)).
|
||||||
|
WithArgs(title).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
|
||||||
id, err := problemRepo.CreateProblem(context.Background(), title)
|
id, err := tf.problemRepo.CreateProblem(context.Background(), tf.problemRepo.DB(), title)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, int32(1), id)
|
require.Equal(t, int32(1), id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProblemRepository_ReadProblemById(t *testing.T) {
|
||||||
|
tf := newProblemTestFixture(t)
|
||||||
|
defer tf.cleanup()
|
||||||
|
|
||||||
|
t.Run("valid problem read", func(t *testing.T) {
|
||||||
|
id := int32(1)
|
||||||
|
rows := sqlmock.NewRows([]string{"id", "title"}).
|
||||||
|
AddRow(1, "Test Problem")
|
||||||
|
|
||||||
|
tf.mock.ExpectQuery(tf.sqlxDB.Rebind(readProblemQuery)).
|
||||||
|
WithArgs(id).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
|
||||||
|
problem, err := tf.problemRepo.ReadProblemById(context.Background(), tf.problemRepo.DB(), id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, problem)
|
||||||
|
require.Equal(t, int32(1), problem.Id)
|
||||||
|
require.Equal(t, "Test Problem", problem.Title)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestProblemRepository_DeleteProblem(t *testing.T) {
|
func TestProblemRepository_DeleteProblem(t *testing.T) {
|
||||||
t.Parallel()
|
tf := newProblemTestFixture(t)
|
||||||
|
defer tf.cleanup()
|
||||||
db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
sqlxDB := sqlx.NewDb(db, "sqlmock")
|
|
||||||
defer sqlxDB.Close()
|
|
||||||
|
|
||||||
problemRepo := NewProblemRepository(sqlxDB, zap.NewNop())
|
|
||||||
|
|
||||||
t.Run("valid problem deletion", func(t *testing.T) {
|
t.Run("valid problem deletion", func(t *testing.T) {
|
||||||
id := int32(1)
|
id := int32(1)
|
||||||
rows := sqlmock.NewResult(1, 1)
|
rows := sqlmock.NewResult(1, 1)
|
||||||
|
|
||||||
mock.ExpectExec(sqlxDB.Rebind(deleteProblemQuery)).WithArgs(id).WillReturnResult(rows)
|
tf.mock.ExpectExec(tf.sqlxDB.Rebind(deleteProblemQuery)).
|
||||||
|
WithArgs(id).
|
||||||
|
WillReturnResult(rows)
|
||||||
|
|
||||||
err = problemRepo.DeleteProblem(context.Background(), id)
|
err := tf.problemRepo.DeleteProblem(context.Background(), tf.problemRepo.DB(), id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProblemRepository_ListProblems(t *testing.T) {
|
||||||
|
tf := newProblemTestFixture(t)
|
||||||
|
defer tf.cleanup()
|
||||||
|
|
||||||
|
t.Run("valid problems list", func(t *testing.T) {
|
||||||
|
filter := models.ProblemsFilter{
|
||||||
|
Page: 1,
|
||||||
|
PageSize: 10,
|
||||||
|
}
|
||||||
|
listRows := sqlmock.NewRows([]string{"id", "title", "solved_count"}).
|
||||||
|
AddRow(1, "Problem 1", 5).
|
||||||
|
AddRow(2, "Problem 2", 3)
|
||||||
|
countRows := sqlmock.NewRows([]string{"count"}).AddRow(2)
|
||||||
|
|
||||||
|
tf.mock.ExpectQuery(tf.sqlxDB.Rebind(ListProblemsQuery)).
|
||||||
|
WithArgs(filter.PageSize, filter.Offset()).
|
||||||
|
WillReturnRows(listRows)
|
||||||
|
|
||||||
|
tf.mock.ExpectQuery(tf.sqlxDB.Rebind(CountProblemsQuery)).
|
||||||
|
WillReturnRows(countRows)
|
||||||
|
|
||||||
|
result, err := tf.problemRepo.ListProblems(context.Background(), tf.problemRepo.DB(), filter)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, result)
|
||||||
|
require.Len(t, result.Problems, 2)
|
||||||
|
require.Equal(t, int32(1), result.Pagination.Page)
|
||||||
|
require.Equal(t, int32(1), result.Pagination.Total)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProblemRepository_UpdateProblem(t *testing.T) {
|
||||||
|
tf := newProblemTestFixture(t)
|
||||||
|
defer tf.cleanup()
|
||||||
|
|
||||||
|
t.Run("valid problem update", func(t *testing.T) {
|
||||||
|
id := int32(1)
|
||||||
|
problemUpdate := models.ProblemUpdate{
|
||||||
|
Title: sp("Updated Title"),
|
||||||
|
TimeLimit: i32p(1000),
|
||||||
|
MemoryLimit: i32p(256),
|
||||||
|
}
|
||||||
|
rows := sqlmock.NewResult(1, 1)
|
||||||
|
|
||||||
|
tf.mock.ExpectExec(tf.sqlxDB.Rebind(UpdateProblemQuery)).
|
||||||
|
WithArgs(
|
||||||
|
problemUpdate.Title,
|
||||||
|
problemUpdate.TimeLimit,
|
||||||
|
problemUpdate.MemoryLimit,
|
||||||
|
nil, nil, nil, nil, nil,
|
||||||
|
nil, nil, nil, nil, nil,
|
||||||
|
id,
|
||||||
|
).
|
||||||
|
WillReturnResult(rows)
|
||||||
|
|
||||||
|
err := tf.problemRepo.UpdateProblem(context.Background(), tf.problemRepo.DB(), id, problemUpdate)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
2
proto
2
proto
|
@ -1 +1 @@
|
||||||
Subproject commit 1fbee7ba29c358c76d1c835ac6999ce9e1b59ee9
|
Subproject commit f00483d24a53a243734c793fc24e02d52d39fdab
|
Loading…
Add table
Reference in a new issue