From de300a641378f93afe1957a0f1b7ccdc30c7c432 Mon Sep 17 00:00:00 2001 From: Maxim Lebedev Date: Fri, 29 Sep 2023 20:45:10 +0600 Subject: [PATCH] :necktie: Created download route for media HTTP delivery with test --- internal/media/delivery/http/media_http.go | 43 +++++++++++++++---- .../media/delivery/http/media_http_test.go | 37 +++++++++++++++- 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/internal/media/delivery/http/media_http.go b/internal/media/delivery/http/media_http.go index 97b2999..5443e73 100644 --- a/internal/media/delivery/http/media_http.go +++ b/internal/media/delivery/http/media_http.go @@ -1,10 +1,12 @@ package http import ( + "bytes" "encoding/json" "io" "mime" "net/http" + "time" "source.toby3d.me/toby3d/pub/internal/common" "source.toby3d.me/toby3d/pub/internal/domain" @@ -31,21 +33,44 @@ func NewHandler(media media.UseCase, config domain.Config) *Handler { } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + switch r.Method { + default: + WriteError(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + case "", http.MethodGet: + h.handleDownload(w, r) + case http.MethodPost: + h.handleUpload(w, r) + } +} + +func (h *Handler) handleDownload(w http.ResponseWriter, r *http.Request) { + if r.Method != "" && r.Method != http.MethodGet { + WriteError(w, "method MUST be "+http.MethodGet, http.StatusMethodNotAllowed) + + return + } + + out, err := h.media.Download(r.Context(), r.RequestURI) + if err != nil { + WriteError(w, "cannot download media: "+err.Error(), http.StatusInternalServerError) + + return + } + + http.ServeContent(w, r, out.LogicalName(), time.Time{}, bytes.NewReader(out.Content)) +} + +func (h *Handler) handleUpload(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { - WriteError(w, "method MUST be "+http.MethodPost, http.StatusBadRequest) + WriteError(w, "method MUST be "+http.MethodPost, http.StatusMethodNotAllowed) return } mediaType, _, err := mime.ParseMediaType(r.Header.Get(common.HeaderContentType)) - if err != nil { - WriteError(w, "Content-Type header MUST be "+common.MIMEMultipartForm, http.StatusBadRequest) - - return - } - - if mediaType != common.MIMEMultipartForm { - WriteError(w, "Content-Type header MUST be "+common.MIMEMultipartForm, http.StatusBadRequest) + if err != nil || mediaType != common.MIMEMultipartForm { + WriteError(w, common.HeaderContentType+" header MUST be "+common.MIMEMultipartForm, + http.StatusBadRequest) return } diff --git a/internal/media/delivery/http/media_http_test.go b/internal/media/delivery/http/media_http_test.go index aea6a73..e2ecd63 100644 --- a/internal/media/delivery/http/media_http_test.go +++ b/internal/media/delivery/http/media_http_test.go @@ -2,6 +2,7 @@ package http_test import ( "bytes" + "io" "mime/multipart" "net/http" "net/http/httptest" @@ -13,7 +14,7 @@ import ( delivery "source.toby3d.me/toby3d/pub/internal/media/delivery/http" ) -func TestUpload(t *testing.T) { +func TestHandler_Upload(t *testing.T) { t.Parallel() testConfig := domain.TestConfig(t) @@ -54,3 +55,37 @@ func TestUpload(t *testing.T) { t.Errorf("%s %s = %s, want not empty", req.Method, req.RequestURI, location) } } + +func TestHandler_Download(t *testing.T) { + t.Parallel() + + testConfig := domain.TestConfig(t) + testFile := domain.TestFile(t) + + req := httptest.NewRequest(http.MethodGet, "https://example.com/media/"+testFile.LogicalName(), nil) + w := httptest.NewRecorder() + + delivery.NewHandler( + media.NewStubUseCase(nil, testFile, nil), *testConfig). + ServeHTTP(w, req) + + resp := w.Result() + + if resp.StatusCode != http.StatusOK { + t.Errorf("%s %s = %d, want %d", req.Method, req.RequestURI, resp.StatusCode, http.StatusOK) + } + + contentType, mediaType := resp.Header.Get(common.HeaderContentType), testFile.MediaType() + if contentType != mediaType { + t.Errorf("%s %s = '%s', want '%s'", req.Method, req.RequestURI, contentType, mediaType) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(body, testFile.Content) { + t.Error("stored and received file contents is not the same") + } +}