|
|
|
|
@@ -0,0 +1,361 @@
|
|
|
|
|
//go:build testing
|
|
|
|
|
|
|
|
|
|
package alerts_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"testing"
|
|
|
|
|
"testing/synctest"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/henrygd/beszel/internal/alerts"
|
|
|
|
|
"github.com/henrygd/beszel/internal/entities/monitor"
|
|
|
|
|
"github.com/henrygd/beszel/internal/entities/system"
|
|
|
|
|
beszelTests "github.com/henrygd/beszel/internal/tests"
|
|
|
|
|
"github.com/pocketbase/dbx"
|
|
|
|
|
"github.com/pocketbase/pocketbase/apis"
|
|
|
|
|
"github.com/pocketbase/pocketbase/core"
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func alertAPIRouter(t *testing.T, hub *beszelTests.TestHub) http.Handler {
|
|
|
|
|
t.Helper()
|
|
|
|
|
router, err := apis.NewRouter(hub)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NoError(t, hub.OnServe().Trigger(&core.ServeEvent{App: hub, Router: router}))
|
|
|
|
|
mux, err := router.BuildMux()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
return mux
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func alertAPIRequest(t *testing.T, handler http.Handler, token, method, path string, body any, status int) map[string]any {
|
|
|
|
|
t.Helper()
|
|
|
|
|
req := httptest.NewRequest(method, path, jsonReader(body))
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
|
req.Header.Set("Authorization", token)
|
|
|
|
|
res := httptest.NewRecorder()
|
|
|
|
|
handler.ServeHTTP(res, req)
|
|
|
|
|
assert.Equal(t, status, res.Code, "%s %s: %s", method, path, res.Body.String())
|
|
|
|
|
var result map[string]any
|
|
|
|
|
require.NoError(t, json.Unmarshal(res.Body.Bytes(), &result))
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAlertRecordSystemAccess(t *testing.T) {
|
|
|
|
|
for _, shareAll := range []string{"false", "true"} {
|
|
|
|
|
t.Run("share_all="+shareAll, func(t *testing.T) {
|
|
|
|
|
t.Setenv("BESZEL_HUB_SHARE_ALL_SYSTEMS", shareAll)
|
|
|
|
|
hub, err := beszelTests.NewTestHub(t.TempDir())
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer hub.Cleanup()
|
|
|
|
|
hub.StartHub()
|
|
|
|
|
user, err := beszelTests.CreateUser(hub, "subscriber@example.com", "password")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
other, err := beszelTests.CreateUser(hub, "owner@example.com", "password")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
token, err := user.NewAuthToken()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
own, err := beszelTests.CreateSystems(hub, 2, user.Id, "paused")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
foreign, err := beszelTests.CreateSystems(hub, 1, other.Id, "paused")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
handler := alertAPIRouter(t, hub)
|
|
|
|
|
const records = "/api/collections/alerts/records"
|
|
|
|
|
// Collection rules reject inaccessible creates as 400 and hide inaccessible updates as 404.
|
|
|
|
|
readStatus, createStatus, updateStatus := 404, 400, 404
|
|
|
|
|
if shareAll == "true" {
|
|
|
|
|
readStatus, createStatus, updateStatus = 200, 200, 200
|
|
|
|
|
}
|
|
|
|
|
alertAPIRequest(t, handler, token, "GET", "/api/collections/systems/records/"+own[0].Id, nil, 200)
|
|
|
|
|
alertAPIRequest(t, handler, token, "GET", "/api/collections/systems/records/"+foreign[0].Id, nil, readStatus)
|
|
|
|
|
collection, err := hub.FindCollectionByNameOrId("alerts")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
for _, name := range collection.Fields.GetByName("name").(*core.SelectField).Values {
|
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
|
// Scalar and array representations both pass through PocketBase's relation binding.
|
|
|
|
|
for _, relation := range []any{foreign[0].Id, []string{foreign[0].Id}} {
|
|
|
|
|
result := alertAPIRequest(t, handler, token, "POST", records, map[string]any{
|
|
|
|
|
"name": name, "user": user.Id, "system": relation, "value": 50,
|
|
|
|
|
}, createStatus)
|
|
|
|
|
if id, ok := result["id"].(string); ok {
|
|
|
|
|
record, err := hub.FindRecordById("alerts", id)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NoError(t, hub.Delete(record))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result := alertAPIRequest(t, handler, token, "POST", records, map[string]any{
|
|
|
|
|
"name": name, "user": user.Id, "system": own[0].Id, "value": 50,
|
|
|
|
|
}, 200)
|
|
|
|
|
id := result["id"].(string)
|
|
|
|
|
defer func() {
|
|
|
|
|
record, err := hub.FindRecordById("alerts", id)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NoError(t, hub.Delete(record))
|
|
|
|
|
}()
|
|
|
|
|
alertAPIRequest(t, handler, token, "PATCH", records+"/"+id, map[string]any{"value": 51}, 200)
|
|
|
|
|
// The system of an existing alert is immutable through the API, even between accessible systems.
|
|
|
|
|
for _, body := range []map[string]any{
|
|
|
|
|
{"system": foreign[0].Id},
|
|
|
|
|
{"system+": foreign[0].Id},
|
|
|
|
|
{"system": []string{own[1].Id}},
|
|
|
|
|
} {
|
|
|
|
|
alertAPIRequest(t, handler, token, "PATCH", records+"/"+id, body, 404)
|
|
|
|
|
}
|
|
|
|
|
saved, err := hub.FindRecordById("alerts", id)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Equal(t, own[0].Id, saved.GetString("system"))
|
|
|
|
|
// Internal saves can still move an alert, which must remove its previous cache binding.
|
|
|
|
|
saved.Set("system", own[1].Id)
|
|
|
|
|
require.NoError(t, hub.Save(saved))
|
|
|
|
|
cache := hub.GetAlertManager().GetSystemAlertsCache()
|
|
|
|
|
assert.Empty(t, cache.GetSystemAlerts(own[0].Id), "moving an alert must remove its previous cache binding")
|
|
|
|
|
assert.Len(t, cache.GetSystemAlerts(own[1].Id), 1)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
alertAPIRequest(t, handler, token, "POST", records, map[string]any{"name": "CPU", "user": other.Id, "system": own[0].Id}, 400)
|
|
|
|
|
alertAPIRequest(t, handler, token, "POST", records, map[string]any{"name": "CPU", "user": user.Id, "system": "missing00000000"}, 400)
|
|
|
|
|
alertAPIRequest(t, handler, "", "POST", records, map[string]any{"name": "CPU", "user": user.Id, "system": own[0].Id}, 400)
|
|
|
|
|
// An old subscription must still be checked when PATCH omits the relation.
|
|
|
|
|
oldAlert, err := beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
|
|
|
|
"name": "CPU", "user": user.Id, "system": foreign[0].Id, "value": 50,
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
alertAPIRequest(t, handler, token, "PATCH", records+"/"+oldAlert.Id, map[string]any{"value": 51}, updateStatus)
|
|
|
|
|
require.NoError(t, hub.Delete(oldAlert))
|
|
|
|
|
otherAlert, err := beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
|
|
|
|
"name": "CPU", "user": other.Id, "system": foreign[0].Id,
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
alertAPIRequest(t, handler, token, "PATCH", records+"/"+otherAlert.Id, map[string]any{"system": own[0].Id}, 404)
|
|
|
|
|
require.NoError(t, hub.Delete(otherAlert))
|
|
|
|
|
// Alerts cannot be handed to another user.
|
|
|
|
|
ownAlert, err := beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
|
|
|
|
"name": "CPU", "user": user.Id, "system": own[0].Id,
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
alertAPIRequest(t, handler, token, "PATCH", records+"/"+ownAlert.Id, map[string]any{"user": other.Id}, 404)
|
|
|
|
|
ownAlert, err = hub.FindRecordById("alerts", ownAlert.Id)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Equal(t, user.Id, ownAlert.GetString("user"))
|
|
|
|
|
require.NoError(t, hub.Delete(ownAlert))
|
|
|
|
|
// Readonly users retain their own alert preferences; superusers retain their bypass.
|
|
|
|
|
user.Set("role", "readonly")
|
|
|
|
|
require.NoError(t, hub.Save(user))
|
|
|
|
|
result := alertAPIRequest(t, handler, token, "POST", records, map[string]any{"name": "CPU", "user": user.Id, "system": own[0].Id}, 200)
|
|
|
|
|
alertAPIRequest(t, handler, token, "PATCH", records+"/"+result["id"].(string), map[string]any{"name": "Memory"}, 200)
|
|
|
|
|
alertAPIRequest(t, handler, token, "PATCH", records+"/"+result["id"].(string), map[string]any{"name": "NetworkMonitorLoss"}, 400)
|
|
|
|
|
record, err := hub.FindRecordById("alerts", result["id"].(string))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NoError(t, hub.Delete(record))
|
|
|
|
|
superuser, err := beszelTests.CreateSuperuser(hub, "superuser@example.com", "password123")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
superToken, err := superuser.NewAuthToken()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
result = alertAPIRequest(t, handler, superToken, "POST", records, map[string]any{"name": "CPU", "user": other.Id, "system": foreign[0].Id}, 200)
|
|
|
|
|
record, err = hub.FindRecordById("alerts", result["id"].(string))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NoError(t, hub.Delete(record))
|
|
|
|
|
// Bulk requests continue to skip inaccessible systems and accept accessible ones.
|
|
|
|
|
alertAPIRequest(t, handler, token, "POST", "/api/beszel/user-alerts", map[string]any{
|
|
|
|
|
"name": "CPU", "systems": []string{own[0].Id, foreign[0].Id}, "value": 50,
|
|
|
|
|
}, 200)
|
|
|
|
|
count, err := hub.CountRecords("alerts")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
expectedCount := 1
|
|
|
|
|
if shareAll == "true" {
|
|
|
|
|
expectedCount = 2
|
|
|
|
|
}
|
|
|
|
|
assert.EqualValues(t, expectedCount, count)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAlertRecordForeignSystemDelivery(t *testing.T) {
|
|
|
|
|
t.Setenv("BESZEL_HUB_SHARE_ALL_SYSTEMS", "false")
|
|
|
|
|
hub, err := beszelTests.NewTestHub(t.TempDir())
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer hub.Cleanup()
|
|
|
|
|
hub.StartHub()
|
|
|
|
|
user, err := beszelTests.CreateUser(hub, "subscriber@example.com", "password")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
owner, err := beszelTests.CreateUser(hub, "owner@example.com", "password")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
for _, recipient := range []*core.Record{user, owner} {
|
|
|
|
|
_, err := beszelTests.CreateRecord(hub, "user_settings", map[string]any{
|
|
|
|
|
"user": recipient.Id, "settings": map[string]any{"emails": []string{recipient.GetString("email")}, "webhooks": []string{}},
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
foreign, err := beszelTests.CreateSystems(hub, 1, owner.Id, "paused")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
foreign[0].Set("name", "private-system")
|
|
|
|
|
require.NoError(t, hub.Save(foreign[0]))
|
|
|
|
|
handler := alertAPIRouter(t, hub)
|
|
|
|
|
for _, recipient := range []*core.Record{user, owner} {
|
|
|
|
|
token, err := recipient.NewAuthToken()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
status := 200
|
|
|
|
|
if recipient.Id == user.Id {
|
|
|
|
|
status = 400
|
|
|
|
|
}
|
|
|
|
|
alertAPIRequest(t, handler, token, "POST", "/api/collections/alerts/records", map[string]any{
|
|
|
|
|
"name": "CPU", "user": recipient.Id, "system": foreign[0].Id, "min": 1, "value": 50,
|
|
|
|
|
}, status)
|
|
|
|
|
}
|
|
|
|
|
synctest.Test(t, func(t *testing.T) {
|
|
|
|
|
require.NoError(t, hub.GetAlertManager().HandleSystemAlerts(foreign[0], &system.CombinedData{Info: system.Info{Cpu: 91}}))
|
|
|
|
|
synctest.Wait()
|
|
|
|
|
messages := hub.TestMailer.Messages()
|
|
|
|
|
assert.Len(t, messages, 1, "only the authorised subscriber should receive telemetry")
|
|
|
|
|
for _, message := range messages {
|
|
|
|
|
assert.Equal(t, "owner@example.com", message.To[0].Address)
|
|
|
|
|
assert.Contains(t, message.Subject, "private-system CPU above threshold")
|
|
|
|
|
assert.Contains(t, message.Text, "91.00%")
|
|
|
|
|
t.Logf("captured synthetic email: recipient=%s subject=%q body=%q", message.To[0].Address, message.Subject, message.Text)
|
|
|
|
|
}
|
|
|
|
|
history, err := hub.FindAllRecords("alerts_history", dbx.HashExp{"system": foreign[0].Id})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Len(t, history, 1)
|
|
|
|
|
for _, record := range history {
|
|
|
|
|
assert.Equal(t, owner.Id, record.GetString("user"))
|
|
|
|
|
}
|
|
|
|
|
t.Logf("captured synthetic history entries=%d", len(history))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAlertStoredSubscriptionAccess(t *testing.T) {
|
|
|
|
|
for _, mode := range []string{"foreign", "revoked", "shared", "share_all", "revoked_active"} {
|
|
|
|
|
t.Run(mode, func(t *testing.T) {
|
|
|
|
|
t.Setenv("BESZEL_HUB_SHARE_ALL_SYSTEMS", "false")
|
|
|
|
|
if mode == "share_all" {
|
|
|
|
|
t.Setenv("BESZEL_HUB_SHARE_ALL_SYSTEMS", "true")
|
|
|
|
|
}
|
|
|
|
|
hub, err := beszelTests.NewTestHub(t.TempDir())
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer hub.Cleanup()
|
|
|
|
|
hub.StartHub()
|
|
|
|
|
owner, err := beszelTests.CreateUser(hub, "owner@example.com", "password")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
subscriber, err := beszelTests.CreateUser(hub, "subscriber@example.com", "password")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
systems, err := beszelTests.CreateSystems(hub, 1, owner.Id, "paused")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
systemRecord := systems[0]
|
|
|
|
|
if mode == "revoked" || mode == "shared" || mode == "revoked_active" {
|
|
|
|
|
systemRecord.Set("users", []string{owner.Id, subscriber.Id})
|
|
|
|
|
require.NoError(t, hub.Save(systemRecord))
|
|
|
|
|
}
|
|
|
|
|
for _, user := range []*core.Record{owner, subscriber} {
|
|
|
|
|
_, err = beszelTests.CreateRecord(hub, "user_settings", map[string]any{
|
|
|
|
|
"user": user.Id, "settings": map[string]any{"emails": []string{user.GetString("email")}, "webhooks": []string{}},
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
// Direct saves model records already stored before the request-hook fix.
|
|
|
|
|
_, err = beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
|
|
|
|
"name": "CPU", "user": user.Id, "system": systemRecord.Id, "value": 50, "min": 1,
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
require.NoError(t, hub.GetAlertManager().GetSystemAlertsCache().PopulateFromDB(true))
|
|
|
|
|
if mode == "revoked" {
|
|
|
|
|
systemRecord.Set("users", []string{owner.Id})
|
|
|
|
|
require.NoError(t, hub.Save(systemRecord))
|
|
|
|
|
}
|
|
|
|
|
synctest.Test(t, func(t *testing.T) {
|
|
|
|
|
require.NoError(t, hub.GetAlertManager().HandleSystemAlerts(systemRecord, &system.CombinedData{Info: system.Info{Cpu: 91}}))
|
|
|
|
|
synctest.Wait()
|
|
|
|
|
})
|
|
|
|
|
allowed := mode == "shared" || mode == "share_all" || mode == "revoked_active"
|
|
|
|
|
want := 1
|
|
|
|
|
if allowed {
|
|
|
|
|
want = 2
|
|
|
|
|
}
|
|
|
|
|
assert.Equal(t, want, hub.TestMailer.TotalSend())
|
|
|
|
|
for _, message := range hub.TestMailer.Messages() {
|
|
|
|
|
if !allowed {
|
|
|
|
|
assert.Equal(t, "owner@example.com", message.To[0].Address)
|
|
|
|
|
}
|
|
|
|
|
t.Logf("%s captured recipient=%s body=%q", mode, message.To[0].Address, message.Text)
|
|
|
|
|
}
|
|
|
|
|
history, err := hub.FindAllRecords("alerts_history")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Len(t, history, want)
|
|
|
|
|
for _, record := range history {
|
|
|
|
|
if !allowed {
|
|
|
|
|
assert.Equal(t, owner.Id, record.GetString("user"))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
count, err := hub.CountRecords("alerts")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.EqualValues(t, 2, count, "stored subscriptions are preserved")
|
|
|
|
|
if mode == "revoked_active" {
|
|
|
|
|
systemRecord.Set("users", []string{owner.Id})
|
|
|
|
|
require.NoError(t, hub.Save(systemRecord))
|
|
|
|
|
synctest.Test(t, func(t *testing.T) {
|
|
|
|
|
require.NoError(t, hub.GetAlertManager().HandleSystemAlerts(systemRecord, &system.CombinedData{Info: system.Info{Cpu: 40}}))
|
|
|
|
|
synctest.Wait()
|
|
|
|
|
})
|
|
|
|
|
assert.Equal(t, 3, hub.TestMailer.TotalSend(), "only the owner should receive recovery after revocation")
|
|
|
|
|
previous, err := hub.FindFirstRecordByFilter("alerts_history", "user={:user}", dbx.Params{"user": subscriber.Id})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.True(t, previous.GetDateTime("resolved").IsZero(), "revoked subscribers must not learn recovery timing through history")
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAlertPendingStatusAccessRevoked(t *testing.T) {
|
|
|
|
|
t.Setenv("BESZEL_HUB_SHARE_ALL_SYSTEMS", "false")
|
|
|
|
|
hub, err := beszelTests.NewTestHub(t.TempDir())
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer hub.Cleanup()
|
|
|
|
|
hub.StartHub()
|
|
|
|
|
user, err := beszelTests.CreateUser(hub, "subscriber@example.com", "password")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
owner, err := beszelTests.CreateUser(hub, "owner@example.com", "password")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
_, err = beszelTests.CreateRecord(hub, "user_settings", map[string]any{
|
|
|
|
|
"user": user.Id, "settings": map[string]any{"emails": []string{"subscriber@example.com"}, "webhooks": []string{}},
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
systems, err := beszelTests.CreateSystems(hub, 1, user.Id, "paused")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
_, err = beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
|
|
|
|
"name": "Status", "user": user.Id, "system": systems[0].Id, "min": 1,
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
synctest.Test(t, func(t *testing.T) {
|
|
|
|
|
defer hub.GetAlertManager().Stop()
|
|
|
|
|
require.NoError(t, hub.GetAlertManager().HandleStatusAlerts("down", systems[0]))
|
|
|
|
|
require.Equal(t, 1, hub.GetAlertManager().GetPendingAlertsCount())
|
|
|
|
|
systems[0].Set("users", []string{owner.Id})
|
|
|
|
|
require.NoError(t, hub.Save(systems[0]))
|
|
|
|
|
time.Sleep(61 * time.Second)
|
|
|
|
|
synctest.Wait()
|
|
|
|
|
assert.Zero(t, hub.TestMailer.TotalSend())
|
|
|
|
|
count, err := hub.CountRecords("alerts_history")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Zero(t, count)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAlertStoredNetworkMonitorAccess(t *testing.T) {
|
|
|
|
|
t.Setenv("BESZEL_HUB_SHARE_ALL_SYSTEMS", "false")
|
|
|
|
|
hub, systemRecord, _, monitors := networkAlertSetup(t)
|
|
|
|
|
other, err := beszelTests.CreateUser(hub, "foreign@example.com", "password")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
foreign, err := beszelTests.CreateRecord(hub, "alerts", map[string]any{
|
|
|
|
|
"name": "NetworkMonitorLoss", "user": other.Id, "system": systemRecord.Id, "value": 5,
|
|
|
|
|
})
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
am := alerts.NewTestAlertManagerWithoutWorker(hub)
|
|
|
|
|
require.NoError(t, am.HandleNetworkMonitorAlerts(systemRecord, map[string]monitor.Result{monitors[0].Id: monitorResult(10)}))
|
|
|
|
|
history, err := hub.FindAllRecords("alerts_history")
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Len(t, history, 1, "foreign subscriptions must not block the authorised incident transaction")
|
|
|
|
|
assert.NotEqual(t, foreign.Id, history[0].GetString("alert_id"))
|
|
|
|
|
assert.Equal(t, 1, hub.TestMailer.TotalSend())
|
|
|
|
|
}
|