From 86145be2b183bf8a3ecc46d1de7b8a374fc7c0cd Mon Sep 17 00:00:00 2001 From: Andrea Spacca Date: Fri, 9 Oct 2020 23:25:59 +0200 Subject: [PATCH 01/10] BASEPATH --- webapp/backend/pkg/web/server.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/webapp/backend/pkg/web/server.go b/webapp/backend/pkg/web/server.go index af993e3c..76f8fb9c 100644 --- a/webapp/backend/pkg/web/server.go +++ b/webapp/backend/pkg/web/server.go @@ -13,6 +13,7 @@ import ( "net/http" "os" "path/filepath" + "strings" ) type AppEngine struct { @@ -27,6 +28,17 @@ func (ae *AppEngine) Setup(logger logrus.FieldLogger) *gin.Engine { r.Use(middleware.ConfigMiddleware(ae.Config)) r.Use(gin.Recovery()) + basePath := ae.Config.GetString("web.src.backend.basepath") + if len(basePath) > 0 { + r.Group(basePath, func(c *gin.Context) { + c.Request.URL.Path = strings.TrimPrefix(c.Request.URL.Path, basePath) + r.HandleContext(c) + }) + } else { + basePath = "/" + } + + api := r.Group("/api") { api.GET("/health", func(c *gin.Context) { @@ -48,7 +60,7 @@ func (ae *AppEngine) Setup(logger logrus.FieldLogger) *gin.Engine { //redirect base url to /web r.GET("/", func(c *gin.Context) { - c.Redirect(http.StatusFound, "/web") + c.Redirect(http.StatusFound, basePath + "/web") }) //catch-all, serve index page. From ea3fbc09f14ba46c4f370bb7a311efc37d868be6 Mon Sep 17 00:00:00 2001 From: Andrea Spacca Date: Fri, 9 Oct 2020 23:26:33 +0200 Subject: [PATCH 02/10] BASEPATH --- docker/Dockerfile | 2 +- webapp/backend/pkg/web/server.go | 41 ++++++++----------- webapp/frontend/src/app/app.module.ts | 11 ++++- webapp/frontend/src/app/app.routing.ts | 11 +++++ .../layout/common/search/search.component.ts | 3 +- .../modules/dashboard/dashboard.service.ts | 3 +- .../src/app/modules/detail/detail.service.ts | 3 +- webapp/frontend/src/browserconfig.xml | 2 +- webapp/frontend/src/index.html | 3 +- webapp/frontend/src/manifest.json | 14 +++---- 10 files changed, 53 insertions(+), 40 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 5409a227..a4b19813 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -22,7 +22,7 @@ COPY webapp/frontend /scrutiny/src RUN npm install -g @angular/cli@9.1.4 && \ mkdir -p /scrutiny/dist && \ npm install && \ - ng build --output-path=/scrutiny/dist --deploy-url="/web/" --base-href="/web/" --prod + ng build --output-path=/scrutiny/dist --prod ######## diff --git a/webapp/backend/pkg/web/server.go b/webapp/backend/pkg/web/server.go index 76f8fb9c..6bfd99a8 100644 --- a/webapp/backend/pkg/web/server.go +++ b/webapp/backend/pkg/web/server.go @@ -13,7 +13,6 @@ import ( "net/http" "os" "path/filepath" - "strings" ) type AppEngine struct { @@ -29,37 +28,31 @@ func (ae *AppEngine) Setup(logger logrus.FieldLogger) *gin.Engine { r.Use(gin.Recovery()) basePath := ae.Config.GetString("web.src.backend.basepath") - if len(basePath) > 0 { - r.Group(basePath, func(c *gin.Context) { - c.Request.URL.Path = strings.TrimPrefix(c.Request.URL.Path, basePath) - r.HandleContext(c) - }) - } else { - basePath = "/" - } - - - api := r.Group("/api") + logger.Debugf("basepath: %s", basePath) + base := r.Group(basePath) { - api.GET("/health", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "success": true, + api := base.Group("/api") + { + api.GET("/health", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "success": true, + }) }) - }) - api.POST("/health/notify", handler.SendTestNotification) //check if notifications are configured correctly + api.POST("/health/notify", handler.SendTestNotification) //check if notifications are configured correctly - api.POST("/devices/register", handler.RegisterDevices) //used by Collector to register new devices and retrieve filtered list - api.GET("/summary", handler.GetDevicesSummary) //used by Dashboard - api.POST("/device/:wwn/smart", handler.UploadDeviceMetrics) //used by Collector to upload data - api.POST("/device/:wwn/selftest", handler.UploadDeviceSelfTests) - api.GET("/device/:wwn/details", handler.GetDeviceDetails) //used by Details + api.POST("/devices/register", handler.RegisterDevices) //used by Collector to register new devices and retrieve filtered list + api.GET("/summary", handler.GetDevicesSummary) //used by Dashboard + api.POST("/device/:wwn/smart", handler.UploadDeviceMetrics) //used by Collector to upload data + api.POST("/device/:wwn/selftest", handler.UploadDeviceSelfTests) + api.GET("/device/:wwn/details", handler.GetDeviceDetails) //used by Details + } } //Static request routing - r.StaticFS("/web", http.Dir(ae.Config.GetString("web.src.frontend.path"))) + base.StaticFS("/web", http.Dir(ae.Config.GetString("web.src.frontend.path"))) //redirect base url to /web - r.GET("/", func(c *gin.Context) { + base.GET("/", func(c *gin.Context) { c.Redirect(http.StatusFound, basePath + "/web") }) diff --git a/webapp/frontend/src/app/app.module.ts b/webapp/frontend/src/app/app.module.ts index 9b5ee3ad..904ee153 100644 --- a/webapp/frontend/src/app/app.module.ts +++ b/webapp/frontend/src/app/app.module.ts @@ -2,6 +2,7 @@ import { NgModule, enableProdMode } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ExtraOptions, PreloadAllModules, RouterModule } from '@angular/router'; +import { APP_BASE_HREF } from '@angular/common'; import { MarkdownModule } from 'ngx-markdown'; import { TreoModule } from '@treo'; import { TreoConfigModule } from '@treo/services/config'; @@ -11,7 +12,7 @@ import { appConfig } from 'app/core/config/app.config'; import { mockDataServices } from 'app/data/mock'; import { LayoutModule } from 'app/layout/layout.module'; import { AppComponent } from 'app/app.component'; -import { appRoutes } from 'app/app.routing'; +import { appRoutes, getAppBaseHref } from 'app/app.routing'; const routerConfig: ExtraOptions = { scrollPositionRestoration: 'enabled', @@ -54,7 +55,13 @@ if (process.env.NODE_ENV === 'production') { ], bootstrap : [ AppComponent - ] + ], + providers: [ + { + provide: APP_BASE_HREF, + useValue: getAppBaseHref() + } + ], }) export class AppModule { diff --git a/webapp/frontend/src/app/app.routing.ts b/webapp/frontend/src/app/app.routing.ts index f7ad8bcb..2be8dd26 100644 --- a/webapp/frontend/src/app/app.routing.ts +++ b/webapp/frontend/src/app/app.routing.ts @@ -2,6 +2,17 @@ import { Route } from '@angular/router'; import { LayoutComponent } from 'app/layout/layout.component'; import { EmptyLayoutComponent } from 'app/layout/layouts/empty/empty.component'; +// @formatter:off +export function getAppBaseHref(): string { + return getBasePath() + '/web'; +} + +// @formatter:off +// tslint:disable:max-line-length +export function getBasePath(): string { + return window.location.pathname.split('/web').slice(0, 1)[0]; +} + // @formatter:off // tslint:disable:max-line-length export const appRoutes: Route[] = [ diff --git a/webapp/frontend/src/app/layout/common/search/search.component.ts b/webapp/frontend/src/app/layout/common/search/search.component.ts index 56a824ff..3c48651d 100644 --- a/webapp/frontend/src/app/layout/common/search/search.component.ts +++ b/webapp/frontend/src/app/layout/common/search/search.component.ts @@ -5,6 +5,7 @@ import { MatFormField } from '@angular/material/form-field'; import { Subject } from 'rxjs'; import { debounceTime, filter, map, takeUntil } from 'rxjs/operators'; import { TreoAnimations } from '@treo/animations/public-api'; +import { getBasePath } from 'app/app.routing'; @Component({ selector : 'search', @@ -199,7 +200,7 @@ export class SearchComponent implements OnInit, OnDestroy }) ) .subscribe((value) => { - this._httpClient.post('api/common/search', {query: value}) + this._httpClient.post(getBasePath() + '/api/common/search', {query: value}) .subscribe((response: any) => { this.results = response.results; }); diff --git a/webapp/frontend/src/app/modules/dashboard/dashboard.service.ts b/webapp/frontend/src/app/modules/dashboard/dashboard.service.ts index bd812040..f73704c5 100644 --- a/webapp/frontend/src/app/modules/dashboard/dashboard.service.ts +++ b/webapp/frontend/src/app/modules/dashboard/dashboard.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; +import { getBasePath } from 'app/app.routing'; @Injectable({ providedIn: 'root' @@ -45,7 +46,7 @@ export class DashboardService */ getData(): Observable { - return this._httpClient.get('/api/summary').pipe( + return this._httpClient.get(getBasePath() + '/api/summary').pipe( tap((response: any) => { this._data.next(response); }) diff --git a/webapp/frontend/src/app/modules/detail/detail.service.ts b/webapp/frontend/src/app/modules/detail/detail.service.ts index 12e0a590..57475716 100644 --- a/webapp/frontend/src/app/modules/detail/detail.service.ts +++ b/webapp/frontend/src/app/modules/detail/detail.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; +import { getBasePath } from 'app/app.routing'; @Injectable({ providedIn: 'root' @@ -45,7 +46,7 @@ export class DetailService */ getData(wwn): Observable { - return this._httpClient.get(`/api/device/${wwn}/details`).pipe( + return this._httpClient.get(getBasePath() + `/api/device/${wwn}/details`).pipe( tap((response: any) => { this._data.next(response); }) diff --git a/webapp/frontend/src/browserconfig.xml b/webapp/frontend/src/browserconfig.xml index c5541482..ddcadc87 100644 --- a/webapp/frontend/src/browserconfig.xml +++ b/webapp/frontend/src/browserconfig.xml @@ -1,2 +1,2 @@ -#ffffff \ No newline at end of file +#ffffff diff --git a/webapp/frontend/src/index.html b/webapp/frontend/src/index.html index 1578a307..56dca499 100644 --- a/webapp/frontend/src/index.html +++ b/webapp/frontend/src/index.html @@ -3,7 +3,6 @@ scrutiny - @@ -22,7 +21,7 @@ - + diff --git a/webapp/frontend/src/manifest.json b/webapp/frontend/src/manifest.json index 013d4a6a..a6ee00cc 100644 --- a/webapp/frontend/src/manifest.json +++ b/webapp/frontend/src/manifest.json @@ -2,40 +2,40 @@ "name": "App", "icons": [ { - "src": "\/android-icon-36x36.png", + "src": ".\/android-icon-36x36.png", "sizes": "36x36", "type": "image\/png", "density": "0.75" }, { - "src": "\/android-icon-48x48.png", + "src": ".\/android-icon-48x48.png", "sizes": "48x48", "type": "image\/png", "density": "1.0" }, { - "src": "\/android-icon-72x72.png", + "src": ".\/android-icon-72x72.png", "sizes": "72x72", "type": "image\/png", "density": "1.5" }, { - "src": "\/android-icon-96x96.png", + "src": ".\/android-icon-96x96.png", "sizes": "96x96", "type": "image\/png", "density": "2.0" }, { - "src": "\/android-icon-144x144.png", + "src": ".\/android-icon-144x144.png", "sizes": "144x144", "type": "image\/png", "density": "3.0" }, { - "src": "\/android-icon-192x192.png", + "src": ".\/android-icon-192x192.png", "sizes": "192x192", "type": "image\/png", "density": "4.0" } ] -} \ No newline at end of file +} From 9fc11b71403443369040294c9cf1ce92a3565df0 Mon Sep 17 00:00:00 2001 From: Andrea Spacca Date: Fri, 9 Oct 2020 23:27:26 +0200 Subject: [PATCH 03/10] BASEPATH --- webapp/backend/pkg/web/server_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/webapp/backend/pkg/web/server_test.go b/webapp/backend/pkg/web/server_test.go index 1b2d87f1..97cf6e33 100644 --- a/webapp/backend/pkg/web/server_test.go +++ b/webapp/backend/pkg/web/server_test.go @@ -26,6 +26,7 @@ func TestHealthRoute(t *testing.T) { fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes() fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes() + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() ae := web.AppEngine{ Config: fakeConfig, @@ -52,6 +53,7 @@ func TestRegisterDevicesRoute(t *testing.T) { fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes() fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes() + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() ae := web.AppEngine{ Config: fakeConfig, } @@ -77,6 +79,7 @@ func TestUploadDeviceMetricsRoute(t *testing.T) { fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() ae := web.AppEngine{ Config: fakeConfig, } @@ -112,6 +115,7 @@ func TestPopulateMultiple(t *testing.T) { fakeConfig.EXPECT().GetStringSlice("notify.urls").Return([]string{}).AnyTimes() fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() ae := web.AppEngine{ Config: fakeConfig, } @@ -198,6 +202,7 @@ func TestSendTestNotificationRoute_WebhookFailure(t *testing.T) { fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"https://unroutable.domain.example.asdfghj"}) ae := web.AppEngine{ Config: fakeConfig, @@ -222,6 +227,7 @@ func TestSendTestNotificationRoute_ScriptFailure(t *testing.T) { fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"script:///missing/path/on/disk"}) ae := web.AppEngine{ Config: fakeConfig, @@ -270,6 +276,7 @@ func TestSendTestNotificationRoute_ShoutrrrFailure(t *testing.T) { fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"discord://invalidtoken@channel"}) ae := web.AppEngine{ Config: fakeConfig, @@ -294,6 +301,7 @@ func TestGetDevicesSummaryRoute_Nvme(t *testing.T) { fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() ae := web.AppEngine{ Config: fakeConfig, } From 48bc7cedf46b57ac6b8ec08f54b7834c1146bead Mon Sep 17 00:00:00 2001 From: Andrea Spacca Date: Fri, 9 Oct 2020 23:51:11 +0200 Subject: [PATCH 04/10] test cases --- webapp/backend/pkg/web/server_test.go | 533 ++++++++++++++------------ 1 file changed, 296 insertions(+), 237 deletions(-) diff --git a/webapp/backend/pkg/web/server_test.go b/webapp/backend/pkg/web/server_test.go index 97cf6e33..a58f455b 100644 --- a/webapp/backend/pkg/web/server_test.go +++ b/webapp/backend/pkg/web/server_test.go @@ -2,6 +2,7 @@ package web_test import ( "encoding/json" + "fmt" mock_config "github.com/analogj/scrutiny/webapp/backend/pkg/config/mock" dbModels "github.com/analogj/scrutiny/webapp/backend/pkg/models/db" "github.com/analogj/scrutiny/webapp/backend/pkg/web" @@ -17,155 +18,189 @@ import ( "testing" ) -func TestHealthRoute(t *testing.T) { - //setup - parentPath, _ := ioutil.TempDir("", "") - defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - fakeConfig := mock_config.NewMockInterface(mockCtrl) - fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes() - fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes() - fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() - - ae := web.AppEngine{ - Config: fakeConfig, - } - - router := ae.Setup(logrus.New()) +const ( + emptyBasePath = "" + notEmptyBasePath = "/basepath" +) - //test - w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/api/health", nil) - router.ServeHTTP(w, req) +var basePathTestCases = []string{ + emptyBasePath, + notEmptyBasePath, +} - //assert - require.Equal(t, 200, w.Code) - require.Equal(t, "{\"success\":true}", w.Body.String()) +func TestHealthRoute(t *testing.T) { + for _, basePath := range basePathTestCases { + t.Run(fmt.Sprintf(`with basePath "%s"`, basePath), func (tt *testing.T) { + //setup + parentPath, _ := ioutil.TempDir("", "") + mockCtrl := gomock.NewController(tt) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes() + fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes() + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return(basePath).AnyTimes() + + ae := web.AppEngine{ + Config: fakeConfig, + } + + router := ae.Setup(logrus.New()) + + //test + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", basePath + "/api/health", nil) + router.ServeHTTP(w, req) + + //assert + require.Equal(tt, 200, w.Code) + require.Equal(tt, "{\"success\":true}", w.Body.String()) + + // tear down + mockCtrl.Finish() + os.RemoveAll(parentPath) + }) + } } func TestRegisterDevicesRoute(t *testing.T) { - //setup - parentPath, _ := ioutil.TempDir("", "") - defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - fakeConfig := mock_config.NewMockInterface(mockCtrl) - fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes() - fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes() - fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() - ae := web.AppEngine{ - Config: fakeConfig, + for _, basePath := range basePathTestCases { + t.Run(fmt.Sprintf(`with basePath "%s"`, basePath), func (tt *testing.T) { + //setup + parentPath, _ := ioutil.TempDir("", "") + mockCtrl := gomock.NewController(tt) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes() + fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes() + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return(basePath).AnyTimes() + ae := web.AppEngine{ + Config: fakeConfig, + } + router := ae.Setup(logrus.New()) + file, err := os.Open("testdata/register-devices-req.json") + require.NoError(tt, err) + + //test + w := httptest.NewRecorder() + req, _ := http.NewRequest("POST", basePath + "/api/devices/register", file) + router.ServeHTTP(w, req) + + //assert + require.Equal(tt, 200, w.Code) + + // tear down + mockCtrl.Finish() + os.RemoveAll(parentPath) + }) } - router := ae.Setup(logrus.New()) - file, err := os.Open("testdata/register-devices-req.json") - require.NoError(t, err) - - //test - w := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/devices/register", file) - router.ServeHTTP(w, req) - - //assert - require.Equal(t, 200, w.Code) } func TestUploadDeviceMetricsRoute(t *testing.T) { - //setup - parentPath, _ := ioutil.TempDir("", "") - defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - fakeConfig := mock_config.NewMockInterface(mockCtrl) - fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) - fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) - fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() - ae := web.AppEngine{ - Config: fakeConfig, + for _, basePath := range basePathTestCases { + t.Run(fmt.Sprintf(`with basePath "%s"`, basePath), func(tt *testing.T) { + //setup + parentPath, _ := ioutil.TempDir("", "") + mockCtrl := gomock.NewController(tt) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) + fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return(basePath).AnyTimes() + ae := web.AppEngine{ + Config: fakeConfig, + } + router := ae.Setup(logrus.New()) + devicesfile, err := os.Open("testdata/register-devices-single-req.json") + require.NoError(tt, err) + + metricsfile, err := os.Open("testdata/upload-device-metrics-req.json") + require.NoError(tt, err) + + //test + wr := httptest.NewRecorder() + req, _ := http.NewRequest("POST", basePath + "/api/devices/register", devicesfile) + router.ServeHTTP(wr, req) + require.Equal(tt, 200, wr.Code) + + mr := httptest.NewRecorder() + req, _ = http.NewRequest("POST", basePath + "/api/device/0x5000cca264eb01d7/smart", metricsfile) + router.ServeHTTP(mr, req) + require.Equal(tt, 200, mr.Code) + + //assert + + // tear down + mockCtrl.Finish() + os.RemoveAll(parentPath) + }) } - router := ae.Setup(logrus.New()) - devicesfile, err := os.Open("testdata/register-devices-single-req.json") - require.NoError(t, err) - - metricsfile, err := os.Open("testdata/upload-device-metrics-req.json") - require.NoError(t, err) - - //test - wr := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/devices/register", devicesfile) - router.ServeHTTP(wr, req) - require.Equal(t, 200, wr.Code) - - mr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5000cca264eb01d7/smart", metricsfile) - router.ServeHTTP(mr, req) - require.Equal(t, 200, mr.Code) - - //assert } func TestPopulateMultiple(t *testing.T) { - //setup - parentPath, _ := ioutil.TempDir("", "") - defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - fakeConfig := mock_config.NewMockInterface(mockCtrl) - //fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return("testdata/scrutiny_test.db") - fakeConfig.EXPECT().GetStringSlice("notify.urls").Return([]string{}).AnyTimes() - fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) - fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) - fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() - ae := web.AppEngine{ - Config: fakeConfig, + for _, basePath := range basePathTestCases { + t.Run(fmt.Sprintf(`with basePath "%s"`, basePath), func(tt *testing.T) { + //setup + parentPath, _ := ioutil.TempDir("", "") + mockCtrl := gomock.NewController(tt) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + //fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return("testdata/scrutiny_test.db") + fakeConfig.EXPECT().GetStringSlice("notify.urls").Return([]string{}).AnyTimes() + fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) + fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return(basePath).AnyTimes() + ae := web.AppEngine{ + Config: fakeConfig, + } + router := ae.Setup(logrus.New()) + devicesfile, err := os.Open("testdata/register-devices-req.json") + require.NoError(tt, err) + + metricsfile, err := os.Open("../models/testdata/smart-ata.json") + require.NoError(tt, err) + failfile, err := os.Open("../models/testdata/smart-fail2.json") + require.NoError(tt, err) + nvmefile, err := os.Open("../models/testdata/smart-nvme.json") + require.NoError(tt, err) + scsifile, err := os.Open("../models/testdata/smart-scsi.json") + require.NoError(tt, err) + scsi2file, err := os.Open("../models/testdata/smart-scsi2.json") + require.NoError(tt, err) + + //test + wr := httptest.NewRecorder() + req, _ := http.NewRequest("POST", basePath + "/api/devices/register", devicesfile) + router.ServeHTTP(wr, req) + require.Equal(tt, 200, wr.Code) + + mr := httptest.NewRecorder() + req, _ = http.NewRequest("POST", basePath + "/api/device/0x5000cca264eb01d7/smart", metricsfile) + router.ServeHTTP(mr, req) + require.Equal(tt, 200, mr.Code) + + fr := httptest.NewRecorder() + req, _ = http.NewRequest("POST", basePath + "/api/device/0x5000cca264ec3183/smart", failfile) + router.ServeHTTP(fr, req) + require.Equal(tt, 200, fr.Code) + + nr := httptest.NewRecorder() + req, _ = http.NewRequest("POST", basePath + "/api/device/0x5002538e40a22954/smart", nvmefile) + router.ServeHTTP(nr, req) + require.Equal(tt, 200, nr.Code) + + sr := httptest.NewRecorder() + req, _ = http.NewRequest("POST", basePath + "/api/device/0x5000cca252c859cc/smart", scsifile) + router.ServeHTTP(sr, req) + require.Equal(tt, 200, sr.Code) + + s2r := httptest.NewRecorder() + req, _ = http.NewRequest("POST", basePath + "/api/device/0x5000cca264ebc248/smart", scsi2file) + router.ServeHTTP(s2r, req) + require.Equal(tt, 200, s2r.Code) + + //assert + + // tear down + mockCtrl.Finish() + os.RemoveAll(parentPath) + }) } - router := ae.Setup(logrus.New()) - devicesfile, err := os.Open("testdata/register-devices-req.json") - require.NoError(t, err) - - metricsfile, err := os.Open("../models/testdata/smart-ata.json") - require.NoError(t, err) - failfile, err := os.Open("../models/testdata/smart-fail2.json") - require.NoError(t, err) - nvmefile, err := os.Open("../models/testdata/smart-nvme.json") - require.NoError(t, err) - scsifile, err := os.Open("../models/testdata/smart-scsi.json") - require.NoError(t, err) - scsi2file, err := os.Open("../models/testdata/smart-scsi2.json") - require.NoError(t, err) - - //test - wr := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/devices/register", devicesfile) - router.ServeHTTP(wr, req) - require.Equal(t, 200, wr.Code) - - mr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5000cca264eb01d7/smart", metricsfile) - router.ServeHTTP(mr, req) - require.Equal(t, 200, mr.Code) - - fr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5000cca264ec3183/smart", failfile) - router.ServeHTTP(fr, req) - require.Equal(t, 200, fr.Code) - - nr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5002538e40a22954/smart", nvmefile) - router.ServeHTTP(nr, req) - require.Equal(t, 200, nr.Code) - - sr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5000cca252c859cc/smart", scsifile) - router.ServeHTTP(sr, req) - require.Equal(t, 200, sr.Code) - - s2r := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5000cca264ebc248/smart", scsi2file) - router.ServeHTTP(s2r, req) - require.Equal(t, 200, s2r.Code) - - //assert } //TODO: this test should use a recorded request/response playback. @@ -194,53 +229,65 @@ func TestPopulateMultiple(t *testing.T) { //} func TestSendTestNotificationRoute_WebhookFailure(t *testing.T) { - //setup - parentPath, _ := ioutil.TempDir("", "") - defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - fakeConfig := mock_config.NewMockInterface(mockCtrl) - fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) - fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) - fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() - fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"https://unroutable.domain.example.asdfghj"}) - ae := web.AppEngine{ - Config: fakeConfig, + for _, basePath := range basePathTestCases { + t.Run(fmt.Sprintf(`with basePath "%s"`, basePath), func(tt *testing.T) { + //setup + parentPath, _ := ioutil.TempDir("", "") + mockCtrl := gomock.NewController(tt) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) + fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return(basePath).AnyTimes() + fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"https://unroutable.domain.example.asdfghj"}) + ae := web.AppEngine{ + Config: fakeConfig, + } + router := ae.Setup(logrus.New()) + + //test + wr := httptest.NewRecorder() + req, _ := http.NewRequest("POST", basePath + "/api/health/notify", strings.NewReader("{}")) + router.ServeHTTP(wr, req) + + //assert + require.Equal(tt, 500, wr.Code) + + // tear down + mockCtrl.Finish() + os.RemoveAll(parentPath) + }) } - router := ae.Setup(logrus.New()) - - //test - wr := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/health/notify", strings.NewReader("{}")) - router.ServeHTTP(wr, req) - - //assert - require.Equal(t, 500, wr.Code) } func TestSendTestNotificationRoute_ScriptFailure(t *testing.T) { - //setup - parentPath, _ := ioutil.TempDir("", "") - defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - fakeConfig := mock_config.NewMockInterface(mockCtrl) - fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) - fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) - fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() - fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"script:///missing/path/on/disk"}) - ae := web.AppEngine{ - Config: fakeConfig, + for _, basePath := range basePathTestCases { + t.Run(fmt.Sprintf(`with basePath "%s"`, basePath), func(tt *testing.T) { + //setup + parentPath, _ := ioutil.TempDir("", "") + mockCtrl := gomock.NewController(tt) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) + fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return(basePath).AnyTimes() + fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"script:///missing/path/on/disk"}) + ae := web.AppEngine{ + Config: fakeConfig, + } + router := ae.Setup(logrus.New()) + + //test + wr := httptest.NewRecorder() + req, _ := http.NewRequest("POST", basePath + "/api/health/notify", strings.NewReader("{}")) + router.ServeHTTP(wr, req) + + //assert + require.Equal(tt, 500, wr.Code) + + // tear down + mockCtrl.Finish() + os.RemoveAll(parentPath) + }) } - router := ae.Setup(logrus.New()) - - //test - wr := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/health/notify", strings.NewReader("{}")) - router.ServeHTTP(wr, req) - - //assert - require.Equal(t, 500, wr.Code) } func TestSendTestNotificationRoute_ScriptSuccess(t *testing.T) { @@ -268,69 +315,81 @@ func TestSendTestNotificationRoute_ScriptSuccess(t *testing.T) { } func TestSendTestNotificationRoute_ShoutrrrFailure(t *testing.T) { - //setup - parentPath, _ := ioutil.TempDir("", "") - defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - fakeConfig := mock_config.NewMockInterface(mockCtrl) - fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) - fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) - fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() - fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"discord://invalidtoken@channel"}) - ae := web.AppEngine{ - Config: fakeConfig, + for _, basePath := range basePathTestCases { + t.Run(fmt.Sprintf(`with basePath "%s"`, basePath), func(tt *testing.T) { + //setup + parentPath, _ := ioutil.TempDir("", "") + mockCtrl := gomock.NewController(tt) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) + fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return(basePath).AnyTimes() + fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"discord://invalidtoken@channel"}) + ae := web.AppEngine{ + Config: fakeConfig, + } + router := ae.Setup(logrus.New()) + + //test + wr := httptest.NewRecorder() + req, _ := http.NewRequest("POST", basePath + "/api/health/notify", strings.NewReader("{}")) + router.ServeHTTP(wr, req) + + //assert + require.Equal(tt, 500, wr.Code) + + // tear down + mockCtrl.Finish() + os.RemoveAll(parentPath) + }) } - router := ae.Setup(logrus.New()) - - //test - wr := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/health/notify", strings.NewReader("{}")) - router.ServeHTTP(wr, req) - - //assert - require.Equal(t, 500, wr.Code) } func TestGetDevicesSummaryRoute_Nvme(t *testing.T) { - //setup - parentPath, _ := ioutil.TempDir("", "") - defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - fakeConfig := mock_config.NewMockInterface(mockCtrl) - fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) - fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) - fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return("").AnyTimes() - ae := web.AppEngine{ - Config: fakeConfig, + for _, basePath := range basePathTestCases { + t.Run(fmt.Sprintf(`with basePath "%s"`, basePath), func(tt *testing.T) { + //setup + parentPath, _ := ioutil.TempDir("", "") + mockCtrl := gomock.NewController(tt) + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) + fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return(basePath).AnyTimes() + ae := web.AppEngine{ + Config: fakeConfig, + } + router := ae.Setup(logrus.New()) + devicesfile, err := os.Open("testdata/register-devices-req-2.json") + require.NoError(tt, err) + + metricsfile, err := os.Open("../models/testdata/smart-nvme2.json") + require.NoError(tt, err) + + //test + wr := httptest.NewRecorder() + req, _ := http.NewRequest("POST", basePath + "/api/devices/register", devicesfile) + router.ServeHTTP(wr, req) + require.Equal(tt, 200, wr.Code) + + mr := httptest.NewRecorder() + req, _ = http.NewRequest("POST", basePath + "/api/device/a4c8e8ed-11a0-4c97-9bba-306440f1b944/smart", metricsfile) + router.ServeHTTP(mr, req) + require.Equal(tt, 200, mr.Code) + + sr := httptest.NewRecorder() + req, _ = http.NewRequest("GET", basePath + "/api/summary", nil) + router.ServeHTTP(sr, req) + require.Equal(t, 200, sr.Code) + var device dbModels.DeviceWrapper + json.Unmarshal(sr.Body.Bytes(), &device) + + //assert + require.Equal(tt, "a4c8e8ed-11a0-4c97-9bba-306440f1b944", device.Data[0].WWN) + require.Equal(tt, "passed", device.Data[0].SmartResults[0].SmartStatus) + + // tear down + mockCtrl.Finish() + os.RemoveAll(parentPath) + }) } - router := ae.Setup(logrus.New()) - devicesfile, err := os.Open("testdata/register-devices-req-2.json") - require.NoError(t, err) - - metricsfile, err := os.Open("../models/testdata/smart-nvme2.json") - require.NoError(t, err) - - //test - wr := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/devices/register", devicesfile) - router.ServeHTTP(wr, req) - require.Equal(t, 200, wr.Code) - - mr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/a4c8e8ed-11a0-4c97-9bba-306440f1b944/smart", metricsfile) - router.ServeHTTP(mr, req) - require.Equal(t, 200, mr.Code) - - sr := httptest.NewRecorder() - req, _ = http.NewRequest("GET", "/api/summary", nil) - router.ServeHTTP(sr, req) - require.Equal(t, 200, sr.Code) - var device dbModels.DeviceWrapper - json.Unmarshal(sr.Body.Bytes(), &device) - - //assert - require.Equal(t, "a4c8e8ed-11a0-4c97-9bba-306440f1b944", device.Data[0].WWN) - require.Equal(t, "passed", device.Data[0].SmartResults[0].SmartStatus) } From 54baeb4c4e11e9290e3716ccc40b4ff89795ad33 Mon Sep 17 00:00:00 2001 From: Andrea Spacca Date: Sat, 10 Oct 2020 10:59:27 +0200 Subject: [PATCH 05/10] trigger check From 234a8f9b019c3f9edef33453c0c79c2b5ae80f81 Mon Sep 17 00:00:00 2001 From: Andrea Spacca Date: Sat, 10 Oct 2020 19:58:54 +0200 Subject: [PATCH 06/10] cr fixes --- example.scrutiny.yaml | 3 +++ webapp/backend/pkg/config/config.go | 1 + 2 files changed, 4 insertions(+) diff --git a/example.scrutiny.yaml b/example.scrutiny.yaml index 9744f19b..292eabcb 100644 --- a/example.scrutiny.yaml +++ b/example.scrutiny.yaml @@ -26,6 +26,9 @@ web: src: frontend: path: /scrutiny/web + # leave empty unless behind a path prefixed proxy + backend: + basepath: # if behind a path prefixed proxy set to path value log: diff --git a/webapp/backend/pkg/config/config.go b/webapp/backend/pkg/config/config.go index 6349b55b..2116e374 100644 --- a/webapp/backend/pkg/config/config.go +++ b/webapp/backend/pkg/config/config.go @@ -30,6 +30,7 @@ func (c *configuration) Init() error { c.SetDefault("web.listen.port", "8080") c.SetDefault("web.listen.host", "0.0.0.0") c.SetDefault("web.src.frontend.path", "/scrutiny/web") + c.SetDefault("web.src.backend.basepath", "") c.SetDefault("web.database.location", "/scrutiny/config/scrutiny.db") c.SetDefault("log.level", "INFO") From 8ac3ab79a453ad71e457c7b3ceedbb772e0824c3 Mon Sep 17 00:00:00 2001 From: Andrea Spacca Date: Sat, 10 Oct 2020 19:59:12 +0200 Subject: [PATCH 07/10] cr fixes --- example.scrutiny.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example.scrutiny.yaml b/example.scrutiny.yaml index 292eabcb..3d20e9b4 100644 --- a/example.scrutiny.yaml +++ b/example.scrutiny.yaml @@ -28,7 +28,7 @@ web: path: /scrutiny/web # leave empty unless behind a path prefixed proxy backend: - basepath: # if behind a path prefixed proxy set to path value + basepath: # if behind a path prefixed proxy set to path value with leading / log: From 2db646563901b96437d46574e3c7c1d618930a81 Mon Sep 17 00:00:00 2001 From: Andrea Spacca Date: Sat, 17 Oct 2020 10:49:57 +0200 Subject: [PATCH 08/10] BASEPATH --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 294bc044..057d8e2c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,12 +25,12 @@ If you're working on the frontend and can use mocked data rather than a real bac ``` cd webapp/frontend npm install -ng serve +ng serve --deploy-url="/web/" --base-href="/web/" ``` However, if you need to also run the backend, and use real data, you'll need to run the following command: ``` -cd webapp/frontend && ng build --watch --output-path=../../dist --deploy-url="/web/" --base-href="/web/" --prod +cd webapp/frontend && ng build --watch --output-path=../../dist --prod ``` > Note: if you do not add `--prod` flag, app will display mocked data for api calls. From a7c8c75a49433ad2e06749c9e9dedec27459016b Mon Sep 17 00:00:00 2001 From: Andrea Spacca Date: Wed, 11 Nov 2020 09:18:03 +0100 Subject: [PATCH 09/10] fix new test --- webapp/backend/pkg/web/server_test.go | 44 +++++++++++++++------------ 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/webapp/backend/pkg/web/server_test.go b/webapp/backend/pkg/web/server_test.go index a58f455b..d7b90867 100644 --- a/webapp/backend/pkg/web/server_test.go +++ b/webapp/backend/pkg/web/server_test.go @@ -291,27 +291,33 @@ func TestSendTestNotificationRoute_ScriptFailure(t *testing.T) { } func TestSendTestNotificationRoute_ScriptSuccess(t *testing.T) { - //setup - parentPath, _ := ioutil.TempDir("", "") - defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - fakeConfig := mock_config.NewMockInterface(mockCtrl) - fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) - fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) - fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"script:///usr/bin/env"}) - ae := web.AppEngine{ - Config: fakeConfig, - } - router := ae.Setup(logrus.New()) + for _, basePath := range basePathTestCases { + t.Run(fmt.Sprintf(`with basePath "%s"`, basePath), func(tt *testing.T) { + //setup + //setup + parentPath, _ := ioutil.TempDir("", "") + defer os.RemoveAll(parentPath) + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + fakeConfig := mock_config.NewMockInterface(mockCtrl) + fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) + fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return(basePath).AnyTimes() + fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"script:///usr/bin/env"}) + ae := web.AppEngine{ + Config: fakeConfig, + } + router := ae.Setup(logrus.New()) - //test - wr := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/health/notify", strings.NewReader("{}")) - router.ServeHTTP(wr, req) + //test + wr := httptest.NewRecorder() + req, _ := http.NewRequest("POST", basePath + "/api/health/notify", strings.NewReader("{}")) + router.ServeHTTP(wr, req) - //assert - require.Equal(t, 200, wr.Code) + //assert + require.Equal(t, 200, wr.Code) + }) + } } func TestSendTestNotificationRoute_ShoutrrrFailure(t *testing.T) { From 381a6799cc9f26bedc005bd65bf94d25c0fff9d7 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 9 May 2022 14:50:35 -0700 Subject: [PATCH 10/10] updates for v0.4.0 release. Slight refactor/organization. --- .../collector-metrics/collector-metrics.go | 6 +- collector/pkg/collector/metrics.go | 5 +- collector/pkg/collector/metrics_test.go | 38 +++ docker/Dockerfile.web | 2 +- example.scrutiny.yaml | 11 +- go.mod | 1 + webapp/backend/pkg/config/config.go | 2 +- webapp/backend/pkg/web/middleware/logger.go | 2 +- webapp/backend/pkg/web/server.go | 17 +- webapp/backend/pkg/web/server_test.go | 225 +++++++++++------- 10 files changed, 204 insertions(+), 105 deletions(-) create mode 100644 collector/pkg/collector/metrics_test.go diff --git a/collector/cmd/collector-metrics/collector-metrics.go b/collector/cmd/collector-metrics/collector-metrics.go index d06be83a..14782e00 100644 --- a/collector/cmd/collector-metrics/collector-metrics.go +++ b/collector/cmd/collector-metrics/collector-metrics.go @@ -10,6 +10,7 @@ import ( "io" "log" "os" + "strings" "time" utils "github.com/analogj/go-util/utils" @@ -113,7 +114,10 @@ OPTIONS: } if c.IsSet("api-endpoint") { - config.Set("api.endpoint", c.String("api-endpoint")) + //if the user is providing an api-endpoint with a basepath (eg. http://localhost:8080/scrutiny), + //we need to ensure the basepath has a trailing slash, otherwise the url.Parse() path concatenation doesnt work. + apiEndpoint := strings.TrimSuffix(c.String("api-endpoint"), "/") + "/" + config.Set("api.endpoint", apiEndpoint) } collectorLogger := logrus.WithFields(logrus.Fields{ diff --git a/collector/pkg/collector/metrics.go b/collector/pkg/collector/metrics.go index 656aaad4..31b6af73 100644 --- a/collector/pkg/collector/metrics.go +++ b/collector/pkg/collector/metrics.go @@ -48,7 +48,7 @@ func (mc *MetricsCollector) Run() error { } apiEndpoint, _ := url.Parse(mc.apiEndpoint.String()) - apiEndpoint.Path = "/api/devices/register" + apiEndpoint, _ = apiEndpoint.Parse("api/devices/register") //this acts like filepath.Join() deviceRespWrapper := new(models.DeviceWrapper) @@ -73,6 +73,7 @@ func (mc *MetricsCollector) Run() error { if !deviceRespWrapper.Success { mc.logger.Errorln("An error occurred while retrieving filtered devices") + mc.logger.Debugln(deviceRespWrapper) return errors.ApiServerCommunicationError("An error occurred while retrieving filtered devices") } else { mc.logger.Debugln(deviceRespWrapper) @@ -146,7 +147,7 @@ func (mc *MetricsCollector) Publish(deviceWWN string, payload []byte) error { mc.logger.Infof("Publishing smartctl results for %s\n", deviceWWN) apiEndpoint, _ := url.Parse(mc.apiEndpoint.String()) - apiEndpoint.Path = fmt.Sprintf("/api/device/%s/smart", strings.ToLower(deviceWWN)) + apiEndpoint, _ = apiEndpoint.Parse(fmt.Sprintf("api/device/%s/smart", strings.ToLower(deviceWWN))) resp, err := httpClient.Post(apiEndpoint.String(), "application/json", bytes.NewBuffer(payload)) if err != nil { diff --git a/collector/pkg/collector/metrics_test.go b/collector/pkg/collector/metrics_test.go new file mode 100644 index 00000000..f2bc4072 --- /dev/null +++ b/collector/pkg/collector/metrics_test.go @@ -0,0 +1,38 @@ +package collector + +import ( + "github.com/stretchr/testify/require" + "net/url" + "testing" +) + +func TestApiEndpointParse(t *testing.T) { + baseURL, _ := url.Parse("http://localhost:8080/") + + url1, _ := baseURL.Parse("d/e") + require.Equal(t, "http://localhost:8080/d/e", url1.String()) + + url2, _ := baseURL.Parse("/d/e") + require.Equal(t, "http://localhost:8080/d/e", url2.String()) +} + +func TestApiEndpointParse_WithBasepathWithoutTrailingSlash(t *testing.T) { + baseURL, _ := url.Parse("http://localhost:8080/scrutiny") + + //This testcase is unexpected and can cause issues. We need to ensure the apiEndpoint always has a trailing slash. + url1, _ := baseURL.Parse("d/e") + require.Equal(t, "http://localhost:8080/d/e", url1.String()) + + url2, _ := baseURL.Parse("/d/e") + require.Equal(t, "http://localhost:8080/d/e", url2.String()) +} + +func TestApiEndpointParse_WithBasepathWithTrailingSlash(t *testing.T) { + baseURL, _ := url.Parse("http://localhost:8080/scrutiny/") + + url1, _ := baseURL.Parse("d/e") + require.Equal(t, "http://localhost:8080/scrutiny/d/e", url1.String()) + + url2, _ := baseURL.Parse("/d/e") + require.Equal(t, "http://localhost:8080/d/e", url2.String()) +} diff --git a/docker/Dockerfile.web b/docker/Dockerfile.web index 8307f01d..f4e928e5 100644 --- a/docker/Dockerfile.web +++ b/docker/Dockerfile.web @@ -20,7 +20,7 @@ COPY webapp/frontend /opt/scrutiny/src RUN npm install -g @angular/cli@9.1.4 && \ mkdir -p /opt/scrutiny/dist && \ npm install && \ - ng build --output-path=/opt/scrutiny/dist --deploy-url="/web/" --base-href="/web/" --prod + ng build --output-path=/opt/scrutiny/dist --prod ######## diff --git a/example.scrutiny.yaml b/example.scrutiny.yaml index 6c6df58e..8e08705f 100644 --- a/example.scrutiny.yaml +++ b/example.scrutiny.yaml @@ -20,15 +20,20 @@ web: listen: port: 8080 host: 0.0.0.0 + + # if you're using a reverse proxy like apache/nginx, you can override this value to serve scrutiny on a subpath. + # eg. http://example.com/scrutiny/* vs http://example.com:8080 + # see docs/TROUBLESHOOTING_REVERSE_PROXY.md + # basepath: `/scrutiny` + # leave empty unless behind a path prefixed proxy + basepath: '' database: # can also set absolute path here location: /opt/scrutiny/config/scrutiny.db src: + # the location on the filesystem where scrutiny javascript + css is located frontend: path: /opt/scrutiny/web - # leave empty unless behind a path prefixed proxy - backend: - basepath: # if behind a path prefixed proxy set to path value with leading / influxdb: host: 0.0.0.0 port: 8086 diff --git a/go.mod b/go.mod index ebc234b1..391441f2 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.13 require ( github.com/analogj/go-util v0.0.0-20190301173314-5295e364eb14 + github.com/citilinkru/libudev v1.0.0 github.com/containrrr/shoutrrr v0.4.4 github.com/fatih/color v1.10.0 github.com/gin-gonic/gin v1.6.3 diff --git a/webapp/backend/pkg/config/config.go b/webapp/backend/pkg/config/config.go index 4d71b4ee..fc5f0d9c 100644 --- a/webapp/backend/pkg/config/config.go +++ b/webapp/backend/pkg/config/config.go @@ -30,8 +30,8 @@ func (c *configuration) Init() error { //set defaults c.SetDefault("web.listen.port", "8080") c.SetDefault("web.listen.host", "0.0.0.0") + c.SetDefault("web.listen.basepath", "") c.SetDefault("web.src.frontend.path", "/opt/scrutiny/web") - c.SetDefault("web.src.backend.basepath", "") c.SetDefault("web.database.location", "/opt/scrutiny/config/scrutiny.db") c.SetDefault("log.level", "INFO") diff --git a/webapp/backend/pkg/web/middleware/logger.go b/webapp/backend/pkg/web/middleware/logger.go index 39804691..dc988bb3 100644 --- a/webapp/backend/pkg/web/middleware/logger.go +++ b/webapp/backend/pkg/web/middleware/logger.go @@ -89,7 +89,7 @@ func LoggerMiddleware(logger logrus.FieldLogger) gin.HandlerFunc { entry.Info(msg) } } - if strings.HasPrefix(path, "/api/") { + if strings.Contains(path, "/api/") { //only debug log request/response from api endpoint. if len(reqBody) > 0 { entry.WithField("bodyType", "request").Debugln(reqBody) // Print request body diff --git a/webapp/backend/pkg/web/server.go b/webapp/backend/pkg/web/server.go index cc73d392..974ffb26 100644 --- a/webapp/backend/pkg/web/server.go +++ b/webapp/backend/pkg/web/server.go @@ -27,8 +27,9 @@ func (ae *AppEngine) Setup(logger logrus.FieldLogger) *gin.Engine { r.Use(middleware.ConfigMiddleware(ae.Config)) r.Use(gin.Recovery()) - basePath := ae.Config.GetString("web.src.backend.basepath") + basePath := ae.Config.GetString("web.listen.basepath") logger.Debugf("basepath: %s", basePath) + base := r.Group(basePath) { api := base.Group("/api") @@ -40,12 +41,12 @@ func (ae *AppEngine) Setup(logger logrus.FieldLogger) *gin.Engine { }) api.POST("/health/notify", handler.SendTestNotification) //check if notifications are configured correctly - api.POST("/devices/register", handler.RegisterDevices) //used by Collector to register new devices and retrieve filtered list - api.GET("/summary", handler.GetDevicesSummary) //used by Dashboard - api.GET("/summary/temp", handler.GetDevicesSummaryTempHistory) //used by Dashboard (Temperature history dropdown) - api.POST("/device/:wwn/smart", handler.UploadDeviceMetrics) //used by Collector to upload data - api.POST("/device/:wwn/selftest", handler.UploadDeviceSelfTests) - api.GET("/device/:wwn/details", handler.GetDeviceDetails) //used by Details + api.POST("/devices/register", handler.RegisterDevices) //used by Collector to register new devices and retrieve filtered list + api.GET("/summary", handler.GetDevicesSummary) //used by Dashboard + api.GET("/summary/temp", handler.GetDevicesSummaryTempHistory) //used by Dashboard (Temperature history dropdown) + api.POST("/device/:wwn/smart", handler.UploadDeviceMetrics) //used by Collector to upload data + api.POST("/device/:wwn/selftest", handler.UploadDeviceSelfTests) + api.GET("/device/:wwn/details", handler.GetDeviceDetails) //used by Details } } @@ -54,7 +55,7 @@ func (ae *AppEngine) Setup(logger logrus.FieldLogger) *gin.Engine { //redirect base url to /web base.GET("/", func(c *gin.Context) { - c.Redirect(http.StatusFound, basePath + "/web") + c.Redirect(http.StatusFound, basePath+"/web") }) //catch-all, serve index page. diff --git a/webapp/backend/pkg/web/server_test.go b/webapp/backend/pkg/web/server_test.go index 4daee5ef..c8400ddd 100644 --- a/webapp/backend/pkg/web/server_test.go +++ b/webapp/backend/pkg/web/server_test.go @@ -11,6 +11,7 @@ import ( "github.com/golang/mock/gomock" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "io" "io/ioutil" "net/http" @@ -61,15 +62,36 @@ func helperReadSmartDataFileFixTimestamp(t *testing.T, smartDataFilepath string) return bytes.NewReader(updatedSmartDataBytes) } -func TestHealthRoute(t *testing.T) { +// Define the suite, and absorb the built-in basic suite +// functionality from testify - including a T() method which +// returns the current testing context +type ServerTestSuite struct { + suite.Suite + Basepath string +} + +func TestServerTestSuite_WithEmptyBasePath(t *testing.T) { + emptyBasePathSuite := new(ServerTestSuite) + emptyBasePathSuite.Basepath = "" + suite.Run(t, emptyBasePathSuite) +} + +func TestServerTestSuite_WithCustomBasePath(t *testing.T) { + emptyBasePathSuite := new(ServerTestSuite) + emptyBasePathSuite.Basepath = "/basepath" + suite.Run(t, emptyBasePathSuite) +} + +func (suite *ServerTestSuite) TestHealthRoute() { //setup parentPath, _ := ioutil.TempDir("", "") defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) + mockCtrl := gomock.NewController(suite.T()) defer mockCtrl.Finish() fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes() fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes() + fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes() fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes() @@ -90,53 +112,26 @@ func TestHealthRoute(t *testing.T) { router := ae.Setup(logrus.New()) -var basePathTestCases = []string{ - emptyBasePath, - notEmptyBasePath, -} + //test + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", suite.Basepath+"/api/health", nil) + router.ServeHTTP(w, req) -func TestHealthRoute(t *testing.T) { - for _, basePath := range basePathTestCases { - t.Run(fmt.Sprintf(`with basePath "%s"`, basePath), func (tt *testing.T) { - //setup - parentPath, _ := ioutil.TempDir("", "") - mockCtrl := gomock.NewController(tt) - fakeConfig := mock_config.NewMockInterface(mockCtrl) - fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes() - fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes() - fakeConfig.EXPECT().GetString("web.src.backend.basepath").Return(basePath).AnyTimes() - - ae := web.AppEngine{ - Config: fakeConfig, - } - - router := ae.Setup(logrus.New()) - - //test - w := httptest.NewRecorder() - req, _ := http.NewRequest("GET", basePath + "/api/health", nil) - router.ServeHTTP(w, req) - - //assert - require.Equal(tt, 200, w.Code) - require.Equal(tt, "{\"success\":true}", w.Body.String()) - - // tear down - mockCtrl.Finish() - os.RemoveAll(parentPath) - }) - } + //assert + require.Equal(suite.T(), 200, w.Code) + require.Equal(suite.T(), "{\"success\":true}", w.Body.String()) } -func TestRegisterDevicesRoute(t *testing.T) { +func (suite *ServerTestSuite) TestRegisterDevicesRoute() { //setup parentPath, _ := ioutil.TempDir("", "") defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) + mockCtrl := gomock.NewController(suite.T()) defer mockCtrl.Finish() fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes() fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes() + fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes() fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes() @@ -153,17 +148,29 @@ func TestRegisterDevicesRoute(t *testing.T) { ae := web.AppEngine{ Config: fakeConfig, } + router := ae.Setup(logrus.New()) + file, err := os.Open("testdata/register-devices-req.json") + require.NoError(suite.T(), err) + + //test + w := httptest.NewRecorder() + req, _ := http.NewRequest("POST", suite.Basepath+"/api/devices/register", file) + router.ServeHTTP(w, req) + + //assert + require.Equal(suite.T(), 200, w.Code) } -func TestUploadDeviceMetricsRoute(t *testing.T) { +func (suite *ServerTestSuite) TestUploadDeviceMetricsRoute() { //setup parentPath, _ := ioutil.TempDir("", "") defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) + mockCtrl := gomock.NewController(suite.T()) defer mockCtrl.Finish() fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes() fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes() @@ -182,35 +189,36 @@ func TestUploadDeviceMetricsRoute(t *testing.T) { } router := ae.Setup(logrus.New()) devicesfile, err := os.Open("testdata/register-devices-single-req.json") - require.NoError(t, err) + require.NoError(suite.T(), err) - metricsfile := helperReadSmartDataFileFixTimestamp(t, "testdata/upload-device-metrics-req.json") + metricsfile := helperReadSmartDataFileFixTimestamp(suite.T(), "testdata/upload-device-metrics-req.json") //test wr := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/devices/register", devicesfile) + req, _ := http.NewRequest("POST", suite.Basepath+"/api/devices/register", devicesfile) router.ServeHTTP(wr, req) - require.Equal(t, 200, wr.Code) + require.Equal(suite.T(), 200, wr.Code) mr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5000cca264eb01d7/smart", metricsfile) + req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5000cca264eb01d7/smart", metricsfile) router.ServeHTTP(mr, req) - require.Equal(t, 200, mr.Code) + require.Equal(suite.T(), 200, mr.Code) //assert } -func TestPopulateMultiple(t *testing.T) { +func (suite *ServerTestSuite) TestPopulateMultiple() { //setup parentPath, _ := ioutil.TempDir("", "") defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) + mockCtrl := gomock.NewController(suite.T()) defer mockCtrl.Finish() fakeConfig := mock_config.NewMockInterface(mockCtrl) //fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return("testdata/scrutiny_test.db") fakeConfig.EXPECT().GetStringSlice("notify.urls").Return([]string{}).AnyTimes() fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes() fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes() @@ -229,44 +237,44 @@ func TestPopulateMultiple(t *testing.T) { } router := ae.Setup(logrus.New()) devicesfile, err := os.Open("testdata/register-devices-req.json") - require.NoError(t, err) + require.NoError(suite.T(), err) - metricsfile := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-ata.json") - failfile := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-fail2.json") - nvmefile := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-nvme.json") - scsifile := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-scsi.json") - scsi2file := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-scsi2.json") + metricsfile := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-ata.json") + failfile := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-fail2.json") + nvmefile := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-nvme.json") + scsifile := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-scsi.json") + scsi2file := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-scsi2.json") //test wr := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/devices/register", devicesfile) + req, _ := http.NewRequest("POST", suite.Basepath+"/api/devices/register", devicesfile) router.ServeHTTP(wr, req) - require.Equal(t, 200, wr.Code) + require.Equal(suite.T(), 200, wr.Code) mr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5000cca264eb01d7/smart", metricsfile) + req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5000cca264eb01d7/smart", metricsfile) router.ServeHTTP(mr, req) - require.Equal(t, 200, mr.Code) + require.Equal(suite.T(), 200, mr.Code) fr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5000cca264ec3183/smart", failfile) + req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5000cca264ec3183/smart", failfile) router.ServeHTTP(fr, req) - require.Equal(t, 200, fr.Code) + require.Equal(suite.T(), 200, fr.Code) nr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5002538e40a22954/smart", nvmefile) + req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5002538e40a22954/smart", nvmefile) router.ServeHTTP(nr, req) - require.Equal(t, 200, nr.Code) + require.Equal(suite.T(), 200, nr.Code) sr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5000cca252c859cc/smart", scsifile) + req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5000cca252c859cc/smart", scsifile) router.ServeHTTP(sr, req) - require.Equal(t, 200, sr.Code) + require.Equal(suite.T(), 200, sr.Code) s2r := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/0x5000cca264ebc248/smart", scsi2file) + req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5000cca264ebc248/smart", scsi2file) router.ServeHTTP(s2r, req) - require.Equal(t, 200, s2r.Code) + require.Equal(suite.T(), 200, s2r.Code) //assert } @@ -296,15 +304,16 @@ func TestPopulateMultiple(t *testing.T) { // require.Equal(t, 200, wr.Code) //} -func TestSendTestNotificationRoute_WebhookFailure(t *testing.T) { +func (suite *ServerTestSuite) TestSendTestNotificationRoute_WebhookFailure() { //setup parentPath, _ := ioutil.TempDir("", "") defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) + mockCtrl := gomock.NewController(suite.T()) defer mockCtrl.Finish() fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes() fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes() @@ -322,17 +331,27 @@ func TestSendTestNotificationRoute_WebhookFailure(t *testing.T) { ae := web.AppEngine{ Config: fakeConfig, } + router := ae.Setup(logrus.New()) + + //test + wr := httptest.NewRecorder() + req, _ := http.NewRequest("POST", suite.Basepath+"/api/health/notify", strings.NewReader("{}")) + router.ServeHTTP(wr, req) + + //assert + require.Equal(suite.T(), 500, wr.Code) } -func TestSendTestNotificationRoute_ScriptFailure(t *testing.T) { +func (suite *ServerTestSuite) TestSendTestNotificationRoute_ScriptFailure() { //setup parentPath, _ := ioutil.TempDir("", "") defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) + mockCtrl := gomock.NewController(suite.T()) defer mockCtrl.Finish() fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes() fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes() @@ -350,17 +369,27 @@ func TestSendTestNotificationRoute_ScriptFailure(t *testing.T) { ae := web.AppEngine{ Config: fakeConfig, } + router := ae.Setup(logrus.New()) + + //test + wr := httptest.NewRecorder() + req, _ := http.NewRequest("POST", suite.Basepath+"/api/health/notify", strings.NewReader("{}")) + router.ServeHTTP(wr, req) + + //assert + require.Equal(suite.T(), 500, wr.Code) } -func TestSendTestNotificationRoute_ScriptSuccess(t *testing.T) { +func (suite *ServerTestSuite) TestSendTestNotificationRoute_ScriptSuccess() { //setup parentPath, _ := ioutil.TempDir("", "") defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) + mockCtrl := gomock.NewController(suite.T()) defer mockCtrl.Finish() fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes() fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes() @@ -378,17 +407,27 @@ func TestSendTestNotificationRoute_ScriptSuccess(t *testing.T) { ae := web.AppEngine{ Config: fakeConfig, } + router := ae.Setup(logrus.New()) + + //test + wr := httptest.NewRecorder() + req, _ := http.NewRequest("POST", suite.Basepath+"/api/health/notify", strings.NewReader("{}")) + router.ServeHTTP(wr, req) + + //assert + require.Equal(suite.T(), 200, wr.Code) } -func TestSendTestNotificationRoute_ShoutrrrFailure(t *testing.T) { +func (suite *ServerTestSuite) TestSendTestNotificationRoute_ShoutrrrFailure() { //setup parentPath, _ := ioutil.TempDir("", "") defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) + mockCtrl := gomock.NewController(suite.T()) defer mockCtrl.Finish() fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes() fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes() @@ -405,17 +444,27 @@ func TestSendTestNotificationRoute_ShoutrrrFailure(t *testing.T) { ae := web.AppEngine{ Config: fakeConfig, } + router := ae.Setup(logrus.New()) + + //test + wr := httptest.NewRecorder() + req, _ := http.NewRequest("POST", suite.Basepath+"/api/health/notify", strings.NewReader("{}")) + router.ServeHTTP(wr, req) + + //assert + require.Equal(suite.T(), 500, wr.Code) } -func TestGetDevicesSummaryRoute_Nvme(t *testing.T) { +func (suite *ServerTestSuite) TestGetDevicesSummaryRoute_Nvme() { //setup parentPath, _ := ioutil.TempDir("", "") defer os.RemoveAll(parentPath) - mockCtrl := gomock.NewController(t) + mockCtrl := gomock.NewController(suite.T()) defer mockCtrl.Finish() fakeConfig := mock_config.NewMockInterface(mockCtrl) fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db")) fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath) + fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes() fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes() fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes() @@ -435,30 +484,30 @@ func TestGetDevicesSummaryRoute_Nvme(t *testing.T) { } router := ae.Setup(logrus.New()) devicesfile, err := os.Open("testdata/register-devices-req-2.json") - require.NoError(t, err) + require.NoError(suite.T(), err) - metricsfile := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-nvme2.json") + metricsfile := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-nvme2.json") //test wr := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/api/devices/register", devicesfile) + req, _ := http.NewRequest("POST", suite.Basepath+"/api/devices/register", devicesfile) router.ServeHTTP(wr, req) - require.Equal(t, 200, wr.Code) + require.Equal(suite.T(), 200, wr.Code) mr := httptest.NewRecorder() - req, _ = http.NewRequest("POST", "/api/device/a4c8e8ed-11a0-4c97-9bba-306440f1b944/smart", metricsfile) + req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/a4c8e8ed-11a0-4c97-9bba-306440f1b944/smart", metricsfile) router.ServeHTTP(mr, req) - require.Equal(t, 200, mr.Code) + require.Equal(suite.T(), 200, mr.Code) sr := httptest.NewRecorder() - req, _ = http.NewRequest("GET", "/api/summary", nil) + req, _ = http.NewRequest("GET", suite.Basepath+"/api/summary", nil) router.ServeHTTP(sr, req) - require.Equal(t, 200, sr.Code) + require.Equal(suite.T(), 200, sr.Code) var deviceSummary models.DeviceSummaryWrapper err = json.Unmarshal(sr.Body.Bytes(), &deviceSummary) - require.NoError(t, err) + require.NoError(suite.T(), err) //assert - require.Equal(t, "a4c8e8ed-11a0-4c97-9bba-306440f1b944", deviceSummary.Data.Summary["a4c8e8ed-11a0-4c97-9bba-306440f1b944"].Device.WWN) - require.Equal(t, pkg.DeviceStatusFailedScrutiny, deviceSummary.Data.Summary["a4c8e8ed-11a0-4c97-9bba-306440f1b944"].Device.DeviceStatus) + require.Equal(suite.T(), "a4c8e8ed-11a0-4c97-9bba-306440f1b944", deviceSummary.Data.Summary["a4c8e8ed-11a0-4c97-9bba-306440f1b944"].Device.WWN) + require.Equal(suite.T(), pkg.DeviceStatusFailedScrutiny, deviceSummary.Data.Summary["a4c8e8ed-11a0-4c97-9bba-306440f1b944"].Device.DeviceStatus) }