Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2e4ad56
gitignore編集
KOUTYA47 Feb 18, 2025
ead81cb
go環境変更
KOUTYA47 Feb 19, 2025
249243e
scoreモックの実装、正常系テストの実装
KOUTYA47 Feb 19, 2025
09215b6
メッセージの定数化
KOUTYA47 Feb 19, 2025
bd2193d
メッセージ定数化の修正
KOUTYA47 Feb 19, 2025
49bec58
GetScoresRankingテストの追加
KOUTYA47 Feb 19, 2025
6f8e39f
Merge branch 'test/handler/score' of https://github.com/su-its/typing…
KOUTYA47 Feb 19, 2025
2dfe92d
registerScoreテストケースの追加
KOUTYA47 Feb 20, 2025
2caea48
sort_by=accuracyの場合のテストケースの追加
KOUTYA47 Feb 20, 2025
5533464
余分なコメントアウト除去
KOUTYA47 Feb 20, 2025
5a18790
chore: go mod tidy
KOUTYA47 Feb 19, 2025
e9574d4
tests: scoreモックの実装、正常系テストの実装
KOUTYA47 Feb 19, 2025
96ddde5
refactor: メッセージの定数化
KOUTYA47 Feb 19, 2025
a8cb324
fix: メッセージ定数化の修正
KOUTYA47 Feb 19, 2025
a780b32
tests: GetScoresRankingテストの追加
KOUTYA47 Feb 19, 2025
dda40b1
tests: registerScoreテストケースの追加
KOUTYA47 Feb 20, 2025
106b5fd
tests: sort_by=accuracyの場合のテストケースの追加
KOUTYA47 Feb 20, 2025
00bf1e3
chore: 余分なコメントアウト除去
KOUTYA47 Feb 20, 2025
8f983f9
tests: score_serviceインスタンス生成テストケース追加
KOUTYA47 Feb 20, 2025
bedfc53
testsValidateScoreメソッドのテストケース追加
KOUTYA47 Feb 20, 2025
cc21106
tests: ComputeRankingテストケース追加
KOUTYA47 Feb 20, 2025
6818d64
tests ShouldUpdateMaxScoreテストケース追加
KOUTYA47 Feb 20, 2025
addc4b5
tests: ComputeRankingソート順テストの修正
KOUTYA47 Feb 20, 2025
cdd1f7b
Merge commit '5533464964c0cf4878055ea271070bc1da81d078' into test/ser…
KinjiKawaguchi Feb 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions typing-server/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,22 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70=
Expand Down
312 changes: 301 additions & 11 deletions typing-server/internal/domain/service/score_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,34 @@ package service

import (
"context"
"errors"
"reflect"
"strings"
"testing"

"github.com/google/uuid"
"github.com/su-its/typing/typing-server/internal/domain/model"
"github.com/su-its/typing/typing-server/internal/domain/repository"
)

type mockScoreRepository struct {
getScores func(ctx context.Context, sortBy string, start int, limit int) ([]*model.Score, int, error)
getMaxScores func(ctx context.Context, userID uuid.UUID) (*model.Score, *model.Score, error)
createScore func(ctx context.Context, userID uuid.UUID, keystrokes int, accuracy float64, isMaxKeystrokes bool, isMaxAccuracy bool) error
}

func (m *mockScoreRepository) GetScores(ctx context.Context, sortBy string, start int, limit int) ([]*model.Score, int, error) {
return m.getScores(ctx, sortBy, start, limit)
}

func (m *mockScoreRepository) GetMaxScores(ctx context.Context, userID uuid.UUID) (*model.Score, *model.Score, error) {
return m.getMaxScores(ctx, userID)
}

func (m *mockScoreRepository) CreateScore(ctx context.Context, userID uuid.UUID, keystrokes int, accuracy float64, isMaxKeystrokes bool, isMaxAccuracy bool) error {
return m.createScore(ctx, userID, keystrokes, accuracy, isMaxKeystrokes, isMaxAccuracy)
}

