123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- package testserver
- import (
- "context"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strings"
- "testing"
- "time"
- "github.com/pkg/errors"
- // sqlite driver.
- _ "modernc.org/sqlite"
- "github.com/usememos/memos/api/auth"
- "github.com/usememos/memos/server"
- "github.com/usememos/memos/server/profile"
- "github.com/usememos/memos/store"
- "github.com/usememos/memos/store/db"
- "github.com/usememos/memos/test"
- )
- type TestingServer struct {
- server *server.Server
- client *http.Client
- profile *profile.Profile
- cookie string
- }
- func NewTestingServer(ctx context.Context, t *testing.T) (*TestingServer, error) {
- profile := test.GetTestingProfile(t)
- dbDriver, err := db.NewDBDriver(profile)
- if err != nil {
- return nil, errors.Wrap(err, "failed to create db driver")
- }
- if err := dbDriver.Migrate(ctx); err != nil {
- return nil, errors.Wrap(err, "failed to migrate db")
- }
- store := store.New(dbDriver, profile)
- server, err := server.NewServer(ctx, profile, store)
- if err != nil {
- return nil, errors.Wrap(err, "failed to create server")
- }
- s := &TestingServer{
- server: server,
- client: &http.Client{},
- profile: profile,
- cookie: "",
- }
- errChan := make(chan error, 1)
- go func() {
- if err := s.server.Start(ctx); err != nil {
- if err != http.ErrServerClosed {
- errChan <- errors.Wrap(err, "failed to run main server")
- }
- }
- }()
- if err := s.waitForServerStart(errChan); err != nil {
- return nil, errors.Wrap(err, "failed to start server")
- }
- return s, nil
- }
- func (s *TestingServer) Shutdown(ctx context.Context) {
- s.server.Shutdown(ctx)
- }
- func (s *TestingServer) waitForServerStart(errChan <-chan error) error {
- ticker := time.NewTicker(100 * time.Millisecond)
- defer ticker.Stop()
- for {
- select {
- case <-ticker.C:
- if s == nil {
- continue
- }
- e := s.server.GetEcho()
- if e == nil {
- continue
- }
- addr := e.ListenerAddr()
- if addr != nil && strings.Contains(addr.String(), ":") {
- return nil // was started
- }
- case err := <-errChan:
- if err == http.ErrServerClosed {
- return nil
- }
- return err
- }
- }
- }
- func (s *TestingServer) request(method, uri string, body io.Reader, params, header map[string]string) (io.ReadCloser, error) {
- fullURL := fmt.Sprintf("http://localhost:%d%s", s.profile.Port, uri)
- req, err := http.NewRequest(method, fullURL, body)
- if err != nil {
- return nil, errors.Wrapf(err, "fail to create a new %s request(%q)", method, fullURL)
- }
- for k, v := range header {
- req.Header.Set(k, v)
- }
- q := url.Values{}
- for k, v := range params {
- q.Add(k, v)
- }
- if len(q) > 0 {
- req.URL.RawQuery = q.Encode()
- }
- resp, err := s.client.Do(req)
- if err != nil {
- return nil, errors.Wrapf(err, "fail to send a %s request(%q)", method, fullURL)
- }
- if resp.StatusCode != http.StatusOK {
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil, errors.Wrap(err, "failed to read http response body")
- }
- return nil, errors.Errorf("http response error code %v body %q", resp.StatusCode, string(body))
- }
- if method == "POST" {
- if strings.Contains(uri, "/api/v1/auth/login") || strings.Contains(uri, "/api/v1/auth/signup") {
- cookie := ""
- h := resp.Header.Get("Set-Cookie")
- parts := strings.Split(h, "; ")
- for _, p := range parts {
- if strings.HasPrefix(p, fmt.Sprintf("%s=", auth.AccessTokenCookieName)) {
- cookie = p
- break
- }
- }
- if cookie == "" {
- return nil, errors.New("unable to find access token in the login response headers")
- }
- s.cookie = cookie
- } else if strings.Contains(uri, "/api/v1/auth/signout") {
- s.cookie = ""
- }
- }
- return resp.Body, nil
- }
- // get sends a GET client request.
- func (s *TestingServer) get(url string, params map[string]string) (io.ReadCloser, error) {
- return s.request("GET", url, nil, params, map[string]string{
- "Cookie": s.cookie,
- })
- }
- // post sends a POST client request.
- func (s *TestingServer) post(url string, body io.Reader, params map[string]string) (io.ReadCloser, error) {
- return s.request("POST", url, body, params, map[string]string{
- "Cookie": s.cookie,
- })
- }
- // patch sends a PATCH client request.
- func (s *TestingServer) patch(url string, body io.Reader, params map[string]string) (io.ReadCloser, error) {
- return s.request("PATCH", url, body, params, map[string]string{
- "Cookie": s.cookie,
- })
- }
- // delete sends a DELETE client request.
- func (s *TestingServer) delete(url string, params map[string]string) (io.ReadCloser, error) {
- return s.request("DELETE", url, nil, params, map[string]string{
- "Cookie": s.cookie,
- })
- }
|