2017-06-08 13:40:51 +00:00
|
|
|
// Copyright 2017 Vector Creations Ltd
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"time"
|
|
|
|
|
2020-05-21 13:40:13 +00:00
|
|
|
"github.com/matrix-org/dendrite/internal/test"
|
2017-06-19 14:21:04 +00:00
|
|
|
"github.com/matrix-org/gomatrixserverlib"
|
|
|
|
"gopkg.in/yaml.v2"
|
2017-06-08 13:40:51 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// How long to wait for the server to write the expected output messages.
|
|
|
|
// This needs to be high enough to account for the time it takes to create
|
|
|
|
// the postgres database tables which can take a while on travis.
|
|
|
|
timeoutString = test.Defaulting(os.Getenv("TIMEOUT"), "10s")
|
|
|
|
// The name of maintenance database to connect to in order to create the test database.
|
|
|
|
postgresDatabase = test.Defaulting(os.Getenv("POSTGRES_DATABASE"), "postgres")
|
|
|
|
// The name of the test database to create.
|
|
|
|
testDatabaseName = test.Defaulting(os.Getenv("DATABASE_NAME"), "mediaapi_test")
|
|
|
|
// Postgres docker container name (for running psql). If not set, psql must be in PATH.
|
|
|
|
postgresContainerName = os.Getenv("POSTGRES_CONTAINER")
|
|
|
|
// Test image to be uploaded/downloaded
|
2019-05-21 20:56:55 +00:00
|
|
|
testJPEG = test.Defaulting(os.Getenv("TEST_JPEG_PATH"), "cmd/mediaapi-integration-tests/totem.jpg")
|
2017-06-19 14:21:04 +00:00
|
|
|
kafkaURI = test.Defaulting(os.Getenv("KAFKA_URIS"), "localhost:9092")
|
2017-06-08 13:40:51 +00:00
|
|
|
)
|
|
|
|
|
2017-06-19 14:21:04 +00:00
|
|
|
var thumbnailSizes = (`
|
2017-06-08 13:40:51 +00:00
|
|
|
- width: 32
|
|
|
|
height: 32
|
|
|
|
method: crop
|
|
|
|
- width: 96
|
|
|
|
height: 96
|
|
|
|
method: crop
|
|
|
|
- width: 320
|
|
|
|
height: 240
|
|
|
|
method: scale
|
|
|
|
- width: 640
|
|
|
|
height: 480
|
|
|
|
method: scale
|
|
|
|
- width: 800
|
|
|
|
height: 600
|
|
|
|
method: scale
|
|
|
|
`)
|
|
|
|
|
|
|
|
const serverType = "media-api"
|
|
|
|
|
2017-09-07 11:50:39 +00:00
|
|
|
const testMediaID = "1VuVy8u_hmDllD8BrcY0deM34Bl7SPJeY9J6BkMmpx0"
|
|
|
|
const testContentType = "image/jpeg"
|
|
|
|
const testOrigin = "localhost:18001"
|
|
|
|
|
2017-06-08 13:40:51 +00:00
|
|
|
var testDatabaseTemplate = "dbname=%s sslmode=disable binary_parameters=yes"
|
|
|
|
|
|
|
|
var timeout time.Duration
|
|
|
|
|
2017-06-19 14:21:04 +00:00
|
|
|
var port = 10000
|
|
|
|
|
2017-11-15 10:25:48 +00:00
|
|
|
func startMediaAPI(suffix string, dynamicThumbnails bool) (*exec.Cmd, chan error, *exec.Cmd, string, string) {
|
2017-06-08 13:40:51 +00:00
|
|
|
dir, err := ioutil.TempDir("", serverType+"-server-test"+suffix)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
proxyAddr := "localhost:1800" + suffix
|
|
|
|
|
2017-06-19 14:21:04 +00:00
|
|
|
database := fmt.Sprintf(testDatabaseTemplate, testDatabaseName+suffix)
|
|
|
|
cfg, nextPort, err := test.MakeConfig(dir, kafkaURI, database, "localhost", port)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2020-08-10 13:18:04 +00:00
|
|
|
cfg.Global.ServerName = gomatrixserverlib.ServerName(proxyAddr)
|
|
|
|
cfg.MediaAPI.DynamicThumbnails = dynamicThumbnails
|
|
|
|
if err = yaml.Unmarshal([]byte(thumbnailSizes), &cfg.MediaAPI.ThumbnailSizes); err != nil {
|
2017-06-19 14:21:04 +00:00
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
port = nextPort
|
|
|
|
if err = test.WriteConfig(cfg, dir); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2017-06-08 13:40:51 +00:00
|
|
|
|
|
|
|
serverArgs := []string{
|
2017-06-19 14:21:04 +00:00
|
|
|
"--config", filepath.Join(dir, test.ConfigFile),
|
2017-06-08 13:40:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
databases := []string{
|
|
|
|
testDatabaseName + suffix,
|
|
|
|
}
|
|
|
|
|
2017-11-15 10:25:48 +00:00
|
|
|
proxyCmd, _ := test.StartProxy(proxyAddr, cfg)
|
2017-06-08 13:40:51 +00:00
|
|
|
|
2017-07-17 17:10:56 +00:00
|
|
|
test.InitDatabase(
|
2017-06-08 13:40:51 +00:00
|
|
|
postgresDatabase,
|
|
|
|
postgresContainerName,
|
|
|
|
databases,
|
|
|
|
)
|
|
|
|
|
2017-07-17 17:10:56 +00:00
|
|
|
cmd, cmdChan := test.CreateBackgroundCommand(
|
|
|
|
filepath.Join(filepath.Dir(os.Args[0]), "dendrite-"+serverType+"-server"),
|
|
|
|
serverArgs,
|
|
|
|
)
|
|
|
|
|
2020-08-13 11:16:37 +00:00
|
|
|
fmt.Printf("==TESTSERVER== STARTED %v -> %v : %v\n", proxyAddr, cfg.MediaAPI.InternalAPI.Listen, dir)
|
2017-11-15 10:25:48 +00:00
|
|
|
return cmd, cmdChan, proxyCmd, proxyAddr, dir
|
2017-06-08 13:40:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func cleanUpServer(cmd *exec.Cmd, dir string) {
|
2017-09-20 09:59:19 +00:00
|
|
|
// ensure server is dead, only cleaning up so don't care about errors this returns
|
|
|
|
cmd.Process.Kill() // nolint: errcheck
|
2017-06-08 13:40:51 +00:00
|
|
|
if err := os.RemoveAll(dir); err != nil {
|
|
|
|
fmt.Printf("WARNING: Failed to remove temporary directory %v: %q\n", dir, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Runs a battery of media API server tests
|
|
|
|
// The tests will pause at various points in this list to conduct tests on the HTTP responses before continuing.
|
|
|
|
func main() {
|
|
|
|
fmt.Println("==TESTING==", os.Args[0])
|
|
|
|
|
|
|
|
var err error
|
|
|
|
timeout, err = time.ParseDuration(timeoutString)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("ERROR: Invalid timeout string %v: %q\n", timeoutString, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// create server1 with only pre-generated thumbnails allowed
|
2017-11-15 10:25:48 +00:00
|
|
|
server1Cmd, server1CmdChan, server1ProxyCmd, server1ProxyAddr, server1Dir := startMediaAPI("1", false)
|
2017-06-08 13:40:51 +00:00
|
|
|
defer cleanUpServer(server1Cmd, server1Dir)
|
2017-09-20 09:59:19 +00:00
|
|
|
defer server1ProxyCmd.Process.Kill() // nolint: errcheck
|
2017-09-07 11:50:39 +00:00
|
|
|
testDownload(server1ProxyAddr, server1ProxyAddr, "doesnotexist", 404, server1CmdChan)
|
2017-06-08 13:40:51 +00:00
|
|
|
|
|
|
|
// upload a JPEG file
|
2017-09-07 11:50:39 +00:00
|
|
|
testUpload(
|
|
|
|
server1ProxyAddr, testJPEG,
|
|
|
|
)
|
2017-06-08 13:40:51 +00:00
|
|
|
|
|
|
|
// download that JPEG file
|
2017-09-07 11:50:39 +00:00
|
|
|
testDownload(server1ProxyAddr, testOrigin, testMediaID, 200, server1CmdChan)
|
2017-06-08 13:40:51 +00:00
|
|
|
|
|
|
|
// thumbnail that JPEG file
|
2017-09-07 11:50:39 +00:00
|
|
|
testThumbnail(64, 64, "crop", server1ProxyAddr, server1CmdChan)
|
2017-06-08 13:40:51 +00:00
|
|
|
|
|
|
|
// create server2 with dynamic thumbnail generation
|
2017-11-15 10:25:48 +00:00
|
|
|
server2Cmd, server2CmdChan, server2ProxyCmd, server2ProxyAddr, server2Dir := startMediaAPI("2", true)
|
2017-06-08 13:40:51 +00:00
|
|
|
defer cleanUpServer(server2Cmd, server2Dir)
|
2017-09-20 09:59:19 +00:00
|
|
|
defer server2ProxyCmd.Process.Kill() // nolint: errcheck
|
2017-09-07 11:50:39 +00:00
|
|
|
testDownload(server2ProxyAddr, server2ProxyAddr, "doesnotexist", 404, server2CmdChan)
|
2017-06-08 13:40:51 +00:00
|
|
|
|
|
|
|
// pre-generated thumbnail that JPEG file via server2
|
2017-09-07 11:50:39 +00:00
|
|
|
testThumbnail(800, 600, "scale", server2ProxyAddr, server2CmdChan)
|
2017-06-08 13:40:51 +00:00
|
|
|
|
|
|
|
// download that JPEG file via server2
|
2017-09-07 11:50:39 +00:00
|
|
|
testDownload(server2ProxyAddr, testOrigin, testMediaID, 200, server2CmdChan)
|
2017-06-08 13:40:51 +00:00
|
|
|
|
|
|
|
// dynamic thumbnail that JPEG file via server2
|
2017-09-07 11:50:39 +00:00
|
|
|
testThumbnail(1920, 1080, "scale", server2ProxyAddr, server2CmdChan)
|
2017-06-08 13:40:51 +00:00
|
|
|
|
|
|
|
// thumbnail that JPEG file via server2
|
2017-09-07 11:50:39 +00:00
|
|
|
testThumbnail(10000, 10000, "scale", server2ProxyAddr, server2CmdChan)
|
2017-06-08 13:40:51 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-09-07 11:50:39 +00:00
|
|
|
func getMediaURI(host, endpoint, query string, components []string) string {
|
2017-06-08 13:40:51 +00:00
|
|
|
pathComponents := []string{host, "_matrix/media/v1", endpoint}
|
|
|
|
pathComponents = append(pathComponents, components...)
|
2017-09-07 11:50:39 +00:00
|
|
|
return "https://" + path.Join(pathComponents...) + query
|
2017-06-08 13:40:51 +00:00
|
|
|
}
|
|
|
|
|
2017-09-07 11:50:39 +00:00
|
|
|
func testUpload(host, filePath string) {
|
2017-06-08 13:40:51 +00:00
|
|
|
fmt.Printf("==TESTING== upload %v to %v\n", filePath, host)
|
|
|
|
file, err := os.Open(filePath)
|
2017-09-20 13:54:17 +00:00
|
|
|
defer file.Close() // nolint: errcheck, staticcheck, megacheck
|
2017-06-08 13:40:51 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
filename := filepath.Base(filePath)
|
|
|
|
stat, err := file.Stat()
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
fileSize := stat.Size()
|
|
|
|
|
|
|
|
req, err := http.NewRequest(
|
|
|
|
"POST",
|
2017-09-07 11:50:39 +00:00
|
|
|
getMediaURI(host, "upload", "?filename="+filename, nil),
|
2017-06-08 13:40:51 +00:00
|
|
|
file,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
req.ContentLength = fileSize
|
2017-09-07 11:50:39 +00:00
|
|
|
req.Header.Set("Content-Type", testContentType)
|
2017-06-08 13:40:51 +00:00
|
|
|
|
2017-09-07 11:50:39 +00:00
|
|
|
wantedBody := `{"content_uri": "mxc://localhost:18001/` + testMediaID + `"}`
|
2017-06-08 13:40:51 +00:00
|
|
|
testReq := &test.Request{
|
|
|
|
Req: req,
|
2017-09-07 11:50:39 +00:00
|
|
|
WantedStatusCode: 200,
|
2017-06-08 13:40:51 +00:00
|
|
|
WantedBody: test.CanonicalJSONInput([]string{wantedBody})[0],
|
|
|
|
}
|
|
|
|
if err := testReq.Do(); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
fmt.Printf("==TESTING== upload %v to %v PASSED\n", filePath, host)
|
|
|
|
}
|
|
|
|
|
2017-09-07 11:50:39 +00:00
|
|
|
func testDownload(host, origin, mediaID string, wantedStatusCode int, serverCmdChan chan error) {
|
2017-06-08 13:40:51 +00:00
|
|
|
req, err := http.NewRequest(
|
|
|
|
"GET",
|
2017-09-07 11:50:39 +00:00
|
|
|
getMediaURI(host, "download", "", []string{
|
2017-06-08 13:40:51 +00:00
|
|
|
origin,
|
|
|
|
mediaID,
|
|
|
|
}),
|
|
|
|
nil,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
testReq := &test.Request{
|
|
|
|
Req: req,
|
|
|
|
WantedStatusCode: wantedStatusCode,
|
2017-11-27 12:05:14 +00:00
|
|
|
WantedBody: "",
|
2017-06-08 13:40:51 +00:00
|
|
|
}
|
|
|
|
testReq.Run(fmt.Sprintf("download mxc://%v/%v from %v", origin, mediaID, host), timeout, serverCmdChan)
|
|
|
|
}
|
|
|
|
|
2017-09-07 11:50:39 +00:00
|
|
|
func testThumbnail(width, height int, resizeMethod, host string, serverCmdChan chan error) {
|
2017-06-08 13:40:51 +00:00
|
|
|
query := fmt.Sprintf("?width=%v&height=%v", width, height)
|
|
|
|
if resizeMethod != "" {
|
|
|
|
query += "&method=" + resizeMethod
|
|
|
|
}
|
|
|
|
req, err := http.NewRequest(
|
|
|
|
"GET",
|
2017-09-07 11:50:39 +00:00
|
|
|
getMediaURI(host, "thumbnail", query, []string{
|
|
|
|
testOrigin,
|
|
|
|
testMediaID,
|
2017-06-08 13:40:51 +00:00
|
|
|
}),
|
|
|
|
nil,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
testReq := &test.Request{
|
|
|
|
Req: req,
|
2017-09-07 11:50:39 +00:00
|
|
|
WantedStatusCode: 200,
|
2017-11-27 12:05:14 +00:00
|
|
|
WantedBody: "",
|
2017-06-08 13:40:51 +00:00
|
|
|
}
|
2017-09-07 11:50:39 +00:00
|
|
|
testReq.Run(fmt.Sprintf("thumbnail mxc://%v/%v%v from %v", testOrigin, testMediaID, query, host), timeout, serverCmdChan)
|
2017-06-08 13:40:51 +00:00
|
|
|
}
|