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 }