gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

testrig.go (7538B)


      1 // GoToSocial
      2 // Copyright (C) GoToSocial Authors admin@gotosocial.org
      3 // SPDX-License-Identifier: AGPL-3.0-or-later
      4 //
      5 // This program is free software: you can redistribute it and/or modify
      6 // it under the terms of the GNU Affero General Public License as published by
      7 // the Free Software Foundation, either version 3 of the License, or
      8 // (at your option) any later version.
      9 //
     10 // This program is distributed in the hope that it will be useful,
     11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 // GNU Affero General Public License for more details.
     14 //
     15 // You should have received a copy of the GNU Affero General Public License
     16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 package testrig
     19 
     20 import (
     21 	"bytes"
     22 	"context"
     23 	"errors"
     24 	"fmt"
     25 	"io"
     26 	"net/http"
     27 	"os"
     28 	"os/signal"
     29 	"syscall"
     30 
     31 	"github.com/gin-gonic/gin"
     32 	"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
     33 	"github.com/superseriousbusiness/gotosocial/internal/api"
     34 	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
     35 	"github.com/superseriousbusiness/gotosocial/internal/config"
     36 	"github.com/superseriousbusiness/gotosocial/internal/gotosocial"
     37 	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
     38 	"github.com/superseriousbusiness/gotosocial/internal/log"
     39 	"github.com/superseriousbusiness/gotosocial/internal/middleware"
     40 	"github.com/superseriousbusiness/gotosocial/internal/oidc"
     41 	tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
     42 	"github.com/superseriousbusiness/gotosocial/internal/state"
     43 	"github.com/superseriousbusiness/gotosocial/internal/storage"
     44 	"github.com/superseriousbusiness/gotosocial/internal/timeline"
     45 	"github.com/superseriousbusiness/gotosocial/internal/tracing"
     46 	"github.com/superseriousbusiness/gotosocial/internal/visibility"
     47 	"github.com/superseriousbusiness/gotosocial/internal/web"
     48 	"github.com/superseriousbusiness/gotosocial/testrig"
     49 )
     50 
     51 // Start creates and starts a gotosocial testrig server
     52 var Start action.GTSAction = func(ctx context.Context) error {
     53 	var state state.State
     54 
     55 	testrig.InitTestConfig()
     56 	testrig.InitTestLog()
     57 
     58 	if err := tracing.Initialize(); err != nil {
     59 		return fmt.Errorf("error initializing tracing: %w", err)
     60 	}
     61 
     62 	// Initialize caches and database
     63 	state.DB = testrig.NewTestDB(&state)
     64 
     65 	// New test db inits caches so we don't need to do
     66 	// that twice, we can just start the initialized caches.
     67 	state.Caches.Start()
     68 	defer state.Caches.Stop()
     69 
     70 	testrig.StandardDBSetup(state.DB, nil)
     71 
     72 	if os.Getenv("GTS_STORAGE_BACKEND") == "s3" {
     73 		state.Storage, _ = storage.NewS3Storage()
     74 	} else {
     75 		state.Storage = testrig.NewInMemoryStorage()
     76 	}
     77 	testrig.StandardStorageSetup(state.Storage, "./testrig/media")
     78 
     79 	// Initialize workers.
     80 	state.Workers.Start()
     81 	defer state.Workers.Stop()
     82 
     83 	// build backend handlers
     84 	transportController := testrig.NewTestTransportController(&state, testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
     85 		r := io.NopCloser(bytes.NewReader([]byte{}))
     86 		return &http.Response{
     87 			StatusCode: 200,
     88 			Body:       r,
     89 		}, nil
     90 	}, ""))
     91 	mediaManager := testrig.NewTestMediaManager(&state)
     92 	federator := testrig.NewTestFederator(&state, transportController, mediaManager)
     93 
     94 	emailSender := testrig.NewEmailSender("./web/template/", nil)
     95 	typeConverter := testrig.NewTestTypeConverter(state.DB)
     96 	filter := visibility.NewFilter(&state)
     97 
     98 	// Initialize timelines.
     99 	state.Timelines.Home = timeline.NewManager(
    100 		tlprocessor.HomeTimelineGrab(&state),
    101 		tlprocessor.HomeTimelineFilter(&state, filter),
    102 		tlprocessor.HomeTimelineStatusPrepare(&state, typeConverter),
    103 		tlprocessor.SkipInsert(),
    104 	)
    105 	if err := state.Timelines.Home.Start(); err != nil {
    106 		return fmt.Errorf("error starting home timeline: %s", err)
    107 	}
    108 
    109 	state.Timelines.List = timeline.NewManager(
    110 		tlprocessor.ListTimelineGrab(&state),
    111 		tlprocessor.ListTimelineFilter(&state, filter),
    112 		tlprocessor.ListTimelineStatusPrepare(&state, typeConverter),
    113 		tlprocessor.SkipInsert(),
    114 	)
    115 	if err := state.Timelines.List.Start(); err != nil {
    116 		return fmt.Errorf("error starting list timeline: %s", err)
    117 	}
    118 
    119 	processor := testrig.NewTestProcessor(&state, federator, emailSender, mediaManager)
    120 
    121 	/*
    122 		HTTP router initialization
    123 	*/
    124 
    125 	router := testrig.NewTestRouter(state.DB)
    126 	middlewares := []gin.HandlerFunc{
    127 		middleware.AddRequestID(config.GetRequestIDHeader()), // requestID middleware must run before tracing
    128 	}
    129 	if config.GetTracingEnabled() {
    130 		middlewares = append(middlewares, tracing.InstrumentGin())
    131 	}
    132 	middlewares = append(middlewares, []gin.HandlerFunc{
    133 		middleware.Logger(config.GetLogClientIP()),
    134 		middleware.UserAgent(),
    135 		middleware.CORS(),
    136 		middleware.ExtraHeaders(),
    137 	}...)
    138 
    139 	// attach global middlewares which are used for every request
    140 	router.AttachGlobalMiddleware(middlewares...)
    141 
    142 	// attach global no route / 404 handler to the router
    143 	router.AttachNoRouteHandler(func(c *gin.Context) {
    144 		apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGetV1)
    145 	})
    146 
    147 	// build router modules
    148 	var idp oidc.IDP
    149 	var err error
    150 	if config.GetOIDCEnabled() {
    151 		idp, err = oidc.NewIDP(ctx)
    152 		if err != nil {
    153 			return fmt.Errorf("error creating oidc idp: %w", err)
    154 		}
    155 	}
    156 
    157 	routerSession, err := state.DB.GetSession(ctx)
    158 	if err != nil {
    159 		return fmt.Errorf("error retrieving router session for session middleware: %w", err)
    160 	}
    161 
    162 	sessionName, err := middleware.SessionName()
    163 	if err != nil {
    164 		return fmt.Errorf("error generating session name for session middleware: %w", err)
    165 	}
    166 
    167 	var (
    168 		authModule        = api.NewAuth(state.DB, processor, idp, routerSession, sessionName) // auth/oauth paths
    169 		clientModule      = api.NewClient(state.DB, processor)                                // api client endpoints
    170 		fileserverModule  = api.NewFileserver(processor)                                      // fileserver endpoints
    171 		wellKnownModule   = api.NewWellKnown(processor)                                       // .well-known endpoints
    172 		nodeInfoModule    = api.NewNodeInfo(processor)                                        // nodeinfo endpoint
    173 		activityPubModule = api.NewActivityPub(state.DB, processor)                           // ActivityPub endpoints
    174 		webModule         = web.New(state.DB, processor)                                      // web pages + user profiles + settings panels etc
    175 	)
    176 
    177 	// these should be routed in order
    178 	authModule.Route(router)
    179 	clientModule.Route(router)
    180 	fileserverModule.Route(router)
    181 	wellKnownModule.Route(router)
    182 	nodeInfoModule.Route(router)
    183 	activityPubModule.Route(router)
    184 	activityPubModule.RoutePublicKey(router)
    185 	webModule.Route(router)
    186 
    187 	gts, err := gotosocial.NewServer(state.DB, router, federator, mediaManager)
    188 	if err != nil {
    189 		return fmt.Errorf("error creating gotosocial service: %s", err)
    190 	}
    191 
    192 	if err := gts.Start(ctx); err != nil {
    193 		return fmt.Errorf("error starting gotosocial service: %s", err)
    194 	}
    195 
    196 	// catch shutdown signals from the operating system
    197 	sigs := make(chan os.Signal, 1)
    198 	signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)
    199 	sig := <-sigs
    200 	log.Infof(ctx, "received signal %s, shutting down", sig)
    201 
    202 	testrig.StandardDBTeardown(state.DB)
    203 	testrig.StandardStorageTeardown(state.Storage)
    204 
    205 	// close down all running services in order
    206 	if err := gts.Stop(ctx); err != nil {
    207 		return fmt.Errorf("error closing gotosocial service: %s", err)
    208 	}
    209 
    210 	log.Info(ctx, "done! exiting...")
    211 	return nil
    212 }