package proxy import ( "encoding/base64" "fmt" "io" "io/ioutil" "net/http" "net/http/httptest" "strconv" "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/umputun/remark/backend/app/store/image" ) func TestImage_Extract(t *testing.T) { tbl := []struct { inp string res []string }{ { `

blah test

`, []string{"http://radio-t.com/img.png"}, }, { `

blah test

`, []string{}, }, { ``, []string{"http://radio-t.com/img2.png"}, }, { `
xyz
`, []string{"http://radio-t.com/img3.png", "http://images.pexels.com/67636/img4.jpeg"}, }, { `
xyz
`, []string{"http://images.pexels.com/67636/img4.jpeg"}, }, { `abcd blah

xxx

`, []string{}, }, } img := Image{HTTP2HTTPS: true} for i, tt := range tbl { t.Run(strconv.Itoa(i), func(t *testing.T) { res, err := img.extract(tt.inp, func(src string) bool { return strings.HasPrefix(src, "http://") }) assert.NoError(t, err) assert.Equal(t, tt.res, res) }) } } func TestImage_Replace(t *testing.T) { img := Image{HTTP2HTTPS: true, RoutePath: "/img"} r := img.replace(` xyz `, []string{"http://radio-t.com/img3.png", "http://images.pexels.com/67636/img4.jpeg"}) assert.Equal(t, ` xyz `, r) } func TestImage_Routes(t *testing.T) { img := Image{HTTP2HTTPS: true, RemarkURL: "https://demo.remark42.com", RoutePath: "/api/v1/proxy"} ts := httptest.NewServer(http.HandlerFunc(img.Handler)) defer ts.Close() httpSrv := imgHTTPTestsServer(t) defer httpSrv.Close() encodedImgURL := base64.URLEncoding.EncodeToString([]byte(httpSrv.URL + "/image/img1.png")) resp, err := http.Get(ts.URL + "/?src=" + encodedImgURL) require.NoError(t, err) assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, "123", resp.Header["Content-Length"][0]) assert.Equal(t, "image/*", resp.Header["Content-Type"][0]) encodedImgURL = base64.URLEncoding.EncodeToString([]byte(httpSrv.URL + "/image/no-such-image.png")) resp, err = http.Get(ts.URL + "/?src=" + encodedImgURL) require.NoError(t, err) assert.Equal(t, 404, resp.StatusCode) encodedImgURL = base64.URLEncoding.EncodeToString([]byte(httpSrv.URL + "bad encoding")) resp, err = http.Get(ts.URL + "/?src=" + encodedImgURL) require.NoError(t, err) assert.Equal(t, 400, resp.StatusCode) } func TestImage_RoutesCachingImage(t *testing.T) { imageStore := image.MockStore{} img := Image{ CacheExternal: true, RemarkURL: "https://demo.remark42.com", RoutePath: "/api/v1/proxy", ImageService: &image.Service{Store: &imageStore}, } ts := httptest.NewServer(http.HandlerFunc(img.Handler)) defer ts.Close() httpSrv := imgHTTPTestsServer(t) defer httpSrv.Close() imgURL := httpSrv.URL + "/image/img1.png" encodedImgURL := base64.URLEncoding.EncodeToString([]byte(imgURL)) imageStore.On("Load", mock.Anything).Once().Return(nil, nil) imageStore.On("SaveWithID", mock.Anything, mock.Anything).Once().Run(func(args mock.Arguments) { _, _ = ioutil.ReadAll(args.Get(1).(io.Reader)) }).Return("", nil) imageStore.On("Commit", mock.Anything).Once().Return(nil) resp, err := http.Get(ts.URL + "/?src=" + encodedImgURL) require.Nil(t, err) assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, "123", resp.Header["Content-Length"][0]) assert.Equal(t, "image/*", resp.Header["Content-Type"][0]) imageStore.AssertCalled(t, "Load", mock.Anything) imageStore.AssertCalled(t, "SaveWithID", "cached_images/4b84b15bff6ee5796152495a230e45e3d7e947d9-"+sha1Str(imgURL), mock.Anything) imageStore.AssertCalled(t, "Commit", mock.Anything) } func TestImage_RoutesUsingCachedImage(t *testing.T) { imageStore := image.MockStore{} img := Image{ CacheExternal: true, RemarkURL: "https://demo.remark42.com", RoutePath: "/api/v1/proxy", ImageService: &image.Service{Store: &imageStore}, } ts := httptest.NewServer(http.HandlerFunc(img.Handler)) defer ts.Close() httpSrv := imgHTTPTestsServer(t) defer httpSrv.Close() encodedImgURL := base64.URLEncoding.EncodeToString([]byte(httpSrv.URL + "/image/img1.png")) // In order to validate that cached data used cache "will return" some other data from what http server would testImage := []byte(fmt.Sprintf("%256s", "X")) imageStore.On("Load", mock.Anything).Once().Return(testImage, nil) resp, err := http.Get(ts.URL + "/?src=" + encodedImgURL) require.Nil(t, err) assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, "256", resp.Header["Content-Length"][0]) assert.Equal(t, "image/*", resp.Header["Content-Type"][0]) imageStore.AssertCalled(t, "Load", mock.Anything) } func TestImage_RoutesTimedOut(t *testing.T) { img := Image{HTTP2HTTPS: true, RemarkURL: "https://demo.remark42.com", RoutePath: "/api/v1/proxy", Timeout: 50 * time.Millisecond} ts := httptest.NewServer(http.HandlerFunc(img.Handler)) defer ts.Close() httpSrv := imgHTTPTestsServer(t) defer httpSrv.Close() encodedImgURL := base64.URLEncoding.EncodeToString([]byte(httpSrv.URL + "/image/img-slow.png")) resp, err := http.Get(ts.URL + "/?src=" + encodedImgURL) require.NoError(t, err) assert.Equal(t, 404, resp.StatusCode) b, err := ioutil.ReadAll(resp.Body) require.NoError(t, err) t.Log(string(b)) assert.True(t, strings.Contains(string(b), "deadline exceeded")) } func TestImage_ConvertProxyMode(t *testing.T) { img := Image{HTTP2HTTPS: true, RoutePath: "/img"} r := img.Convert(` xyz `) assert.Equal(t, ` xyz `, r) r = img.Convert(` xyz `) assert.Equal(t, ` xyz `, r) img = Image{HTTP2HTTPS: true, RoutePath: "/img", RemarkURL: "http://example.com"} r = img.Convert(` xyz`) assert.Equal(t, ` xyz`, r, "http:// remark url, no proxy") img = Image{HTTP2HTTPS: false, RoutePath: "/img"} r = img.Convert(` xyz`) assert.Equal(t, ` xyz`, r, "disabled, no proxy") } func TestImage_ConvertCachingMode(t *testing.T) { img := Image{CacheExternal: true, RoutePath: "/img", RemarkURL: "https://remark42.com"} r := img.Convert(` xyz `) assert.Equal(t, ` xyz `, r) r = img.Convert(` xyz `) assert.Equal(t, ` xyz `, r) r = img.Convert(``) assert.Equal(t, ``, r) img = Image{CacheExternal: false, RoutePath: "/img", RemarkURL: "https://remark42.com"} r = img.Convert(``) assert.Equal(t, ``, r) // both Caching and Proxy enabled img = Image{CacheExternal: true, HTTP2HTTPS: true, RoutePath: "/img", RemarkURL: "https://remark42.com"} r = img.Convert(` xyz `) assert.Equal(t, ` xyz `, r) } func imgHTTPTestsServer(t *testing.T) *httptest.Server { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/image/img1.png" { t.Log("http img request", r.URL) w.Header().Add("Content-Length", "123") w.Header().Add("Content-Type", "image/png") _, err := w.Write([]byte(fmt.Sprintf("%123s", "X"))) assert.NoError(t, err) return } if r.URL.Path == "/image/img-slow.png" { time.Sleep(500 * time.Millisecond) w.WriteHeader(500) return } t.Log("http img request - not found", r.URL) w.WriteHeader(404) })) return ts }