From 4e4201f7d286d3bdc3e35d0f937d95510d7900fd Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Fri, 31 Dec 2021 19:25:22 +0100 Subject: [PATCH] Add test coverage for getPeaksFromFileStore flow --- backend/media/get_audio_test.go | 136 ++++++++++++++++++++++++++++++ backend/media/get_segment_test.go | 33 ++++---- 2 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 backend/media/get_audio_test.go diff --git a/backend/media/get_audio_test.go b/backend/media/get_audio_test.go new file mode 100644 index 0000000..662adca --- /dev/null +++ b/backend/media/get_audio_test.go @@ -0,0 +1,136 @@ +package media_test + +import ( + "context" + "database/sql" + "errors" + "io" + "testing" + "time" + + "git.netflux.io/rob/clipper/config" + "git.netflux.io/rob/clipper/generated/mocks" + "git.netflux.io/rob/clipper/generated/store" + "git.netflux.io/rob/clipper/media" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.uber.org/zap" +) + +func TestGetPeaksFromFileStore(t *testing.T) { + const inFixturePath = "testdata/tone-44100-stereo-int16-30000ms.raw" + + ctx := context.Background() + logger := zap.NewNop().Sugar() + mediaSetID := uuid.New() + mediaSet := store.MediaSet{ + ID: mediaSetID, + AudioChannels: 2, + AudioFrames: sql.NullInt64{Int64: 1_323_000, Valid: true}, + AudioRawS3Key: sql.NullString{String: "raw audio key", Valid: true}, + AudioRawS3UploadedAt: sql.NullTime{Time: time.Now(), Valid: true}, + AudioEncodedS3Key: sql.NullString{String: "encoded audio key", Valid: true}, + AudioEncodedS3UploadedAt: sql.NullTime{Time: time.Now(), Valid: true}, + } + + t.Run("NOK,ErrorFetchingMediaSet", func(t *testing.T) { + var mockStore mocks.Store + mockStore.On("GetMediaSet", mock.Anything, mediaSetID).Return(store.MediaSet{}, errors.New("db went boom")) + service := media.NewMediaSetService(&mockStore, nil, nil, nil, config.Config{}, logger) + _, err := service.GetPeaks(ctx, mediaSetID, 10) + assert.EqualError(t, err, "error getting media set: db went boom") + }) + + t.Run("NOK,ErrorGettingObjectFromFileStore", func(t *testing.T) { + var mockStore mocks.Store + mockStore.On("GetMediaSet", mock.Anything, mediaSetID).Return(mediaSet, nil) + defer mockStore.AssertExpectations(t) + + var fileStore mocks.FileStore + fileStore.On("GetObject", mock.Anything, "raw audio key").Return(nil, errors.New("boom")) + + service := media.NewMediaSetService(&mockStore, nil, &fileStore, nil, config.Config{}, logger) + _, err := service.GetPeaks(ctx, mediaSetID, 10) + require.EqualError(t, err, "error getting object from file store: boom") + }) + + t.Run("NOK,ErrorGettingObjectURL", func(t *testing.T) { + var mockStore mocks.Store + mockStore.On("GetMediaSet", mock.Anything, mediaSetID).Return(mediaSet, nil) + defer mockStore.AssertExpectations(t) + + var fileStore mocks.FileStore + reader := fixtureReader(t, inFixturePath, 5_292_000) + fileStore.On("GetObject", mock.Anything, "raw audio key").Return(reader, nil) + fileStore.On("GetURL", mock.Anything, "encoded audio key").Return("", errors.New("network error")) + defer fileStore.AssertExpectations(t) + + service := media.NewMediaSetService(&mockStore, nil, &fileStore, nil, config.Config{}, logger) + stream, err := service.GetPeaks(ctx, mediaSetID, 10) + require.NoError(t, err) + + var hadError bool + for { + _, err := stream.Next() + if err != nil { + hadError = true + assert.EqualError(t, err, "error waiting for progress: error generating object URL: network error") + break + } + } + assert.True(t, hadError) + }) + + t.Run("OK", func(t *testing.T) { + var mockStore mocks.Store + mockStore.On("GetMediaSet", mock.Anything, mediaSetID).Return(mediaSet, nil) + defer mockStore.AssertExpectations(t) + + var fileStore mocks.FileStore + reader := fixtureReader(t, inFixturePath, 5_292_000) + fileStore.On("GetObject", mock.Anything, "raw audio key").Return(reader, nil) + fileStore.On("GetURL", mock.Anything, "encoded audio key").Return("https://www.example.com/foo", nil) + defer fileStore.AssertExpectations(t) + + numBins := 10 + service := media.NewMediaSetService(&mockStore, nil, &fileStore, nil, config.Config{}, logger) + stream, err := service.GetPeaks(ctx, mediaSetID, numBins) + require.NoError(t, err) + + lastPeaks := make([]int16, 2) // stereo + var ( + count int + lastPercentComplete float32 + lastURL string + ) + + for { + progress, err := stream.Next() + if err != io.EOF { + require.NoError(t, err) + } + + assert.Len(t, progress.Peaks, 2) + assert.GreaterOrEqual(t, progress.PercentComplete, lastPercentComplete) + lastPercentComplete = progress.PercentComplete + lastURL = progress.URL + + if err == io.EOF { + break + } + + // the fixture is a tone gradually increasing in amplitude: + assert.Greater(t, progress.Peaks[0], lastPeaks[0]) + assert.Greater(t, progress.Peaks[1], lastPeaks[1]) + lastPeaks = progress.Peaks + count++ + } + + assert.Equal(t, float32(100), lastPercentComplete) + assert.Equal(t, []int16{32_767, 32_766}, lastPeaks) + assert.Equal(t, numBins, count) + assert.Equal(t, "https://www.example.com/foo", lastURL) + }) +} diff --git a/backend/media/get_segment_test.go b/backend/media/get_segment_test.go index 2b1d0bd..9b7d537 100644 --- a/backend/media/get_segment_test.go +++ b/backend/media/get_segment_test.go @@ -24,7 +24,19 @@ import ( "go.uber.org/zap" ) -const inFixturePath = "testdata/tone-44100-stereo-int16-30000ms.raw" +func fixtureReader(t *testing.T, fixturePath string, limit int64) io.ReadCloser { + fptr, err := os.Open(fixturePath) + require.NoError(t, err) + + // limitReader to make the mock work realistically, not intended for assertions: + return struct { + io.Reader + io.Closer + }{ + Reader: io.LimitReader(fptr, limit), + Closer: fptr, + } +} func helperCommand(t *testing.T, wantCommand, stdoutFile, stderrString string, forceExitCode int) media.CommandFunc { return func(ctx context.Context, name string, args ...string) *exec.Cmd { @@ -85,22 +97,9 @@ func TestHelperProcess(t *testing.T) { } } -func fixtureReader(t *testing.T, limit int64) io.ReadCloser { - fptr, err := os.Open(inFixturePath) - require.NoError(t, err) - - // limitReader to make the mock work realistically, not intended for assertions: - return struct { - io.Reader - io.Closer - }{ - Reader: io.LimitReader(fptr, limit), - Closer: fptr, - } -} - func TestGetSegment(t *testing.T) { mediaSetID := uuid.MustParse("4c440241-cca9-436f-adb0-be074588cf2b") + const inFixturePath = "testdata/tone-44100-stereo-int16-30000ms.raw" t.Run("invalid range", func(t *testing.T) { var mockStore mocks.Store @@ -148,7 +147,7 @@ func TestGetSegment(t *testing.T) { var fileStore mocks.FileStore fileStore.On("GetObjectWithRange", mock.Anything, mock.Anything, mock.Anything, mock.Anything). - Return(fixtureReader(t, 1), nil) + Return(fixtureReader(t, inFixturePath, 1), nil) cmd := helperCommand(t, "", "", "something bad happened", 2) service := media.NewMediaSetService(&mockStore, nil, &fileStore, cmd, config.Config{}, zap.NewNop().Sugar()) @@ -233,7 +232,7 @@ func TestGetSegment(t *testing.T) { var fileStore mocks.FileStore fileStore. On("GetObjectWithRange", mock.Anything, "media_sets/4c440241-cca9-436f-adb0-be074588cf2b/audio.raw", tc.wantStartByte, tc.wantEndByte). - Return(fixtureReader(t, tc.wantEndByte-tc.wantStartByte), nil) + Return(fixtureReader(t, inFixturePath, tc.wantEndByte-tc.wantStartByte), nil) defer fileStore.AssertExpectations(t) cmd := helperCommand(t, tc.wantCommand, tc.outFixturePath, "", 0)