From c68c54b0212ebc8d111b11c6edff82942c3a4463 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 4 May 2026 00:10:32 +0300 Subject: task 6: narrow service interfaces in Server and Middleware Split Server struct to accept narrow service interfaces instead of fat composites (MediaService, AdminService). Each handler now depends only on its specific slice (MediaBrowseService, MediaWriteService, etc.). Split Middleware to depend on a narrow UserStore interface instead of full repository.Store. Updated all constructors, call sites, and tests. Added negative tests for nil AdminService and ProgressService returning 501 Not Implemented. References task 6. --- internal/api/handlers_file.go | 16 ++-- internal/api/handlers_media.go | 60 +++++++------- internal/api/handlers_more_test.go | 163 +++++++++++++++++++++++++------------ internal/api/handlers_share.go | 32 ++++---- internal/api/handlers_test.go | 77 ++++++++++-------- internal/api/middleware.go | 11 ++- internal/api/server.go | 32 ++++++-- 7 files changed, 241 insertions(+), 150 deletions(-) (limited to 'internal/api') diff --git a/internal/api/handlers_file.go b/internal/api/handlers_file.go index d8bb14f..3228962 100644 --- a/internal/api/handlers_file.go +++ b/internal/api/handlers_file.go @@ -41,14 +41,14 @@ func (s *Server) fileHandler(fn func(context.Context, int64, int64) (*service.Fi } func (s *Server) handleStream(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.browseSvc) { return } - s.fileHandler(s.mediaSvc.StreamMedia)(w, r) + s.fileHandler(s.browseSvc.StreamMedia)(w, r) } func (s *Server) handleDownload(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.browseSvc) { return } id := pathID(r, "id") @@ -56,7 +56,7 @@ func (s *Server) handleDownload(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid media id"}) return } - res, err := s.mediaSvc.DownloadMedia(r.Context(), id, userIDFromContext(r)) + res, err := s.browseSvc.DownloadMedia(r.Context(), id, userIDFromContext(r)) if err != nil { if errors.Is(err, service.ErrNotFound) { writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"}) @@ -77,15 +77,15 @@ func (s *Server) handleDownload(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleThumbnail(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.browseSvc) { return } w.Header().Set("Cache-Control", "no-cache") - s.fileHandler(s.mediaSvc.GetThumbnail)(w, r) + s.fileHandler(s.browseSvc.GetThumbnail)(w, r) } func (s *Server) handleRegenThumbnail(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.browseSvc) { return } id := pathID(r, "id") @@ -93,7 +93,7 @@ func (s *Server) handleRegenThumbnail(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid media id"}) return } - if err := s.mediaSvc.RegenerateThumbnail(r.Context(), id, userIDFromContext(r)); err != nil { + if err := s.browseSvc.RegenerateThumbnail(r.Context(), id, userIDFromContext(r)); err != nil { if errors.Is(err, service.ErrNotFound) { writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"}) return diff --git a/internal/api/handlers_media.go b/internal/api/handlers_media.go index 20e44ab..6095c72 100644 --- a/internal/api/handlers_media.go +++ b/internal/api/handlers_media.go @@ -18,10 +18,10 @@ import ( // ------------------------------------------------------------------ func (s *Server) handleListSets(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.browseSvc) { return } - sets, err := s.mediaSvc.ListSets(r.Context(), userIDFromContext(r)) + sets, err := s.browseSvc.ListSets(r.Context(), userIDFromContext(r)) if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return @@ -30,7 +30,7 @@ func (s *Server) handleListSets(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleGetSetCover(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.browseSvc) { return } setID := pathID(r, "id") @@ -39,7 +39,7 @@ func (s *Server) handleGetSetCover(w http.ResponseWriter, r *http.Request) { return } folder := r.URL.Query().Get("folder") - fr, err := s.mediaSvc.GetSetCover(r.Context(), setID, folder, userIDFromContext(r)) + fr, err := s.browseSvc.GetSetCover(r.Context(), setID, folder, userIDFromContext(r)) if err != nil { if errors.Is(err, service.ErrNotFound) { writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"}) @@ -57,7 +57,7 @@ func (s *Server) handleGetSetCover(w http.ResponseWriter, r *http.Request) { } func (s *Server) handlePostSetCover(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.browseSvc) { return } setID := pathID(r, "id") @@ -66,7 +66,7 @@ func (s *Server) handlePostSetCover(w http.ResponseWriter, r *http.Request) { return } folder := r.URL.Query().Get("folder") - if err := s.mediaSvc.RegenerateSetCover(r.Context(), setID, folder, userIDFromContext(r)); err != nil { + if err := s.browseSvc.RegenerateSetCover(r.Context(), setID, folder, userIDFromContext(r)); err != nil { if errors.Is(err, service.ErrNotFound) { writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"}) return @@ -82,7 +82,7 @@ func (s *Server) handlePostSetCover(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleBrowseSet(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.browseSvc) { return } setID := pathID(r, "id") @@ -91,7 +91,7 @@ func (s *Server) handleBrowseSet(w http.ResponseWriter, r *http.Request) { return } parent := r.URL.Query().Get("parent") - result, err := s.mediaSvc.BrowseSet(r.Context(), setID, userIDFromContext(r), parent) + result, err := s.browseSvc.BrowseSet(r.Context(), setID, userIDFromContext(r), parent) if err != nil { if errors.Is(err, service.ErrForbidden) { writeJSON(w, http.StatusForbidden, map[string]string{"error": "forbidden"}) @@ -104,7 +104,7 @@ func (s *Server) handleBrowseSet(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleUpload(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.writeSvc) { return } setID := pathID(r, "id") @@ -132,7 +132,7 @@ func (s *Server) handleUpload(w http.ResponseWriter, r *http.Request) { } defer file.Close() - media, err := s.mediaSvc.UploadMedia(r.Context(), setID, userIDFromContext(r), fh.Filename, file, fh.Size) + media, err := s.writeSvc.UploadMedia(r.Context(), setID, userIDFromContext(r), fh.Filename, file, fh.Size) if err != nil { if errors.Is(err, service.ErrNotFound) { writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"}) @@ -222,7 +222,7 @@ func parseMediaListQuery(q url.Values) repository.MediaFilter { } func (s *Server) handleListMedia(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.browseSvc) { return } path := r.URL.Path @@ -236,7 +236,7 @@ func (s *Server) handleListMedia(w http.ResponseWriter, r *http.Request) { maxDur := q.Get("max_duration") start := time.Now() filter := parseMediaListQuery(q) - media, err := s.mediaSvc.ListMedia(r.Context(), userIDFromContext(r), filter) + media, err := s.browseSvc.ListMedia(r.Context(), userIDFromContext(r), filter) dur := time.Since(start) if err != nil { s.logger.Error("api list media failed", "path", path, "set_id", setID, "set_ids", setIDs, "search", search, "type", typ, "favorites", fav, "min_duration", minDur, "max_duration", maxDur, "duration", dur, "err", err) @@ -248,7 +248,7 @@ func (s *Server) handleListMedia(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleGetMedia(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.browseSvc) { return } id := pathID(r, "id") @@ -256,7 +256,7 @@ func (s *Server) handleGetMedia(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid media id"}) return } - detail, err := s.mediaSvc.GetMediaDetail(r.Context(), id, userIDFromContext(r)) + detail, err := s.browseSvc.GetMediaDetail(r.Context(), id, userIDFromContext(r)) if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return @@ -269,7 +269,7 @@ func (s *Server) handleGetMedia(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleFavorite(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.favSvc) { return } id := pathID(r, "id") @@ -277,7 +277,7 @@ func (s *Server) handleFavorite(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid media id"}) return } - fav, err := s.mediaSvc.ToggleFavorite(r.Context(), userIDFromContext(r), id) + fav, err := s.favSvc.ToggleFavorite(r.Context(), userIDFromContext(r), id) if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return @@ -286,7 +286,7 @@ func (s *Server) handleFavorite(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleAddTag(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.tagSvc) { return } id := pathID(r, "id") @@ -301,7 +301,7 @@ func (s *Server) handleAddTag(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "tag required"}) return } - if err := s.mediaSvc.AssignTag(r.Context(), id, userIDFromContext(r), req.Tag); err != nil { + if err := s.tagSvc.AssignTag(r.Context(), id, userIDFromContext(r), req.Tag); err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } @@ -309,7 +309,7 @@ func (s *Server) handleAddTag(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleRemoveTag(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.tagSvc) { return } id := pathID(r, "id") @@ -318,7 +318,7 @@ func (s *Server) handleRemoveTag(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid parameters"}) return } - if err := s.mediaSvc.RemoveTag(r.Context(), id, userIDFromContext(r), tagName); err != nil { + if err := s.tagSvc.RemoveTag(r.Context(), id, userIDFromContext(r), tagName); err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } @@ -326,7 +326,7 @@ func (s *Server) handleRemoveTag(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleSoftDelete(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.writeSvc) { return } id := pathID(r, "id") @@ -334,7 +334,7 @@ func (s *Server) handleSoftDelete(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid media id"}) return } - if err := s.mediaSvc.SoftDeleteMedia(r.Context(), id, userIDFromContext(r)); err != nil { + if err := s.writeSvc.SoftDeleteMedia(r.Context(), id, userIDFromContext(r)); err != nil { if errors.Is(err, service.ErrNotFound) { writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"}) return @@ -350,7 +350,7 @@ func (s *Server) handleSoftDelete(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleRestore(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.writeSvc) { return } id := pathID(r, "id") @@ -358,7 +358,7 @@ func (s *Server) handleRestore(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid media id"}) return } - if err := s.mediaSvc.RestoreMedia(r.Context(), id, userIDFromContext(r)); err != nil { + if err := s.writeSvc.RestoreMedia(r.Context(), id, userIDFromContext(r)); err != nil { if errors.Is(err, service.ErrNotFound) { writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"}) return @@ -378,7 +378,7 @@ func (s *Server) handleRestore(w http.ResponseWriter, r *http.Request) { // ------------------------------------------------------------------ func (s *Server) handleGetNote(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.noteSvc) { return } id := pathID(r, "id") @@ -386,7 +386,7 @@ func (s *Server) handleGetNote(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid media id"}) return } - note, err := s.mediaSvc.GetNote(r.Context(), id, userIDFromContext(r)) + note, err := s.noteSvc.GetNote(r.Context(), id, userIDFromContext(r)) if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return @@ -399,7 +399,7 @@ func (s *Server) handleGetNote(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleUpsertNote(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.noteSvc) { return } id := pathID(r, "id") @@ -415,7 +415,7 @@ func (s *Server) handleUpsertNote(w http.ResponseWriter, r *http.Request) { return } note := &model.Note{MediaID: id, UserID: userIDFromContext(r), Content: req.Content} - if err := s.mediaSvc.UpsertNote(r.Context(), note); err != nil { + if err := s.noteSvc.UpsertNote(r.Context(), note); err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } @@ -423,7 +423,7 @@ func (s *Server) handleUpsertNote(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleDeleteNote(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.noteSvc) { return } id := pathID(r, "id") @@ -431,7 +431,7 @@ func (s *Server) handleDeleteNote(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid media id"}) return } - if err := s.mediaSvc.DeleteNote(r.Context(), id, userIDFromContext(r)); err != nil { + if err := s.noteSvc.DeleteNote(r.Context(), id, userIDFromContext(r)); err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } diff --git a/internal/api/handlers_more_test.go b/internal/api/handlers_more_test.go index 47adfe7..06b5502 100644 --- a/internal/api/handlers_more_test.go +++ b/internal/api/handlers_more_test.go @@ -91,7 +91,7 @@ func TestNewGracefulServer(t *testing.T) { func TestPingStore_nonPinger(t *testing.T) { store := &repository.MockStore{} - srv := newTestServer(t, store, nil, nil, &internal.Config{}, nil, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, nil, &internal.Config{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) if err := srv.pingStore(context.Background()); err != nil { t.Fatal("expected nil for non-pinger") } @@ -99,7 +99,7 @@ func TestPingStore_nonPinger(t *testing.T) { func TestPingStore_pingerError(t *testing.T) { store := &mockPingStore{err: errors.New("down")} - srv := newTestServer(t, store, nil, nil, &internal.Config{}, nil, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, nil, &internal.Config{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) if err := srv.pingStore(context.Background()); err == nil { t.Fatal("expected error") } @@ -161,7 +161,7 @@ func TestServer_ServeFile_success(t *testing.T) { }, } sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) - srv := newTestServer(t, store, nil, sm, &internal.Config{SessionTimeoutHours: 24}, nil, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, &internal.Config{SessionTimeoutHours: 24}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) @@ -181,7 +181,7 @@ func TestServer_ServeFile_notFound(t *testing.T) { }, } sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) - srv := newTestServer(t, store, nil, sm, &internal.Config{SessionTimeoutHours: 24}, nil, nil, nil, nil, fs) + srv := newTestServer(t, store, nil, sm, &internal.Config{SessionTimeoutHours: 24}, nil, nil, nil, nil, nil, nil, nil, nil, nil, fs) req := httptest.NewRequest(http.MethodGet, "/", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) @@ -227,7 +227,7 @@ func TestServer_Bootstrap_hashError(t *testing.T) { return nil, errors.New("hash err") }, } - srv := newTestServer(t, nil, nil, nil, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, nil, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) body := `{"username":"u","password":"p"}` req := httptest.NewRequest(http.MethodPost, "/api/bootstrap", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") @@ -245,7 +245,7 @@ func TestServer_Bootstrap_createUserError(t *testing.T) { return nil, errors.New("boom") }, } - srv := newTestServer(t, nil, nil, nil, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, nil, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) body := `{"username":"u","password":"p"}` req := httptest.NewRequest(http.MethodPost, "/api/bootstrap", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") @@ -263,7 +263,7 @@ func TestServer_Bootstrap_createSessionError(t *testing.T) { return nil, errors.New("boom") }, } - srv := newTestServer(t, nil, nil, nil, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, nil, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) body := `{"username":"u","password":"p"}` req := httptest.NewRequest(http.MethodPost, "/api/bootstrap", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") @@ -283,7 +283,7 @@ func TestServer_Login_negativePaths(t *testing.T) { t.Run("invalid json", func(t *testing.T) { authSvc := &service.MockAuthService{} - srv := newTestServer(t, nil, nil, nil, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, nil, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) req := httptest.NewRequest(http.MethodPost, "/api/login", bytes.NewReader([]byte(`bad`))) req.Header.Set("Content-Type", "application/json") rr := httptest.NewRecorder() @@ -299,7 +299,7 @@ func TestServer_Login_negativePaths(t *testing.T) { return nil, errors.New("boom") }, } - srv := newTestServer(t, nil, nil, nil, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, nil, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) body := `{"username":"alice","password":"correct"}` req := httptest.NewRequest(http.MethodPost, "/api/login", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") @@ -345,7 +345,7 @@ func TestServer_SetCover(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/sets/"+tt.id+"/cover", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -363,7 +363,7 @@ func TestServer_ListSets_negative(t *testing.T) { cfg := &internal.Config{SessionTimeoutHours: 24} t.Run("nil service", func(t *testing.T) { - srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/sets", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -379,7 +379,7 @@ func TestServer_ListSets_negative(t *testing.T) { return nil, errors.New("boom") }, } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/sets", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -429,7 +429,7 @@ func TestServer_Upload(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) var req *http.Request if tt.noFile { var buf bytes.Buffer @@ -466,7 +466,7 @@ func TestServer_MediaDetail_nilService(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/media/1", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -503,7 +503,7 @@ func TestServer_Favorite_negative(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/media/"+tt.id+"/favorite", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -519,7 +519,7 @@ func TestServer_AddTag_nilService(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/media/1/tags", strings.NewReader(`{"tag":"x"}`)) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) req.Header.Set("Content-Type", "application/json") @@ -558,7 +558,7 @@ func TestServer_RemoveTag_negative(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/api/media/%s/tags/%s", tt.id, tt.tag), nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -606,7 +606,7 @@ func TestServer_Stream(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/media/"+tt.id+"/stream", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -673,7 +673,7 @@ func TestServer_Download(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/media/"+tt.id+"/download", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -723,7 +723,7 @@ func TestServer_Thumbnail(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/media/"+tt.id+"/thumbnail", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -763,7 +763,7 @@ func TestServer_RegenThumbnail(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/media/"+tt.id+"/thumbnail", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -798,7 +798,7 @@ func TestServer_RegenThumbnail_errorMapping(t *testing.T) { return tt.svcErr }, } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/media/1/thumbnail", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -841,7 +841,7 @@ func TestServer_CreateShare_negative(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/media/"+tt.id+"/shares", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -880,7 +880,7 @@ func TestServer_ListShares_negative(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/media/"+tt.id+"/shares", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -919,7 +919,7 @@ func TestServer_RevokeShare(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodDelete, "/api/shares/"+tt.token, nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -941,7 +941,7 @@ func TestServer_SharePage(t *testing.T) { }) t.Run("nil service", func(t *testing.T) { - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, nil, nil, nil, nil, fs) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, fs) req := httptest.NewRequest(http.MethodGet, "/s/abc", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -956,7 +956,7 @@ func TestServer_SharePage(t *testing.T) { return nil, errors.New("boom") }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, fs) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, fs) req := httptest.NewRequest(http.MethodGet, "/s/abc", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -971,7 +971,7 @@ func TestServer_SharePage(t *testing.T) { return nil, nil }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, fs) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, fs) req := httptest.NewRequest(http.MethodGet, "/s/abc", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -986,7 +986,7 @@ func TestServer_SharePage(t *testing.T) { return nil, service.ErrShareExpired }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, fs) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, fs) req := httptest.NewRequest(http.MethodGet, "/s/abc", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -1005,7 +1005,7 @@ func TestServer_SharePage(t *testing.T) { }, nil }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, fs) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, fs) req := httptest.NewRequest(http.MethodGet, "/s/abc", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -1032,7 +1032,7 @@ func TestServer_SharePage(t *testing.T) { }, nil }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, fs) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, fs) req := httptest.NewRequest(http.MethodGet, "/s/abc", nil) req.Header.Set("Accept", "text/html") rr := httptest.NewRecorder() @@ -1056,7 +1056,7 @@ func TestServer_SharePage(t *testing.T) { }, nil }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, fs) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, fs) req := httptest.NewRequest(http.MethodGet, "/s/abc", nil) req.Header.Set("Accept", "application/json") rr := httptest.NewRecorder() @@ -1083,7 +1083,7 @@ func TestServer_ShareStream(t *testing.T) { cfg := &internal.Config{SessionTimeoutHours: 24} t.Run("nil service", func(t *testing.T) { - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, nil, nil, nil, nil, nil) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/s/abc/stream", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -1098,7 +1098,7 @@ func TestServer_ShareStream(t *testing.T) { return nil, errors.New("boom") }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/s/abc/stream", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -1113,7 +1113,7 @@ func TestServer_ShareStream(t *testing.T) { return nil, service.ErrShareNotFound }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/s/abc/stream", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -1128,7 +1128,7 @@ func TestServer_ShareStream(t *testing.T) { return nil, service.ErrShareExpired }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/s/abc/stream", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -1143,7 +1143,7 @@ func TestServer_ShareStream(t *testing.T) { return nil, service.ErrMediaNotFound }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/s/abc/stream", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -1158,7 +1158,7 @@ func TestServer_ShareStream(t *testing.T) { return &service.FileResult{Path: "/nonexistent", FileName: "a.mp4"}, nil }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/s/abc/stream", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -1173,7 +1173,7 @@ func TestServer_ShareStream(t *testing.T) { return &service.FileResult{Path: path, FileName: "a.mp4"}, nil }, } - srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildSessionStore(1), nil, nil, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/s/abc/stream", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -1215,7 +1215,7 @@ func TestServer_SoftDelete_negative(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodDelete, "/api/media/"+tt.id, nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -1255,7 +1255,7 @@ func TestServer_Restore_negative(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/media/"+tt.id+"/restore", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -1301,7 +1301,7 @@ func TestServer_UpsertNote(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/media/"+tt.id+"/notes", strings.NewReader(tt.body)) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) req.Header.Set("Content-Type", "application/json") @@ -1342,7 +1342,7 @@ func TestServer_DeleteNote(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodDelete, "/api/media/"+tt.id+"/notes", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -1386,7 +1386,7 @@ func TestServer_Progress_negative(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, nil, nil, ps, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, nil, ps, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/progress", strings.NewReader(tt.body)) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) req.Header.Set("Content-Type", "application/json") @@ -1427,7 +1427,7 @@ func TestServer_AdminRescan(t *testing.T) { TriggerRescanFunc: func(ctx context.Context) error { return tt.svcErr }, } } - srv := newTestServer(t, store, nil, sm, cfg, nil, as, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, as, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/admin/rescan", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -1463,7 +1463,7 @@ func TestServer_AdminListTrash(t *testing.T) { ListTrashFunc: func(ctx context.Context) ([]model.Media, error) { return nil, tt.svcErr }, } } - srv := newTestServer(t, store, nil, sm, cfg, nil, as, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, as, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/admin/trash", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -1499,7 +1499,7 @@ func TestServer_AdminListUsers(t *testing.T) { ListUsersFunc: func(ctx context.Context) ([]model.User, error) { return nil, tt.svcErr }, } } - srv := newTestServer(t, store, nil, sm, cfg, nil, as, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, as, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/admin/users", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -1540,7 +1540,7 @@ func TestServer_AdminCreateUser(t *testing.T) { }, } } - srv := newTestServer(t, store, nil, sm, cfg, nil, as, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, as, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/admin/users", strings.NewReader(tt.body)) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) req.Header.Set("Content-Type", "application/json") @@ -1579,7 +1579,7 @@ func TestServer_AdminDeleteUser(t *testing.T) { DeleteUserFunc: func(ctx context.Context, id int64) error { return tt.svcErr }, } } - srv := newTestServer(t, store, nil, sm, cfg, nil, as, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, as, nil, nil, nil) req := httptest.NewRequest(http.MethodDelete, "/api/admin/users/"+tt.id, nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -1615,7 +1615,7 @@ func TestServer_AdminListPermissions(t *testing.T) { ListPermissionsFunc: func(ctx context.Context) (*service.PermissionsMatrix, error) { return nil, tt.svcErr }, } } - srv := newTestServer(t, store, nil, sm, cfg, nil, as, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, as, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/admin/permissions", nil) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -1653,7 +1653,7 @@ func TestServer_AdminGrantPermission(t *testing.T) { GrantPermissionFunc: func(ctx context.Context, setID, userID int64, role model.Role) error { return tt.svcErr }, } } - srv := newTestServer(t, store, nil, sm, cfg, nil, as, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, as, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/admin/permissions", strings.NewReader(tt.body)) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) req.Header.Set("Content-Type", "application/json") @@ -1692,7 +1692,7 @@ func TestServer_AdminRevokePermission(t *testing.T) { RevokePermissionFunc: func(ctx context.Context, setID, userID int64) error { return tt.svcErr }, } } - srv := newTestServer(t, store, nil, sm, cfg, nil, as, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, as, nil, nil, nil) req := httptest.NewRequest(http.MethodDelete, "/api/admin/permissions", strings.NewReader(tt.body)) req.AddCookie(sessionCookieForStore(t, store, sm, 1)) req.Header.Set("Content-Type", "application/json") @@ -1835,3 +1835,62 @@ func Test_parseMediaListQuery_emptyTags(t *testing.T) { t.Fatalf("expected nil tags for empty string, got %v", got.Tags) } } + +// ------------------------------------------------------------------ +// Negative nil-service tests for narrow interface split +// ------------------------------------------------------------------ + +func TestServer_NilAdminSvc(t *testing.T) { + cfg := &internal.Config{SessionTimeoutHours: 24} + store := buildSessionStore(1) + sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) + store.UserRepo.GetUserByIDFunc = func(ctx context.Context, id int64) (*model.User, error) { + return &model.User{ID: 1, IsAdmin: true}, nil + } + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) + cookie := addSessionCookie(t, store, sm, 1) + + tests := []struct { + method string + path string + }{ + {http.MethodGet, "/api/admin/trash"}, + {http.MethodPost, "/api/admin/rescan"}, + {http.MethodGet, "/api/admin/scan-progress"}, + {http.MethodGet, "/api/admin/users"}, + {http.MethodPost, "/api/admin/users"}, + {http.MethodDelete, "/api/admin/users/1"}, + {http.MethodGet, "/api/admin/permissions"}, + {http.MethodPost, "/api/admin/permissions"}, + {http.MethodDelete, "/api/admin/permissions"}, + } + + for _, tt := range tests { + t.Run(tt.path, func(t *testing.T) { + req := httptest.NewRequest(tt.method, tt.path, nil) + req.AddCookie(cookie) + rr := httptest.NewRecorder() + srv.ServeHTTP(rr, req) + if rr.Code != http.StatusNotImplemented { + t.Fatalf("expected %d, got %d", http.StatusNotImplemented, rr.Code) + } + }) + } +} + +func TestServer_NilProgressSvc(t *testing.T) { + cfg := &internal.Config{SessionTimeoutHours: 24} + store := buildSessionStore(1) + sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) + cookie := addSessionCookie(t, store, sm, 1) + + req := httptest.NewRequest(http.MethodPost, "/api/progress", strings.NewReader(`{"media_id":1,"position_seconds":5}`)) + req.AddCookie(cookie) + req.Header.Set("Content-Type", "application/json") + rr := httptest.NewRecorder() + srv.ServeHTTP(rr, req) + if rr.Code != http.StatusNotImplemented { + t.Fatalf("expected %d, got %d", http.StatusNotImplemented, rr.Code) + } +} diff --git a/internal/api/handlers_share.go b/internal/api/handlers_share.go index 1d8d242..9aa3e8d 100644 --- a/internal/api/handlers_share.go +++ b/internal/api/handlers_share.go @@ -16,7 +16,7 @@ import ( // ------------------------------------------------------------------ func (s *Server) handleCreateShare(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.shareSvc) { return } id := pathID(r, "id") @@ -25,7 +25,7 @@ func (s *Server) handleCreateShare(w http.ResponseWriter, r *http.Request) { return } expiresAt := time.Now().Add(time.Duration(s.cfg.ShareDefaultExpiryDays) * 24 * time.Hour) - share, err := s.mediaSvc.CreateShare(r.Context(), userIDFromContext(r), id, expiresAt) + share, err := s.shareSvc.CreateShare(r.Context(), userIDFromContext(r), id, expiresAt) if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return @@ -34,7 +34,7 @@ func (s *Server) handleCreateShare(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleListShares(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.shareSvc) { return } id := pathID(r, "id") @@ -42,7 +42,7 @@ func (s *Server) handleListShares(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid media id"}) return } - shares, err := s.mediaSvc.ListShares(r.Context(), id, userIDFromContext(r)) + shares, err := s.shareSvc.ListShares(r.Context(), id, userIDFromContext(r)) if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return @@ -51,7 +51,7 @@ func (s *Server) handleListShares(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleRevokeShare(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.shareSvc) { return } token := r.PathValue("token") @@ -59,7 +59,7 @@ func (s *Server) handleRevokeShare(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "token required"}) return } - if err := s.mediaSvc.RevokeShare(r.Context(), token, userIDFromContext(r)); err != nil { + if err := s.shareSvc.RevokeShare(r.Context(), token, userIDFromContext(r)); err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } @@ -67,11 +67,11 @@ func (s *Server) handleRevokeShare(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleSharePage(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.shareSvc) { return } token := r.PathValue("token") - res, err := s.mediaSvc.GetSharedMedia(r.Context(), token) + res, err := s.shareSvc.GetSharedMedia(r.Context(), token) if err != nil || res == nil { if err != nil && errors.Is(err, service.ErrShareExpired) { http.Error(w, "gone", http.StatusGone) @@ -115,11 +115,11 @@ func (s *Server) handleSharePage(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleShareThumbnail(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.shareSvc) { return } token := r.PathValue("token") - fr, err := s.mediaSvc.GetSharedThumbnail(r.Context(), token) + fr, err := s.shareSvc.GetSharedThumbnail(r.Context(), token) if err != nil { if errors.Is(err, service.ErrShareExpired) { http.Error(w, "gone", http.StatusGone) @@ -141,11 +141,11 @@ func (s *Server) handleShareThumbnail(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleShareStream(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.shareSvc) { return } token := r.PathValue("token") - res, err := s.mediaSvc.StreamSharedMedia(r.Context(), token) + res, err := s.shareSvc.StreamSharedMedia(r.Context(), token) if err != nil { if errors.Is(err, service.ErrShareExpired) { http.Error(w, "gone", http.StatusGone) @@ -166,11 +166,11 @@ func (s *Server) handleShareStream(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleShareDownload(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.shareSvc) { return } token := r.PathValue("token") - fr, err := s.mediaSvc.StreamSharedMedia(r.Context(), token) + fr, err := s.shareSvc.StreamSharedMedia(r.Context(), token) if err != nil { if errors.Is(err, service.ErrShareExpired) { http.Error(w, "gone", http.StatusGone) @@ -191,10 +191,10 @@ func (s *Server) handleShareDownload(w http.ResponseWriter, r *http.Request) { } func (s *Server) handleMyShares(w http.ResponseWriter, r *http.Request) { - if !requireService(w, s.mediaSvc) { + if !requireService(w, s.shareSvc) { return } - shares, err := s.mediaSvc.ListMyShares(r.Context(), userIDFromContext(r)) + shares, err := s.shareSvc.ListMyShares(r.Context(), userIDFromContext(r)) if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return diff --git a/internal/api/handlers_test.go b/internal/api/handlers_test.go index 5d188fa..2741172 100644 --- a/internal/api/handlers_test.go +++ b/internal/api/handlers_test.go @@ -35,7 +35,14 @@ func newTestFS(files map[string]string) http.FileSystem { } func newTestServer(t *testing.T, store repository.Store, hasher auth.Hasher, sm *auth.SessionManager, cfg *internal.Config, - mediaSvc service.MediaService, adminSvc service.AdminService, progressSvc service.ProgressService, + browseSvc service.MediaBrowseService, + writeSvc service.MediaWriteService, + shareSvc service.MediaShareService, + tagSvc service.MediaTagService, + favSvc service.MediaFavoriteService, + noteSvc service.MediaNoteService, + adminSvc service.AdminService, + progressSvc service.ProgressService, authSvc service.AuthService, fs http.FileSystem, remuxer ...probe.Remuxer, @@ -53,7 +60,7 @@ func newTestServer(t *testing.T, store repository.Store, hasher auth.Hasher, sm if len(remuxer) > 0 { rem = remuxer[0] } - return NewServer(store, hasher, sm, cfg, mediaSvc, adminSvc, progressSvc, authSvc, fs, rem) + return NewServer(store, hasher, sm, cfg, browseSvc, writeSvc, shareSvc, tagSvc, favSvc, noteSvc, adminSvc, progressSvc, authSvc, fs, rem) } func addSessionCookie(t *testing.T, store repository.Store, sm *auth.SessionManager, userID int64) *http.Cookie { @@ -253,7 +260,7 @@ func TestServer_StaticPages(t *testing.T) { } t.Run("index requires session", func(t *testing.T) { - srv := newTestServer(t, store, nil, nil, cfg, nil, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -263,7 +270,7 @@ func TestServer_StaticPages(t *testing.T) { }) t.Run("login public", func(t *testing.T) { - srv := newTestServer(t, store, nil, nil, cfg, nil, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/login.html", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -276,7 +283,7 @@ func TestServer_StaticPages(t *testing.T) { }) t.Run("bootstrap public", func(t *testing.T) { - srv := newTestServer(t, store, nil, nil, cfg, nil, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/bootstrap.html", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -287,7 +294,7 @@ func TestServer_StaticPages(t *testing.T) { t.Run("css public", func(t *testing.T) { fs := newTestFS(map[string]string{"css/theme.css": "body{}"}) - srv := newTestServer(t, store, nil, nil, cfg, nil, nil, nil, nil, fs) + srv := newTestServer(t, store, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, fs) req := httptest.NewRequest(http.MethodGet, "/css/theme.css", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -333,7 +340,7 @@ func TestServer_Bootstrap(t *testing.T) { } sm := auth.NewSessionManager(&repo, clk, time.Hour) authSvc := service.NewAuthService(store, clk, hasher, sm) - srv := newTestServer(t, store, hasher, sm, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, store, hasher, sm, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) body := `{"username":"admin","password":"secret"}` req := httptest.NewRequest(http.MethodPost, "/api/bootstrap", bytes.NewReader([]byte(body))) @@ -361,7 +368,7 @@ func TestServer_Bootstrap(t *testing.T) { }, } authSvc := service.NewAuthService(store, clk, hasher, nil) - srv := newTestServer(t, store, hasher, nil, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, store, hasher, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) body := `{"username":"admin","password":"secret"}` req := httptest.NewRequest(http.MethodPost, "/api/bootstrap", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") @@ -375,7 +382,7 @@ func TestServer_Bootstrap(t *testing.T) { t.Run("missing fields", func(t *testing.T) { store := &repository.MockStore{UserRepo: repository.MockUserRepo{CountUsersFunc: func(ctx context.Context) (int, error) { return 0, nil }}} authSvc := service.NewAuthService(store, clk, hasher, nil) - srv := newTestServer(t, store, hasher, nil, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, store, hasher, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) req := httptest.NewRequest(http.MethodPost, "/api/bootstrap", bytes.NewReader([]byte(`{"username":""}`))) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -385,7 +392,7 @@ func TestServer_Bootstrap(t *testing.T) { }) t.Run("wrong method", func(t *testing.T) { - srv := newTestServer(t, nil, hasher, nil, cfg, nil, nil, nil, nil, nil) + srv := newTestServer(t, nil, hasher, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/bootstrap", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -414,7 +421,7 @@ func TestServer_Login(t *testing.T) { } sm := auth.NewSessionManager(&repo, clk, time.Hour) authSvc := service.NewAuthService(store, clk, hasher, sm) - srv := newTestServer(t, store, hasher, sm, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, store, hasher, sm, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) body := `{"username":"alice","password":"correct"}` req := httptest.NewRequest(http.MethodPost, "/api/login", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") @@ -441,7 +448,7 @@ func TestServer_Login(t *testing.T) { }, } authSvc := service.NewAuthService(store, clk, hasher, nil) - srv := newTestServer(t, store, hasher, nil, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, store, hasher, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) body := `{"username":"alice","password":"wrong"}` req := httptest.NewRequest(http.MethodPost, "/api/login", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") @@ -462,7 +469,7 @@ func TestServer_Login(t *testing.T) { }, } authSvc := service.NewAuthService(store, clk, hasher, nil) - srv := newTestServer(t, store, hasher, nil, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, store, hasher, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) body := `{"username":"nobody","password":"pass"}` req := httptest.NewRequest(http.MethodPost, "/api/login", bytes.NewReader([]byte(body))) rr := httptest.NewRecorder() @@ -492,7 +499,7 @@ func TestServer_SessionCookieSecure(t *testing.T) { t.Run("Secure=true by default", func(t *testing.T) { cfg := &internal.Config{SessionTimeoutHours: 24, SecureCookies: true} - srv := newTestServer(t, store, hasher, sm, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, store, hasher, sm, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) body := `{"username":"alice","password":"correct"}` req := httptest.NewRequest(http.MethodPost, "/api/login", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") @@ -510,7 +517,7 @@ func TestServer_SessionCookieSecure(t *testing.T) { t.Run("Secure=false", func(t *testing.T) { cfg := &internal.Config{SessionTimeoutHours: 24, SecureCookies: false} - srv := newTestServer(t, store, hasher, sm, cfg, nil, nil, nil, authSvc, nil) + srv := newTestServer(t, store, hasher, sm, cfg, nil, nil, nil, nil, nil, nil, nil, nil, authSvc, nil) body := `{"username":"alice","password":"correct"}` req := httptest.NewRequest(http.MethodPost, "/api/login", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") @@ -547,7 +554,7 @@ func TestServer_SessionCookieSecure(t *testing.T) { } logoutSM := auth.NewSessionManager(&sessStore.SessionRepo, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24, SecureCookies: false} - srv := newTestServer(t, sessStore, nil, logoutSM, cfg, nil, nil, nil, nil, nil) + srv := newTestServer(t, sessStore, nil, logoutSM, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/logout", nil) req.AddCookie(&http.Cookie{Name: "session", Value: "abc"}) rr := httptest.NewRecorder() @@ -585,7 +592,7 @@ func TestServer_Logout(t *testing.T) { } sm := auth.NewSessionManager(&repo, &clock.MockClock{T: time.Now()}, time.Hour) store := &repository.MockStore{UserRepo: repository.MockUserRepo{CountUsersFunc: func(ctx context.Context) (int, error) { return 1, nil }}} - srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/logout", nil) req.AddCookie(&http.Cookie{Name: "session", Value: "abc"}) @@ -606,7 +613,7 @@ func TestServer_Logout(t *testing.T) { t.Run("no cookie logout", func(t *testing.T) { store := &repository.MockStore{UserRepo: repository.MockUserRepo{CountUsersFunc: func(ctx context.Context) (int, error) { return 1, nil }}} - srv := newTestServer(t, store, nil, nil, cfg, nil, nil, nil, nil, nil) + srv := newTestServer(t, store, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/logout", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -617,7 +624,7 @@ func TestServer_Logout(t *testing.T) { } func TestServer_Healthz(t *testing.T) { - srv := newTestServer(t, &repository.MockStore{}, nil, nil, &internal.Config{}, nil, nil, nil, nil, nil) + srv := newTestServer(t, &repository.MockStore{}, nil, nil, &internal.Config{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/healthz", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -632,7 +639,7 @@ func TestServer_Readyz(t *testing.T) { UserRepo: repository.MockUserRepo{CountUsersFunc: func(ctx context.Context) (int, error) { return 1, nil }}, } store2 := &mockPingStore{store: store, err: nil} - srv := newTestServer(t, store2, nil, nil, &internal.Config{}, nil, nil, nil, nil, nil) + srv := newTestServer(t, store2, nil, nil, &internal.Config{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/readyz", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -646,7 +653,7 @@ func TestServer_Readyz(t *testing.T) { UserRepo: repository.MockUserRepo{CountUsersFunc: func(ctx context.Context) (int, error) { return 1, nil }}, } store2 := &mockPingStore{store: store, err: errors.New("down")} - srv := newTestServer(t, store2, nil, nil, &internal.Config{}, nil, nil, nil, nil, nil) + srv := newTestServer(t, store2, nil, nil, &internal.Config{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/readyz", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) @@ -703,7 +710,7 @@ func TestServer_MediaList(t *testing.T) { } store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) - srv := newTestServer(t, buildCountStore(1), hasher, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildCountStore(1), hasher, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/api/media"+tt.query, nil) req.AddCookie(addSessionCookie(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -756,7 +763,7 @@ func TestServer_MediaDetail(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/media/%s", tt.id), nil) req.AddCookie(addSessionCookie(t, store, sm, 1)) rr := httptest.NewRecorder() @@ -797,7 +804,7 @@ func TestServer_Favorite(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/media/5/favorite", strings.NewReader(`{}`)) req.AddCookie(addSessionCookie(t, store, sm, 1)) @@ -826,7 +833,7 @@ func TestServer_AddTag(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) t.Run("add tag", func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/media/1/tags", strings.NewReader(`{"tag":"rock"}`)) @@ -871,7 +878,7 @@ func TestServer_RemoveTag(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodDelete, "/api/media/1/tags/rock", nil) req.AddCookie(addSessionCookie(t, store, sm, 1)) @@ -891,7 +898,7 @@ func TestServer_SoftDelete(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodDelete, "/api/media/99", nil) req.AddCookie(addSessionCookie(t, store, sm, 1)) @@ -911,7 +918,7 @@ func TestServer_Restore(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/media/99/restore", nil) req.AddCookie(addSessionCookie(t, store, sm, 1)) @@ -931,7 +938,7 @@ func TestServer_SoftDelete_Forbidden(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodDelete, "/api/media/99", nil) req.AddCookie(addSessionCookie(t, store, sm, 1)) @@ -951,7 +958,7 @@ func TestServer_Restore_Forbidden(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodPost, "/api/media/99/restore", nil) req.AddCookie(addSessionCookie(t, store, sm, 1)) @@ -984,7 +991,7 @@ func TestServer_Notes(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) t.Run("get note", func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/api/media/1/notes", nil) @@ -1043,7 +1050,7 @@ func TestServer_Progress(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24} - srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, nil, nil, ps, nil, nil) + srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, nil, nil, nil, nil, nil, nil, nil, ps, nil, nil) t.Run("ok", func(t *testing.T) { called = false @@ -1104,7 +1111,7 @@ func TestServer_Shares(t *testing.T) { store := buildSessionStore(1) sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour) cfg := &internal.Config{SessionTimeoutHours: 24, ShareDefaultExpiryDays: 14} - srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, nil, nil, nil, nil) + srv := newTestServer(t, buildCountStore(1), nil, sm, cfg, ms, ms, ms, ms, ms, ms, nil, nil, nil, nil) t.Run("create share", func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/api/media/1/shares", nil) @@ -1183,7 +1190,7 @@ func TestServer_AdminRoutes(t *testing.T) { } sm := auth.NewSessionManager(store, &clock.MockClock{T: time.Now()}, time.Hour)