Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion cmd/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ type Container struct {
TimecodeRepository TimecodeRepository
UserRepository UserRepository

YoutubeAPI *youtubeapi.Service
YoutubeAPI youtubeapi.IService
}
14 changes: 5 additions & 9 deletions cmd/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,21 @@ import (
"context"
"net/http"
"net/http/httptest"
"os"
"testing"

"github.com/khaiql/dbcleaner/engine"
"gopkg.in/khaiql/dbcleaner.v2"
)

var Cleaner = dbcleaner.New()
var TestDB = initDB()
func createDBCleaner(t *testing.T) dbcleaner.DbCleaner {
t.Helper()

func TestMain(m *testing.M) {
cleaner := dbcleaner.New()
dsn := getEnvDSN()
pg := engine.NewPostgresEngine(dsn.String())
Cleaner.SetEngine(pg)

runMigrations(TestDB)
defer TestDB.Close()
cleaner.SetEngine(pg)

os.Exit(m.Run())
return cleaner
}

func executeRequest(t *testing.T, router http.Handler, req *http.Request, user *User) *httptest.ResponseRecorder {
Expand Down
41 changes: 25 additions & 16 deletions cmd/timecode_like_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,48 @@ import (
"github.com/jinzhu/gorm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"gopkg.in/khaiql/dbcleaner.v2"
)

var tablesForCleaning = []string{
"timecode_likes",
"timecodes",
"users",
}

type TimecodeLikeRepositorySuite struct {
suite.Suite
DB *gorm.DB
Repo *DBTimecodeLikeRepository
Cleaner dbcleaner.DbCleaner
DB *gorm.DB
Repo *DBTimecodeLikeRepository
}

func (suite *TimecodeLikeRepositorySuite) SetupSuite() {
suite.DB = TestDB
suite.Repo = &DBTimecodeLikeRepository{DB: TestDB}
cleaner := createDBCleaner(suite.T())
db := initDB()
runMigrations(db)

suite.Cleaner = cleaner
suite.DB = db
suite.Repo = &DBTimecodeLikeRepository{DB: db}
}

func (suite *TimecodeLikeRepositorySuite) SetupTest() {
for _, table := range []string{
"timecode_likes",
"timecodes",
"users",
} {
Cleaner.Acquire(table)
for _, table := range tablesForCleaning {
suite.Cleaner.Acquire(table)
}
}

func (suite *TimecodeLikeRepositorySuite) TearDownTest() {
for _, table := range []string{
"timecode_likes",
"timecodes",
"users",
} {
Cleaner.Clean(table)
for _, table := range tablesForCleaning {
suite.Cleaner.Clean(table)
}
}

func (suite *TimecodeLikeRepositorySuite) TearDownSuite() {
suite.DB.Close()
}

func TestTimecodeLikeRepositorySuite(t *testing.T) {
suite.Run(t, new(TimecodeLikeRepositorySuite))
}
Expand Down
55 changes: 34 additions & 21 deletions cmd/timecode_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,41 @@ import (
"github.com/jinzhu/gorm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"gopkg.in/khaiql/dbcleaner.v2"
)

const videoID = "armenian-dram"
const anotherVideoID = "strategist"

type TimecodeRepositorySuite struct {
suite.Suite
DB *gorm.DB
Repo *DBTimecodeRepository

VideoID string
AnotherVideoID string
Cleaner dbcleaner.DbCleaner
DB *gorm.DB
Repo *DBTimecodeRepository
}

func (suite *TimecodeRepositorySuite) SetupSuite() {
suite.DB = TestDB
suite.Repo = &DBTimecodeRepository{DB: TestDB}
cleaner := createDBCleaner(suite.T())
db := initDB()
runMigrations(db)

suite.VideoID = "armenian-dram"
suite.AnotherVideoID = "strategist"
suite.Cleaner = cleaner
suite.DB = db
suite.Repo = &DBTimecodeRepository{DB: db}
}

func (suite *TimecodeRepositorySuite) SetupTest() {
Cleaner.Acquire("timecodes")
suite.Cleaner.Acquire("timecodes")
}

func (suite *TimecodeRepositorySuite) TearDownTest() {
Cleaner.Clean("timecodes")
suite.Cleaner.Clean("timecodes")
}

func (suite *TimecodeRepositorySuite) TearDownSuite() {
suite.DB.Close()
}

func TestTimecodeRepositorySuite(t *testing.T) {
Expand All @@ -39,22 +52,22 @@ func (suite *TimecodeRepositorySuite) TestDBTimecodeRepository_FindByVideoId() {
t := suite.T()

t.Run("when matching records exist", func(t *testing.T) {
suite.DB.Create(&Timecode{VideoID: videoID, Seconds: 55, Description: "ABC"})
suite.DB.Create(&Timecode{VideoID: videoID, Seconds: 23, Description: "DEFG"})
suite.DB.Create(&Timecode{VideoID: anotherVideoID, Seconds: 77, Description: "FGHJ"})
defer Cleaner.Clean("timecodes")
suite.DB.Create(&Timecode{VideoID: suite.VideoID, Seconds: 55, Description: "ABC"})
suite.DB.Create(&Timecode{VideoID: suite.VideoID, Seconds: 23, Description: "DEFG"})
suite.DB.Create(&Timecode{VideoID: suite.AnotherVideoID, Seconds: 77, Description: "FGHJ"})
defer suite.Cleaner.Clean("timecodes")

timecodes := *suite.Repo.FindByVideoId(videoID)
timecodes := *suite.Repo.FindByVideoId(suite.VideoID)

assert.Equal(t, 2, len(timecodes))
assert.Equal(t, 23, timecodes[0].Seconds)
assert.Equal(t, 55, timecodes[1].Seconds)
})

t.Run("when there are no matching records", func(t *testing.T) {
suite.DB.Create(&Timecode{VideoID: anotherVideoID, Seconds: 77, Description: "FGHJ"})
suite.DB.Create(&Timecode{VideoID: suite.AnotherVideoID, Seconds: 77, Description: "FGHJ"})

timecodes := *suite.Repo.FindByVideoId(videoID)
timecodes := *suite.Repo.FindByVideoId(suite.VideoID)

assert.Equal(t, 0, len(timecodes))
})
Expand All @@ -64,20 +77,20 @@ func (suite *TimecodeRepositorySuite) TestDBTimecodeRepository_Create() {
t := suite.T()

t.Run("when record has been created", func(t *testing.T) {
timecode, err := suite.Repo.Create(&Timecode{VideoID: videoID, Seconds: 55, Description: "ABC"})
timecode, err := suite.Repo.Create(&Timecode{VideoID: suite.VideoID, Seconds: 55, Description: "ABC"})

assert.Nil(t, err)
assert.NotNil(t, timecode.ID)
assert.Equal(t, videoID, timecode.VideoID)
assert.Equal(t, suite.VideoID, timecode.VideoID)
})

t.Run("when db returns an error", func(t *testing.T) {
seconds := 10
description := "ABC"

suite.DB.Create(&Timecode{VideoID: videoID, Seconds: seconds, Description: description})
suite.DB.Create(&Timecode{VideoID: suite.VideoID, Seconds: seconds, Description: description})

timecode, err := suite.Repo.Create(&Timecode{VideoID: videoID, Seconds: seconds, Description: description})
timecode, err := suite.Repo.Create(&Timecode{VideoID: suite.VideoID, Seconds: seconds, Description: description})

assert.True(t, suite.DB.NewRecord(timecode))
assert.EqualError(t, err, `pq: duplicate key value violates unique constraint "idx_timecodes_seconds_text_video_id"`)
Expand All @@ -94,7 +107,7 @@ func (suite *TimecodeRepositorySuite) TestDBTimecodeRepository_CreateFromParsedC
{Seconds: 56, Description: ""},
}

timecodes := *suite.Repo.CreateFromParsedCodes(parsedTimecodes, videoID)
timecodes := *suite.Repo.CreateFromParsedCodes(parsedTimecodes, suite.VideoID)

assert.Equal(t, 2, len(timecodes))
assert.Equal(t, 24, timecodes[0].Seconds)
Expand Down
4 changes: 3 additions & 1 deletion cmd/timecodes_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type TimecodeJSON struct {
ID uint `json:"id"`
Description string `json:"description"`
LikesCount int `json:"likesCount"`
LikedByMe bool `json:"likedByMe"`
LikedByMe bool `json:"likedByMe,omitempty"`
Seconds int `json:"seconds"`
VideoID string `json:"videoId"`
}
Expand Down Expand Up @@ -67,8 +67,10 @@ func handleCreateTimecode(c *Container, w http.ResponseWriter, r *http.Request)

_, err = c.TimecodeRepository.Create(timecode)
if err != nil {
w.WriteHeader(http.StatusUnprocessableEntity)
json.NewEncoder(w).Encode(err)
} else {
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(serializeTimecode(timecode, currentUser))
}
}
Expand Down
67 changes: 61 additions & 6 deletions cmd/timecodes_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@ package main

import (
"bytes"
"errors"
"net/http"
"testing"
"time"
timecodeParser "timecodes/cmd/timecode_parser"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

var mockTimecodeRepo = &MockTimecodeRepository{}
var mockYTAPI = &mockYT{}
var timecodesContainer = &Container{
YoutubeAPI: mockYTAPI,
TimecodeRepository: mockTimecodeRepo,
}
var timecodesRouter = createRouter(timecodesContainer)
Expand All @@ -28,7 +32,7 @@ func (m *MockTimecodeRepository) FindByVideoId(videoID string) *[]*Timecode {
return collection
}

collection = &[]*Timecode{{}, {}, {}}
collection = &[]*Timecode{{}, {}, {Likes: []TimecodeLike{{UserID: 1}}}}

return collection
}
Expand All @@ -47,12 +51,32 @@ func (m *MockTimecodeRepository) Create(timecode *Timecode) (*Timecode, error) {
return timecode, nil
}

func (m *MockTimecodeRepository) CreateFromParsedCodes(parsedCodes []timecodeParser.ParsedTimeCode, videoID string) *[]*Timecode {
args := m.Called(parsedCodes, videoID)
func (m *MockTimecodeRepository) CreateFromParsedCodes(parsedCodes []timecodeParser.ParsedTimeCode, videoId string) *[]*Timecode {
args := m.Called(parsedCodes, videoId)

return args.Get(0).(*[]*Timecode)
}

type mockYT struct {
mock.Mock
}

func (m *mockYT) FetchVideoDescription(videoId string) string {
args := m.Called(videoId)

_ = args.Get(0).(string)

return "description"
}

func (m *mockYT) FetchVideoComments(videoId string) []string {
args := m.Called(videoId)

_ = args.Get(0).([]string)

return []string{"comment one", "comment two"}
}

func Test_handleGetTimecodes(t *testing.T) {
currentUser := &User{}
currentUser.ID = 1
Expand All @@ -71,15 +95,23 @@ func Test_handleGetTimecodes(t *testing.T) {
})

t.Run("when timecodes don't exist", func(t *testing.T) {
t.Skip()
timecodes := &[]*Timecode{}
var emptyParsedCodes []timecodeParser.ParsedTimeCode

mockTimecodeRepo.On("FindByVideoId", "no-items").Return(timecodes, nil)
mockYTAPI.On("FetchVideoDescription", "no-items").Return("")
mockYTAPI.On("FetchVideoComments", "no-items").Return([]string{})
mockTimecodeRepo.
On("CreateFromParsedCodes", emptyParsedCodes, "no-items").
Return(timecodes, nil)

req, _ := http.NewRequest(http.MethodGet, "/timecodes/video-id", nil)
req, _ := http.NewRequest(http.MethodGet, "/timecodes/no-items", nil)

response := executeRequest(t, timecodesRouter, req, currentUser)

time.Sleep(1 * time.Millisecond)

mockYTAPI.AssertExpectations(t)
mockTimecodeRepo.AssertExpectations(t)
assert.Equal(t, http.StatusOK, response.Code)
})
Expand All @@ -100,6 +132,29 @@ func Test_handleCreateTimecode(t *testing.T) {
response := executeRequest(t, timecodesRouter, req, currentUser)

mockTimecodeRepo.AssertExpectations(t)
assert.Equal(t, http.StatusOK, response.Code)
assert.Equal(t, http.StatusCreated, response.Code)
})

t.Run("when request params are invalid", func(t *testing.T) {
timecode := &Timecode{VideoID: "video-id", Seconds: 71, Description: ""}

mockTimecodeRepo.On("Create", timecode).Return(nil, errors.New(""))

params := []byte(`{ "videoId": "video-id", "seconds": "1:11", "description": "" }`)
req, _ := http.NewRequest(http.MethodPost, "/auth/timecodes", bytes.NewBuffer(params))

response := executeRequest(t, timecodesRouter, req, currentUser)

mockTimecodeRepo.AssertExpectations(t)
assert.Equal(t, http.StatusUnprocessableEntity, response.Code)
})

t.Run("when request params contain invalid JSON", func(t *testing.T) {
params := []byte(`{ "Invalid json }`)
req, _ := http.NewRequest(http.MethodPost, "/auth/timecodes", bytes.NewBuffer(params))

response := executeRequest(t, timecodesRouter, req, currentUser)

assert.Equal(t, http.StatusBadRequest, response.Code)
})
}
Loading