func TestNewScoreService(t *testing.T) {
type args struct {
scoreRepo repository.ScoreRepository
Expand All @@ -19,7 +39,15 @@ func TestNewScoreService(t *testing.T) {
args args
want *ScoreService
}{
// TODO: Add test cases.
{
name: "正常系: インスタンスが正しく生成される",
args: args{
scoreRepo: &mockScoreRepository{},
},
want: &ScoreService{
scoreRepo: &mockScoreRepository{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -37,17 +65,90 @@ func TestScoreService_ValidateScore(t *testing.T) {
accuracy float64
}
tests := []struct {
name string
s *ScoreService
args args
wantErr bool
name string
s *ScoreService
args args
wantErr bool
wantErrMsg string
}{
// TODO: Add test cases.
{
name: "正常系: 正しいパラメータの場合",
s: &ScoreService{
scoreRepo: &mockScoreRepository{},
},
args: args{
userID: uuid.New(),
keystrokes: 100,
accuracy: 0.5,
},
wantErr: false,
},
{
name: "異常系: keystrokes が負の場合",
s: &ScoreService{
scoreRepo: &mockScoreRepository{},
},
args: args{
userID: uuid.New(),
keystrokes: -10,
accuracy: 0.5,
},
wantErr: true,
wantErrMsg: "keystrokes must be non-negative",
},
{
name: "異常系: accuracy が 0未満の場合",
s: &ScoreService{
scoreRepo: &mockScoreRepository{},
},
args: args{
userID: uuid.New(),
keystrokes: 100,
accuracy: -0.1,
},
wantErr: true,
},
{
name: "異常系: accuracy が 1 を超える場合",
s: &ScoreService{
scoreRepo: &mockScoreRepository{},
},
args: args{
userID: uuid.New(),
keystrokes: 100,
accuracy: 1.1,
},
wantErr: true,
wantErrMsg: "accuracy must be between 0 and 1",
},
{
name: "異常系: userID が uuid.Nilの場合",
s: &ScoreService{
scoreRepo: &mockScoreRepository{},
},
args: args{
userID: uuid.Nil,
keystrokes: 100,
accuracy: 0.5,
},
wantErr: true,
wantErrMsg: "invalid user ID\n",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.s.ValidateScore(tt.args.userID, tt.args.keystrokes, tt.args.accuracy); (err != nil) != tt.wantErr {
t.Errorf("ScoreService.ValidateScore() error = %v, wantErr %v", err, tt.wantErr)
err := tt.s.ValidateScore(tt.args.userID, tt.args.keystrokes, tt.args.accuracy)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateScore() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.wantErrMsg != "" {
if err == nil {
t.Fatalf("expected error %q, but got nil", tt.wantErrMsg)
}

if !strings.Contains(err.Error(), strings.TrimSpace(tt.wantErrMsg)) {
t.Errorf("expected error message %q, but got %q", tt.wantErrMsg, err.Error())
}
}
})
}
Expand All @@ -65,12 +166,90 @@ func TestScoreService_ComputeRanking(t *testing.T) {
args args
want []*model.ScoreRanking
}{
// TODO: Add test cases.
{
name: "正常系: keystrokes で降順ソート、 start=1の場合",
s: &ScoreService{},
args: args{
scores: []*model.Score{
{
ID: "s1",
Keystrokes: 200,
Accuracy: 0.90,
},
{
ID: "s2",
Keystrokes: 300,
Accuracy: 0.85,
},
{
ID: "s3",
Keystrokes: 100,
Accuracy: 0.95,
},
},
sortBy: "keystrokes",
start: 1,
},
want: []*model.ScoreRanking{
// s2: keystrokes=300
{Rank: 1, Score: model.Score{ID: "s2", Keystrokes: 300, Accuracy: 0.85}},
// s1: keystrokes=200
{Rank: 2, Score: model.Score{ID: "s1", Keystrokes: 200, Accuracy: 0.90}},
// s3: keystrokes=100
{Rank: 3, Score: model.Score{ID: "s3", Keystrokes: 100, Accuracy: 0.95}},
},
},
{
name: "正常系: accuracy で降順ソート、 start=1, 重複accuracyありの場合",
s: &ScoreService{},
args: args{
scores: []*model.Score{
{
ID: "s1",
Keystrokes: 200,
Accuracy: 0.90,
},
{
ID: "s2",
Keystrokes: 500,
Accuracy: 0.90, // s1 と同じ accuracy
},
{
ID: "s3",
Keystrokes: 100,
Accuracy: 0.95,
},
{
ID: "s4",
Keystrokes: 300,
Accuracy: 0.85,
},
},
sortBy: "accuracy",
start: 1,
},
want: []*model.ScoreRanking{
// s3: accuracy=0.95
{Rank: 1, Score: model.Score{ID: "s3", Keystrokes: 100, Accuracy: 0.95}},
// s1: accuracy=0.90 (上から2番目)
{Rank: 2, Score: model.Score{ID: "s1", Keystrokes: 200, Accuracy: 0.90}},
// s2: accuracy=0.90 (上から2番目)
{Rank: 2, Score: model.Score{ID: "s2", Keystrokes: 500, Accuracy: 0.90}},
//rank2で重複があったためrank4になる
{Rank: 4, Score: model.Score{ID: "s4", Keystrokes: 300, Accuracy: 0.85}},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.s.ComputeRanking(tt.args.scores, tt.args.sortBy, tt.args.start); !reflect.DeepEqual(got, tt.want) {
t.Errorf("ScoreService.ComputeRanking() = %v, want %v", got, tt.want)
t.Errorf("ScoreService.ComputeRanking() = %+v, want %+v", got, tt.want)
for i := range got {
t.Logf("got[%d] = %+v", i, got[i])
}
for i := range tt.want {
t.Logf("want[%d] = %+v", i, tt.want[i])
}
}
})
}
Expand All @@ -90,7 +269,118 @@ func TestScoreService_ShouldUpdateMaxScore(t *testing.T) {
want1 bool
wantErr bool
}{
// TODO: Add test cases.
{
name: "正常系: GetMaxScores が nil を返す場合",
s: &ScoreService{
scoreRepo: &mockScoreRepository{
getMaxScores: func(ctx context.Context, userID uuid.UUID) (*model.Score, *model.Score, error) {
// まだ最大スコアが登録されていない状態
return nil, nil, nil
},
},
},
args: args{
ctx: context.Background(),
userID: uuid.New(),
newScore: &model.Score{
Keystrokes: 100,
Accuracy: 0.8,
},
},
want: true, // isMaxKeystrokes
want1: true, // isMaxAccuracy
wantErr: false,
},
{
name: "正常系: 新しいスコアが既存より keystrokes だけ大きい場合",
s: &ScoreService{
scoreRepo: &mockScoreRepository{
getMaxScores: func(ctx context.Context, userID uuid.UUID) (*model.Score, *model.Score, error) {
return &model.Score{Keystrokes: 90, Accuracy: 0.8}, // maxKeystrokeScore
&model.Score{Keystrokes: 50, Accuracy: 0.9}, // maxAccuracyScore
nil
},
},
},
args: args{
ctx: context.Background(),
userID: uuid.New(),
newScore: &model.Score{
Keystrokes: 100,
Accuracy: 0.8, // 既存 =0.9 より低い
},
},
want: true,
want1: false,
wantErr: false,
},
{
name: "正常系: 新しいスコアが既存より accuracy だけ高い場合",
s: &ScoreService{
scoreRepo: &mockScoreRepository{
getMaxScores: func(ctx context.Context, userID uuid.UUID) (*model.Score, *model.Score, error) {
return &model.Score{Keystrokes: 200, Accuracy: 0.7},
&model.Score{Keystrokes: 100, Accuracy: 0.8},
nil
},
},
},
args: args{
ctx: context.Background(),
userID: uuid.New(),
newScore: &model.Score{
Keystrokes: 150, // 既存 200 より低い
Accuracy: 0.85,
},
},
want: false,
want1: true,
wantErr: false,
},
{
name: "正常系: どちらも既存より高い場合",
s: &ScoreService{
scoreRepo: &mockScoreRepository{
getMaxScores: func(ctx context.Context, userID uuid.UUID) (*model.Score, *model.Score, error) {
return &model.Score{Keystrokes: 200, Accuracy: 0.8},
&model.Score{Keystrokes: 150, Accuracy: 0.85},
nil
},
},
},
args: args{
ctx: context.Background(),
userID: uuid.New(),
newScore: &model.Score{
Keystrokes: 300,
Accuracy: 0.9,
},
},
want: true,
want1: true,
wantErr: false,
},
{
name: "異常系: リポジトリがエラーを返す場合",
s: &ScoreService{
scoreRepo: &mockScoreRepository{
getMaxScores: func(ctx context.Context, userID uuid.UUID) (*model.Score, *model.Score, error) {
return nil, nil, errors.New("db error")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repositoryErrorなのにdbとしているところは気になります。
どうでも良いですが。

},
},
},
args: args{
ctx: context.Background(),
userID: uuid.New(),
newScore: &model.Score{
Keystrokes: 300,
Accuracy: 0.9,
},
},
want: false,
want1: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
Loading
Loading