diff --git a/internal/api/rest.go b/internal/api/rest.go index ff5a85d..5344f9b 100644 --- a/internal/api/rest.go +++ b/internal/api/rest.go @@ -116,16 +116,20 @@ func NewRestGateway( middleware.LoggerWithConfig(middleware.LoggerConfig{ Format: "[Request] ${time_rfc3339} :: ${method} ${uri} -> ${status} ${error} {ip=${remote_ip}, user_agent=${user_agent}}\n", }), - middleware.CORSWithConfig(middleware.CORSConfig{ - AllowOrigins: []string{"*"}, - // AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept, echo.HeaderAccessControlAllowOrigin}, - // AllowMethods: []string{echo.OPTIONS, echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE}, - }), - authProvider.GetSecurityValidatorMiddleware(apiBasePath), + // middleware.CORSWithConfig(middleware.CORSConfig{ + // AllowOrigins: []string{"*"}, + // AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept, echo.HeaderAccessControlAllowOrigin}, + // AllowMethods: []string{echo.OPTIONS, echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE}, + // }), ) // -- Setup gateway -- socket := websocket.New() + ec.GET(apiBasePath+"/activity/ws", func(c echo.Context) error { + // TODO authentication things here pls + socket.UpgradeToSocket(c.Response(), c.Request()) + return nil + }) gateway := &RestGateway{ broadcaster: newBroadcaster(socket, ingestService, transcodeService, store), config: config, @@ -143,7 +147,8 @@ func NewRestGateway( workflows.New(store), }, []gen.StrictMiddlewareFunc{requestBodyValidatorMiddleware}) - gen.RegisterHandlersWithBaseURL(ec, serverImpl, apiBasePath) + authenticatedGroup := ec.Group(apiBasePath, authProvider.GetSecurityValidatorMiddleware(apiBasePath)) + gen.RegisterHandlers(authenticatedGroup, serverImpl) return gateway } diff --git a/internal/ingest/service_test.go b/internal/ingest/service_test.go index 53e617e..fbe30d3 100644 --- a/internal/ingest/service_test.go +++ b/internal/ingest/service_test.go @@ -10,7 +10,6 @@ import ( "encoding/json" "errors" "fmt" - "os" "reflect" "sync" "testing" @@ -23,6 +22,7 @@ import ( mocks "github.com/hbomb79/Thea/internal/ingest/mocks" "github.com/hbomb79/Thea/internal/media" "github.com/hbomb79/Thea/pkg/logger" + "github.com/hbomb79/Thea/tests/helpers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -79,30 +79,9 @@ func startService(t *testing.T, config ingest.Config, searcherMock *mocks.MockSe return startServiceWithBus(t, config, searcherMock, scraperMock, storeMock, defaultEventBus) } -func tempDir(t *testing.T) string { - tempDir, err := os.MkdirTemp("", "thea_ingest_test") - assert.Nil(t, err, "failed to create temporary dir") - t.Cleanup(func() { os.RemoveAll(tempDir) }) - - return tempDir -} - -func tempDirWithFiles(t *testing.T, files []string) (string, []string) { - dirPath := tempDir(t) - filePaths := make([]string, 0, len(files)) - for _, filename := range files { - fileName, err := os.CreateTemp(dirPath, filename) - assert.Nil(t, err, "failed to create temporary file in temporary dir") - filePaths = append(filePaths, fileName.Name()) - } - - assert.Len(t, filePaths, len(files), "Expected file paths recorded to match length of requested files") - return dirPath, filePaths -} - func Test_EpisodeImports_CorrectlySaved(t *testing.T) { t.Parallel() - tempDir, files := tempDirWithFiles(t, []string{"episode"}) + tempDir, files := helpers.TempDirWithFiles(t, []string{"episode"}) cfg := ingest.Config{ForceSyncSeconds: 100, IngestPath: tempDir, IngestionParallelism: 1} searcherMock := mocks.NewMockSearcher(t) @@ -201,7 +180,7 @@ func Test_EpisodeImports_CorrectlySaved(t *testing.T) { func Test_MovieImports_CorrectlySaved(t *testing.T) { t.Parallel() - tempDir, files := tempDirWithFiles(t, []string{"movie"}) + tempDir, files := helpers.TempDirWithFiles(t, []string{"movie"}) cfg := ingest.Config{ForceSyncSeconds: 100, IngestPath: tempDir, IngestionParallelism: 1} searcherMock := mocks.NewMockSearcher(t) @@ -285,7 +264,7 @@ func Test_MovieImports_CorrectlySaved(t *testing.T) { func Test_NewFile_IgnoredIfAlreadyImported(t *testing.T) { t.Parallel() - tempDir, files := tempDirWithFiles(t, []string{"anynameworks"}) + tempDir, files := helpers.TempDirWithFiles(t, []string{"anynameworks"}) cfg := ingest.Config{ForceSyncSeconds: 100, IngestPath: tempDir, RequiredModTimeAgeSeconds: 2, IngestionParallelism: 1} searcherMock := mocks.NewMockSearcher(t) @@ -305,7 +284,7 @@ func Test_NewFile_CorrectlyHeld(t *testing.T) { t.Parallel() // Construct a new ingest service with the import delay set to a low value // and noop mocks for the dependencies. - tempDir, files := tempDirWithFiles(t, []string{"anynameworks"}) + tempDir, files := helpers.TempDirWithFiles(t, []string{"anynameworks"}) cfg := ingest.Config{ForceSyncSeconds: 100, IngestPath: tempDir, RequiredModTimeAgeSeconds: 2, IngestionParallelism: 1} searcherMock := mocks.NewMockSearcher(t) @@ -347,7 +326,7 @@ func Test_NewFile_CorrectlyHeld(t *testing.T) { func Test_PollsFilesystemPeriodically(t *testing.T) { t.Parallel() - tempDir := tempDir(t) + tempDir := t.TempDir() cfg := ingest.Config{ForceSyncSeconds: 1, IngestPath: tempDir, RequiredModTimeAgeSeconds: 2, IngestionParallelism: 1} searcherMock := mocks.NewMockSearcher(t) diff --git a/tests/helpers/fs.go b/tests/helpers/fs.go new file mode 100644 index 0000000..bbbd045 --- /dev/null +++ b/tests/helpers/fs.go @@ -0,0 +1,21 @@ +package helpers + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TempDirWithFiles(t *testing.T, files []string) (string, []string) { + dirPath := t.TempDir() + filePaths := make([]string, 0, len(files)) + for _, filename := range files { + fileName, err := os.CreateTemp(dirPath, filename) + assert.Nil(t, err, "failed to create temporary file in temporary dir") + filePaths = append(filePaths, fileName.Name()) + } + + assert.Len(t, filePaths, len(files), "Expected file paths recorded to match length of requested files") + return dirPath, filePaths +} diff --git a/tests/helpers/service.go b/tests/helpers/service.go index 64f6cfe..9cc6fe6 100644 --- a/tests/helpers/service.go +++ b/tests/helpers/service.go @@ -4,9 +4,12 @@ import ( "context" "fmt" "net/http" + "net/http/cookiejar" "os" "testing" + "time" + "github.com/gorilla/websocket" "github.com/hbomb79/Thea/internal/user/permissions" "github.com/hbomb79/Thea/tests/gen" "github.com/labstack/gommon/random" @@ -15,8 +18,9 @@ import ( ) const ( - ServerBasePathTemplate = "http://localhost:%d/api/thea/v1/" + ServerBasePathTemplate = "%s://localhost:%d/api/thea/v1/" LoginCookiesCount = 2 + ActivityPath = "activity/ws" ) var ( @@ -57,7 +61,26 @@ type TestService struct { } func (service *TestService) GetServerBasePath() string { - return fmt.Sprintf(ServerBasePathTemplate, service.Port) + return fmt.Sprintf(ServerBasePathTemplate, "http", service.Port) +} + +func (service *TestService) GetActivityURL() string { + return fmt.Sprintf("%s%s", fmt.Sprintf(ServerBasePathTemplate, "ws", service.Port), ActivityPath) +} + +func (service *TestService) ConnectToActivitySocket(t *testing.T) *websocket.Conn { + jar, err := cookiejar.New(nil) + if err != nil { + t.Fatalf("failed to connect to activity socket: failed to create cookie jar: %s", err) + } + + dialer := websocket.Dialer{HandshakeTimeout: 5 * time.Second, Jar: jar} + ws, resp, err := dialer.Dial(service.GetActivityURL(), make(map[string][]string)) + if err != nil { + t.Fatalf("failed to connect to activity socket: %s", err) + } + t.Logf("Connected: %v [%v]", ws, resp) + return ws } func (service *TestService) String() string { @@ -70,6 +93,7 @@ func (service *TestService) String() string { type TestUser struct { User gen.User Password string + Cookies []*http.Cookie } func (service *TestService) NewClient(t *testing.T) *gen.ClientWithResponses { @@ -102,7 +126,7 @@ func (service *TestService) NewClientWithCredentials(t *testing.T, username stri authClient, err := gen.NewClientWithResponses(service.GetServerBasePath(), gen.WithRequestEditorFn(WithCookies(cookies))) assert.Nil(t, err) - return TestUser{User: *resp.JSON200, Password: password}, authClient + return TestUser{User: *resp.JSON200, Password: password, Cookies: cookies}, authClient } // NewClientWithRandomUser creates a new user using a default API client, diff --git a/tests/integration/ingest_test.go b/tests/integration/ingest_test.go new file mode 100644 index 0000000..f6bba2f --- /dev/null +++ b/tests/integration/ingest_test.go @@ -0,0 +1,21 @@ +package integration_test + +import ( + "testing" + + "github.com/hbomb79/Thea/tests/helpers" + "github.com/stretchr/testify/assert" +) + +// TestIngestion_FailsToMetadataScrape ensures that files +// which are not valid media files correctly reports failure. Retrying +// these ingestions should see the same error return. +func TestIngestion_FailsToMetadataScrape(t *testing.T) { + tempDir, _ := helpers.TempDirWithFiles(t, []string{"thisisnotavalidfile.mp4"}) + srvReq := helpers.NewTheaContainerRequest().WithDatabaseName(t.Name()).WithIngestDirectory(tempDir) + srv := helpers.RequireThea(t, srvReq) + + // Connect to the websocket + ws := srv.ConnectToActivitySocket(t) + assert.NotNil(t, ws, "ahh") +}