github.com/avenga/couper@v1.12.2/handler/spa_test.go (about)

     1  package handler_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"os"
     9  	"path"
    10  	"testing"
    11  
    12  	"github.com/google/go-cmp/cmp"
    13  	"github.com/hashicorp/hcl/v2"
    14  	"github.com/zclconf/go-cty/cty"
    15  
    16  	"github.com/avenga/couper/config"
    17  	"github.com/avenga/couper/config/request"
    18  	"github.com/avenga/couper/config/runtime/server"
    19  	"github.com/avenga/couper/eval"
    20  	"github.com/avenga/couper/handler"
    21  )
    22  
    23  func TestSpa_ServeHTTP(t *testing.T) {
    24  	wd, werr := os.Getwd()
    25  	if werr != nil {
    26  		t.Error(werr)
    27  	}
    28  
    29  	appHtmlContent, ferr := os.ReadFile(path.Join(wd, "testdata/spa/app.html"))
    30  	if ferr != nil {
    31  		t.Fatal(ferr)
    32  	}
    33  
    34  	tests := []struct {
    35  		cfg             *config.Spa
    36  		req             *http.Request
    37  		expectedContent []byte
    38  		expectedCode    int
    39  		expectedErr     string
    40  	}{
    41  		{&config.Spa{Name: "serve bootstrap file", BootstrapFile: path.Join(wd, "testdata/spa/app.html")}, httptest.NewRequest(http.MethodGet, "/", nil), appHtmlContent, http.StatusOK, ""},
    42  		{&config.Spa{Name: "serve no bootstrap file", BootstrapFile: path.Join(wd, "testdata/spa/not_exist.html")}, httptest.NewRequest(http.MethodGet, "/", nil), nil, http.StatusNotFound, fmt.Sprintf("open %s/testdata/spa/not_exist.html: no such file or directory", wd)},
    43  		{&config.Spa{Name: "serve bootstrap dir", BootstrapFile: path.Join(wd, "testdata/spa")}, httptest.NewRequest(http.MethodGet, "/", nil), nil, http.StatusInternalServerError, ""},
    44  		{&config.Spa{Name: "serve bootstrap file /w simple-data", BootstrapFile: path.Join(wd, "testdata/spa/app_bs_data.html"),
    45  			BootstrapData: hcl.StaticExpr(cty.StringVal("no-object"), hcl.Range{})}, httptest.NewRequest(http.MethodGet, "/", nil),
    46  			nil, http.StatusInternalServerError, ":0,0-0: configuration error; bootstrap_data must be an object type"},
    47  		{&config.Spa{Name: "serve bootstrap file /w obj-data", BootstrapFile: path.Join(wd, "testdata/spa/app_bs_data.html"),
    48  			BootstrapData: hcl.StaticExpr(cty.ObjectVal(map[string]cty.Value{"prop": cty.StringVal("value")}), hcl.Range{})}, httptest.NewRequest(http.MethodGet, "/", nil),
    49  			[]byte(`<!DOCTYPE html>
    50  <html lang="en">
    51  <head>
    52      <meta charset="UTF-8">
    53      <title>App</title>
    54      <script>const conf = {"prop":"value"};</script>
    55  </head>
    56  <body>
    57  App with __BOOTSTRAP_DATA__.
    58  </body>
    59  </html>
    60  `), http.StatusOK, ""},
    61  		{&config.Spa{Name: "serve bootstrap file /w obj-data /custom placeholder", BootStrapDataName: "__SERVER_DATA__", BootstrapFile: path.Join(wd, "testdata/spa/app_bs_data_custom.html"),
    62  			BootstrapData: hcl.StaticExpr(cty.ObjectVal(map[string]cty.Value{"prop": cty.StringVal("value")}), hcl.Range{})}, httptest.NewRequest(http.MethodGet, "/", nil),
    63  			[]byte(`<!DOCTYPE html>
    64  <html lang="en">
    65  <head>
    66      <meta charset="UTF-8">
    67      <title>App</title>
    68      <script>const conf = {"prop":"value"};</script>
    69  </head>
    70  <body>
    71  App with __SERVER_DATA__.
    72  </body>
    73  </html>
    74  `), http.StatusOK, ""},
    75  		{&config.Spa{Name: "serve bootstrap file /w html obj-data", BootstrapFile: path.Join(wd, "testdata/spa/app_bs_data.html"),
    76  			BootstrapData: hcl.StaticExpr(cty.ObjectVal(map[string]cty.Value{"prop": cty.StringVal("</script>")}), hcl.Range{})}, httptest.NewRequest(http.MethodGet, "/", nil),
    77  			[]byte(`<!DOCTYPE html>
    78  <html lang="en">
    79  <head>
    80      <meta charset="UTF-8">
    81      <title>App</title>
    82      <script>const conf = {"prop":"\u003c/script\u003e"};</script>
    83  </head>
    84  <body>
    85  App with __BOOTSTRAP_DATA__.
    86  </body>
    87  </html>
    88  `), http.StatusOK, ""},
    89  	}
    90  	for _, tt := range tests {
    91  		t.Run(tt.cfg.Name, func(subT *testing.T) {
    92  			opts, _ := server.NewServerOptions(&config.Server{}, nil)
    93  			s, err := handler.NewSpa(eval.NewDefaultContext().HCLContext(), tt.cfg, opts, nil)
    94  			if err != nil {
    95  				if tt.expectedErr != "" && err.Error() == tt.expectedErr {
    96  					return
    97  				}
    98  				subT.Fatal(err)
    99  			}
   100  
   101  			res := httptest.NewRecorder()
   102  			s.ServeHTTP(res, tt.req)
   103  
   104  			if !res.Flushed {
   105  				res.Flush()
   106  			}
   107  
   108  			if res.Code != tt.expectedCode {
   109  				subT.Errorf("Expected status code %d, got: %d", tt.expectedCode, res.Code)
   110  			}
   111  
   112  			if tt.expectedContent != nil {
   113  				if diff := cmp.Diff(tt.expectedContent, []byte(res.Body.String())); diff != "" {
   114  					t.Error(diff)
   115  				}
   116  			}
   117  		})
   118  	}
   119  }
   120  
   121  func BenchmarkSPA_BootstrapData(b *testing.B) {
   122  	wd, werr := os.Getwd()
   123  	if werr != nil {
   124  		b.Error(werr)
   125  	}
   126  
   127  	cfg := &config.Spa{Name: "serve bootstrap file /w obj-data", BootstrapFile: path.Join(wd, "testdata/spa/app_bs_data.html"),
   128  		BootstrapData: hcl.StaticExpr(cty.ObjectVal(map[string]cty.Value{"prop": cty.StringVal("value")}), hcl.Range{})}
   129  
   130  	opts, _ := server.NewServerOptions(&config.Server{}, nil)
   131  	s, err := handler.NewSpa(eval.NewDefaultContext().HCLContext(), cfg, opts, nil)
   132  	if err != nil {
   133  		b.Fatal(err)
   134  	}
   135  
   136  	req := httptest.NewRequest(http.MethodGet, "/", nil)
   137  	req = req.WithContext(context.WithValue(context.Background(), request.ContextType, eval.NewDefaultContext()))
   138  
   139  	b.ResetTimer()
   140  	b.ReportAllocs()
   141  
   142  	for i := 0; i < b.N; i++ {
   143  		res := httptest.NewRecorder()
   144  		s.ServeHTTP(res, req)
   145  		res.Flush()
   146  	}
   147  }