test(tester): fix tests

This commit is contained in:
Vyacheslav1557 2025-04-12 00:12:28 +05:00
parent 2ab7a16ddf
commit 4fb0b80f24
6 changed files with 1500 additions and 181 deletions

5
go.mod
View file

@ -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
View file

@ -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=

View file

@ -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

View file

@ -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

@ -1 +1 @@
Subproject commit 1fbee7ba29c358c76d1c835ac6999ce9e1b59ee9 Subproject commit f00483d24a53a243734c793fc24e02d52d39fdab