Add very basic syncapi tests

This commit is contained in:
Kegan Dougal 2022-05-06 16:21:07 +01:00
parent 6493c0c0f2
commit 850bfb24ee
7 changed files with 225 additions and 11 deletions

View file

@ -102,7 +102,7 @@ func TestMain(m *testing.M) {
)
// Finally, build the server key APIs.
sbase := base.NewBaseDendrite(cfg, "Monolith", base.NoCacheMetrics)
sbase := base.NewBaseDendrite(cfg, "Monolith", base.DisableMetrics)
s.api = NewInternalAPI(sbase, s.fedclient, nil, s.cache, nil, true)
}

View file

@ -84,6 +84,7 @@ type BaseDendrite struct {
DNSCache *gomatrixserverlib.DNSCache
Database *sql.DB
DatabaseWriter sqlutil.Writer
EnableMetrics bool
}
const NoListener = ""
@ -94,7 +95,7 @@ const HTTPClientTimeout = time.Second * 30
type BaseDendriteOptions int
const (
NoCacheMetrics BaseDendriteOptions = iota
DisableMetrics BaseDendriteOptions = iota
UseHTTPAPIs
PolylithMode
)
@ -105,12 +106,12 @@ const (
func NewBaseDendrite(cfg *config.Dendrite, componentName string, options ...BaseDendriteOptions) *BaseDendrite {
platformSanityChecks()
useHTTPAPIs := false
cacheMetrics := true
enableMetrics := true
isMonolith := true
for _, opt := range options {
switch opt {
case NoCacheMetrics:
cacheMetrics = false
case DisableMetrics:
enableMetrics = false
case UseHTTPAPIs:
useHTTPAPIs = true
case PolylithMode:
@ -158,7 +159,7 @@ func NewBaseDendrite(cfg *config.Dendrite, componentName string, options ...Base
}
}
cache, err := caching.NewInMemoryLRUCache(cacheMetrics)
cache, err := caching.NewInMemoryLRUCache(enableMetrics)
if err != nil {
logrus.WithError(err).Warnf("Failed to create cache")
}
@ -243,6 +244,7 @@ func NewBaseDendrite(cfg *config.Dendrite, componentName string, options ...Base
apiHttpClient: &apiClient,
Database: db, // set if monolith with global connection pool only
DatabaseWriter: writer, // set if monolith with global connection pool only
EnableMetrics: enableMetrics,
}
}

View file

@ -65,11 +65,13 @@ func NewRequestPool(
userAPI userapi.SyncUserAPI, keyAPI keyapi.SyncKeyAPI,
rsAPI roomserverAPI.SyncRoomserverAPI,
streams *streams.Streams, notifier *notifier.Notifier,
producer PresencePublisher,
producer PresencePublisher, enableMetrics bool,
) *RequestPool {
prometheus.MustRegister(
activeSyncRequests, waitingSyncRequests,
)
if enableMetrics {
prometheus.MustRegister(
activeSyncRequests, waitingSyncRequests,
)
}
rp := &RequestPool{
db: db,
cfg: cfg,

View file

@ -65,7 +65,7 @@ func AddPublicRoutes(
JetStream: js,
}
requestPool := sync.NewRequestPool(syncDB, cfg, userAPI, keyAPI, rsAPI, streams, notifier, federationPresenceProducer)
requestPool := sync.NewRequestPool(syncDB, cfg, userAPI, keyAPI, rsAPI, streams, notifier, federationPresenceProducer, base.EnableMetrics)
userAPIStreamEventProducer := &producers.UserAPIStreamEventProducer{
JetStream: js,

95
syncapi/syncapi_test.go Normal file
View file

@ -0,0 +1,95 @@
package syncapi
import (
"context"
"net/http"
"net/http/httptest"
"testing"
keyapi "github.com/matrix-org/dendrite/keyserver/api"
rsapi "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/test"
userapi "github.com/matrix-org/dendrite/userapi/api"
)
var (
alice = "@alice:localhost"
aliceAccessToken = "ALICE_BEARER_TOKEN"
)
type syncRoomserverAPI struct {
rsapi.RoomserverInternalAPI
}
type syncUserAPI struct {
userapi.UserInternalAPI
}
func (s *syncUserAPI) QueryAccessToken(ctx context.Context, req *userapi.QueryAccessTokenRequest, res *userapi.QueryAccessTokenResponse) error {
if req.AccessToken == aliceAccessToken {
res.Device = &userapi.Device{
ID: "ID",
UserID: alice,
AccessToken: aliceAccessToken,
AccountType: userapi.AccountTypeUser,
DisplayName: "Alice",
}
return nil
}
res.Err = "unknown user"
return nil
}
func (s *syncUserAPI) PerformLastSeenUpdate(ctx context.Context, req *userapi.PerformLastSeenUpdateRequest, res *userapi.PerformLastSeenUpdateResponse) error {
return nil
}
type syncKeyAPI struct {
keyapi.KeyInternalAPI
}
func TestSyncAPI(t *testing.T) {
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
base, close := test.CreateBaseDendrite(t, dbType)
defer close()
AddPublicRoutes(base, &syncUserAPI{}, &syncRoomserverAPI{}, &syncKeyAPI{})
testCases := []struct {
name string
req *http.Request
wantCode int
}{
{
name: "missing access token",
req: test.NewRequest(t, "GET", "/_matrix/client/v3/sync", test.WithQueryParams(map[string]string{
"timeout": "0",
})),
wantCode: 401,
},
{
name: "unknown access token",
req: test.NewRequest(t, "GET", "/_matrix/client/v3/sync", test.WithQueryParams(map[string]string{
"access_token": "foo",
"timeout": "0",
})),
wantCode: 401,
},
{
name: "valid access token",
req: test.NewRequest(t, "GET", "/_matrix/client/v3/sync", test.WithQueryParams(map[string]string{
"access_token": aliceAccessToken,
"timeout": "0",
})),
wantCode: 200,
},
}
for _, tc := range testCases {
w := httptest.NewRecorder()
base.PublicClientAPIMux.ServeHTTP(w, tc.req)
if w.Code != tc.wantCode {
t.Fatalf("%s: got HTTP %d want %d", tc.name, w.Code, tc.wantCode)
}
}
})
}

70
test/base.go Normal file
View file

@ -0,0 +1,70 @@
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// 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 test
import (
"errors"
"io/fs"
"os"
"strings"
"testing"
"github.com/matrix-org/dendrite/setup/base"
"github.com/matrix-org/dendrite/setup/config"
)
func CreateBaseDendrite(t *testing.T, dbType DBType) (*base.BaseDendrite, func()) {
var cfg config.Dendrite
cfg.Defaults(false)
switch dbType {
case DBTypePostgres:
cfg.Global.Defaults(true) // autogen a signing key
cfg.MediaAPI.Defaults(true) // autogen a media path
connStr, close := PrepareDBConnectionString(t, dbType)
cfg.Global.DatabaseOptions = config.DatabaseOptions{
ConnectionString: config.DataSource(connStr),
MaxOpenConnections: 10,
MaxIdleConnections: 2,
ConnMaxLifetimeSeconds: 60,
}
return base.NewBaseDendrite(&cfg, "Test", base.DisableMetrics), close
case DBTypeSQLite:
cfg.Defaults(true) // sets a sqlite db per component
return base.NewBaseDendrite(&cfg, "Test", base.DisableMetrics), func() {
// cleanup db files. This risks getting out of sync as we add more database strings :(
dbFiles := []config.DataSource{
cfg.AppServiceAPI.Database.ConnectionString,
cfg.FederationAPI.Database.ConnectionString,
cfg.KeyServer.Database.ConnectionString,
cfg.MSCs.Database.ConnectionString,
cfg.MediaAPI.Database.ConnectionString,
cfg.RoomServer.Database.ConnectionString,
cfg.SyncAPI.Database.ConnectionString,
cfg.UserAPI.AccountDatabase.ConnectionString,
}
for _, fileURI := range dbFiles {
path := strings.TrimPrefix(string(fileURI), "file:")
err := os.Remove(path)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
t.Fatalf("failed to cleanup sqlite db '%s': %s", fileURI, err)
}
}
}
default:
t.Fatalf("unknown db type: %v", dbType)
}
return nil, nil
}

45
test/http.go Normal file
View file

@ -0,0 +1,45 @@
package test
import (
"bytes"
"encoding/json"
"io"
"net/http"
"net/url"
"testing"
)
type HTTPRequestOpt func(req *http.Request)
func WithJSONBody(t *testing.T, body interface{}) HTTPRequestOpt {
t.Helper()
b, err := json.Marshal(body)
if err != nil {
t.Fatalf("WithJSONBody: %s", err)
}
return func(req *http.Request) {
req.Body = io.NopCloser(bytes.NewBuffer(b))
}
}
func WithQueryParams(qps map[string]string) HTTPRequestOpt {
var vals url.Values = map[string][]string{}
for k, v := range qps {
vals.Set(k, v)
}
return func(req *http.Request) {
req.URL.RawQuery = vals.Encode()
}
}
func NewRequest(t *testing.T, method, path string, opts ...HTTPRequestOpt) *http.Request {
t.Helper()
req, err := http.NewRequest(method, "http://localhost"+path, nil)
if err != nil {
t.Fatalf("failed to make new HTTP request %v %v : %v", method, path, err)
}
for _, o := range opts {
o(req)
}
return req
}