mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
This allows people to use unsee with a sub uri setup, which is pretty common usa case, fixes #22
419 lines
9.1 KiB
Go
419 lines
9.1 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cloudflare/unsee/config"
|
|
"github.com/cloudflare/unsee/models"
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
"github.com/gin-gonic/gin"
|
|
cache "github.com/patrickmn/go-cache"
|
|
|
|
"gopkg.in/jarcoal/httpmock.v1"
|
|
)
|
|
|
|
func mockConfig() {
|
|
log.SetLevel(log.FatalLevel)
|
|
os.Setenv("ALERTMANAGER_URI", "http://localhost")
|
|
os.Setenv("COLOR_LABELS_UNIQUE", "alertname")
|
|
config.Config.Read()
|
|
}
|
|
|
|
func ginTestEngine() *gin.Engine {
|
|
gin.SetMode(gin.TestMode)
|
|
r := gin.New()
|
|
r.SetHTMLTemplate(loadTemplates("templates"))
|
|
setupRouter(r)
|
|
return r
|
|
}
|
|
|
|
func TestIndex(t *testing.T) {
|
|
mockConfig()
|
|
r := ginTestEngine()
|
|
req, _ := http.NewRequest("GET", "/", nil)
|
|
resp := httptest.NewRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != http.StatusOK {
|
|
t.Errorf("GET / returned status %d", resp.Code)
|
|
}
|
|
}
|
|
|
|
func TestIndexPrefix(t *testing.T) {
|
|
os.Setenv("WEB_PREFIX", "/prefix")
|
|
defer os.Unsetenv("WEB_PREFIX")
|
|
mockConfig()
|
|
r := ginTestEngine()
|
|
req, _ := http.NewRequest("GET", "/prefix/", nil)
|
|
resp := httptest.NewRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != http.StatusOK {
|
|
t.Errorf("GET /prefix/ returned status %d", resp.Code)
|
|
}
|
|
}
|
|
|
|
func TestHelp(t *testing.T) {
|
|
mockConfig()
|
|
r := ginTestEngine()
|
|
req, _ := http.NewRequest("GET", "/help", nil)
|
|
resp := httptest.NewRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != http.StatusOK {
|
|
t.Errorf("GET /help returned status %d", resp.Code)
|
|
}
|
|
}
|
|
|
|
func TestHelpPrefix(t *testing.T) {
|
|
os.Setenv("WEB_PREFIX", "/prefix")
|
|
defer os.Unsetenv("WEB_PREFIX")
|
|
mockConfig()
|
|
r := ginTestEngine()
|
|
req, _ := http.NewRequest("GET", "/prefix/help", nil)
|
|
resp := httptest.NewRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != http.StatusOK {
|
|
t.Errorf("GET /prefix/help returned status %d", resp.Code)
|
|
}
|
|
req, _ = http.NewRequest("GET", "/help", nil)
|
|
resp = httptest.NewRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != http.StatusNotFound {
|
|
t.Errorf("GET /help returned status %d, expected 404", resp.Code)
|
|
}
|
|
}
|
|
|
|
func mockAlerts() {
|
|
httpmock.Activate()
|
|
defer httpmock.DeactivateAndReset()
|
|
|
|
apiCache = cache.New(cache.NoExpiration, 10*time.Second)
|
|
|
|
silences := `{
|
|
"status": "success",
|
|
"data": {
|
|
"silences": [
|
|
{
|
|
"id": 1,
|
|
"matchers": [
|
|
{
|
|
"name": "alertname",
|
|
"value": "myService",
|
|
"isRegex": false
|
|
}
|
|
],
|
|
"startsAt": "2016-11-08T16:30:21Z",
|
|
"endsAt": "2063-04-06T09:22:30Z",
|
|
"createdAt": "2016-11-08T16:30:21Z",
|
|
"createdBy": "john@localhost",
|
|
"comment": "JIRA-3273"
|
|
}
|
|
],
|
|
"totalSilences": 1100
|
|
}
|
|
}`
|
|
httpmock.RegisterResponder("GET", "http://localhost/api/v1/silences?limit=4294967295", httpmock.NewStringResponder(200, silences))
|
|
|
|
alerts := `{
|
|
"status": "success",
|
|
"data": [
|
|
{
|
|
"labels": {
|
|
"alertname": "myService"
|
|
},
|
|
"blocks": [
|
|
{
|
|
"routeOpts": {
|
|
"receiver": "email",
|
|
"groupBy": [
|
|
"cluster"
|
|
],
|
|
"groupWait": 30000000000,
|
|
"groupInterval": 300000000000,
|
|
"repeatInterval": 10800000000000
|
|
},
|
|
"alerts": [
|
|
{
|
|
"labels": {
|
|
"alertname": "myService",
|
|
"node": "localhost",
|
|
"cluster": "prod"
|
|
},
|
|
"annotations": {
|
|
"dashboard": "https://localhost/dashboard"
|
|
},
|
|
"startsAt": "2016-12-10T18:57:42.308Z",
|
|
"endsAt": "0001-01-01T00:00:00Z",
|
|
"generatorURL": "https://localhost/prometheus",
|
|
"inhibited": false,
|
|
"silenced": 1
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}`
|
|
httpmock.RegisterResponder("GET", "http://localhost/api/v1/alerts/groups", httpmock.NewStringResponder(200, alerts))
|
|
|
|
PullFromAlertmanager()
|
|
}
|
|
|
|
func TestAlerts(t *testing.T) {
|
|
mockConfig()
|
|
mockAlerts()
|
|
r := ginTestEngine()
|
|
req, _ := http.NewRequest("GET", "/alerts.json?q=alertname=myService", nil)
|
|
resp := httptest.NewRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != http.StatusOK {
|
|
t.Errorf("GET /alerts.json returned status %d", resp.Code)
|
|
}
|
|
|
|
ur := models.UnseeAlertsResponse{}
|
|
json.Unmarshal(resp.Body.Bytes(), &ur)
|
|
if len(ur.Filters) != 1 {
|
|
t.Error("No filters in response")
|
|
}
|
|
if len(ur.Colors) != 1 {
|
|
t.Error("No colors in response")
|
|
}
|
|
if len(ur.Silences) != 1 {
|
|
t.Error("No silences in response")
|
|
}
|
|
if len(ur.AlertGroups) != 1 {
|
|
t.Error("No alerts in response")
|
|
}
|
|
if ur.Version == "" {
|
|
t.Error("No version in response")
|
|
}
|
|
if ur.Timestamp == "" {
|
|
t.Error("No timestamp in response")
|
|
}
|
|
if ur.Error != "" {
|
|
t.Errorf("Error in response: %s", ur.Error)
|
|
}
|
|
if ur.Status != "success" {
|
|
t.Errorf("Invalid status in response: %s", ur.Status)
|
|
}
|
|
if len(ur.Counters) != 4 {
|
|
t.Errorf("Invalid number of counters in response (%d): %v", len(ur.Counters), ur.Counters)
|
|
}
|
|
}
|
|
|
|
type acTestCase struct {
|
|
Term string
|
|
Results []string
|
|
}
|
|
|
|
var acTests = []acTestCase{
|
|
acTestCase{
|
|
Term: "a",
|
|
Results: []string{
|
|
"@age<10m",
|
|
"@age<1h",
|
|
"@age>10m",
|
|
"@age>1h",
|
|
"alertname!=myService",
|
|
"alertname=myService",
|
|
},
|
|
},
|
|
acTestCase{
|
|
Term: "alert",
|
|
Results: []string{
|
|
"alertname!=myService",
|
|
"alertname=myService",
|
|
},
|
|
},
|
|
acTestCase{
|
|
Term: "alertname",
|
|
Results: []string{
|
|
"alertname!=myService",
|
|
"alertname=myService",
|
|
},
|
|
},
|
|
acTestCase{
|
|
Term: "aLeRtNaMe",
|
|
Results: []string{
|
|
"alertname!=myService",
|
|
"alertname=myService",
|
|
},
|
|
},
|
|
acTestCase{
|
|
Term: "myservice",
|
|
Results: []string{
|
|
"alertname!=myService",
|
|
"alertname=myService",
|
|
},
|
|
},
|
|
acTestCase{
|
|
Term: "MYservice",
|
|
Results: []string{
|
|
"alertname!=myService",
|
|
"alertname=myService",
|
|
},
|
|
},
|
|
acTestCase{
|
|
Term: "@",
|
|
Results: []string{
|
|
"@age<10m",
|
|
"@age<1h",
|
|
"@age>10m",
|
|
"@age>1h",
|
|
"@limit=10",
|
|
"@limit=50",
|
|
"@silence_author!=john@localhost",
|
|
"@silence_author!~john@localhost",
|
|
"@silence_author=john@localhost",
|
|
"@silence_author=~john@localhost",
|
|
"@silenced=false",
|
|
"@silenced=true",
|
|
},
|
|
},
|
|
acTestCase{
|
|
Term: "nod",
|
|
Results: []string{
|
|
"node!=localhost",
|
|
"node=localhost",
|
|
},
|
|
},
|
|
acTestCase{
|
|
Term: "Nod",
|
|
Results: []string{
|
|
"node!=localhost",
|
|
"node=localhost",
|
|
},
|
|
},
|
|
// duplicated to test reponse caching
|
|
acTestCase{
|
|
Term: "Nod",
|
|
Results: []string{
|
|
"node!=localhost",
|
|
"node=localhost",
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestAutocomplete(t *testing.T) {
|
|
mockConfig()
|
|
mockAlerts()
|
|
r := ginTestEngine()
|
|
|
|
req, _ := http.NewRequest("GET", "/autocomplete.json", nil)
|
|
resp := httptest.NewRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != http.StatusBadRequest {
|
|
t.Errorf("Invalid status code for request without any query: %d", resp.Code)
|
|
}
|
|
|
|
for _, acTest := range acTests {
|
|
url := fmt.Sprintf("/autocomplete.json?term=%s", acTest.Term)
|
|
req, _ := http.NewRequest("GET", url, nil)
|
|
resp := httptest.NewRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
|
|
if resp.Code != http.StatusOK {
|
|
t.Errorf("GET %s returned status %d", url, resp.Code)
|
|
}
|
|
|
|
ur := []string{}
|
|
json.Unmarshal(resp.Body.Bytes(), &ur)
|
|
|
|
if len(ur) != len(acTest.Results) {
|
|
t.Errorf("Invalid number of autocomplete hints for %s, got %d, expected %d", url, len(ur), len(acTest.Results))
|
|
t.Errorf("Results: %s", ur)
|
|
}
|
|
for i := range ur {
|
|
if ur[i] != acTest.Results[i] {
|
|
t.Errorf("Result mismatch, got '%s' when '%s' was expected", ur[i], acTest.Results[i])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
type staticFileTestCase struct {
|
|
path string
|
|
code int
|
|
}
|
|
|
|
var staticFileTests = []staticFileTestCase{
|
|
staticFileTestCase{
|
|
path: "/favicon.ico",
|
|
code: 200,
|
|
},
|
|
staticFileTestCase{
|
|
path: "/static/unsee.js",
|
|
code: 200,
|
|
},
|
|
staticFileTestCase{
|
|
path: "/static/managed/js/assets.txt",
|
|
code: 200,
|
|
},
|
|
staticFileTestCase{
|
|
path: "/xxx",
|
|
code: 404,
|
|
},
|
|
staticFileTestCase{
|
|
path: "/static/abcd",
|
|
code: 404,
|
|
},
|
|
}
|
|
|
|
func TestStaticFiles(t *testing.T) {
|
|
mockConfig()
|
|
mockAlerts()
|
|
r := ginTestEngine()
|
|
for _, staticFileTest := range staticFileTests {
|
|
req, _ := http.NewRequest("GET", staticFileTest.path, nil)
|
|
resp := httptest.NewRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != staticFileTest.code {
|
|
t.Errorf("Invalid status code for GET %s: %d", staticFileTest.path, resp.Code)
|
|
}
|
|
}
|
|
}
|
|
|
|
var staticFilePrefixTests = []staticFileTestCase{
|
|
staticFileTestCase{
|
|
path: "/sub/favicon.ico",
|
|
code: 200,
|
|
},
|
|
staticFileTestCase{
|
|
path: "/sub/static/unsee.js",
|
|
code: 200,
|
|
},
|
|
staticFileTestCase{
|
|
path: "/sub/static/managed/js/assets.txt",
|
|
code: 200,
|
|
},
|
|
staticFileTestCase{
|
|
path: "/sub/xxx",
|
|
code: 404,
|
|
},
|
|
staticFileTestCase{
|
|
path: "/sub/static/abcd",
|
|
code: 404,
|
|
},
|
|
}
|
|
|
|
func TestStaticFilesPrefix(t *testing.T) {
|
|
os.Setenv("WEB_PREFIX", "/sub")
|
|
defer os.Unsetenv("WEB_PREFIX")
|
|
mockConfig()
|
|
mockAlerts()
|
|
r := ginTestEngine()
|
|
for _, staticFileTest := range staticFilePrefixTests {
|
|
req, _ := http.NewRequest("GET", staticFileTest.path, nil)
|
|
resp := httptest.NewRecorder()
|
|
r.ServeHTTP(resp, req)
|
|
if resp.Code != staticFileTest.code {
|
|
t.Errorf("Invalid status code for GET %s: %d", staticFileTest.path, resp.Code)
|
|
}
|
|
}
|
|
}
|