fix(store): cascade delete related rows when removing a site
DeleteSite now removes maintenance_windows, check_history, and state_changes for the site within a transaction before deleting the site itself. Prevents orphaned rows. Closes #71
This commit was merged in pull request #92.
This commit is contained in:
@@ -152,10 +152,26 @@ func (s *SQLStore) UpdateSitePaused(id int, paused bool) error {
|
||||
}
|
||||
|
||||
func (s *SQLStore) DeleteSite(id int) error {
|
||||
_, err := s.db.Exec(s.q("DELETE FROM sites WHERE id=?"), id)
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
for _, q := range []string{
|
||||
"DELETE FROM maintenance_windows WHERE monitor_id = ?",
|
||||
"DELETE FROM check_history WHERE site_id = ?",
|
||||
"DELETE FROM state_changes WHERE site_id = ?",
|
||||
"DELETE FROM sites WHERE id = ?",
|
||||
} {
|
||||
if _, err := tx.Exec(s.q(q), id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
s.dialect.ResetSequenceOnEmpty(s.db, "sites")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"gitea.lerkolabs.com/lerkolabs/uptop/internal/models"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.lerkolabs.com/lerkolabs/uptop/internal/models"
|
||||
)
|
||||
|
||||
func newTestStore(t *testing.T) *SQLStore {
|
||||
@@ -229,3 +231,51 @@ func TestCheckHistory(t *testing.T) {
|
||||
t.Errorf("expected 1 up record for site 1, got %d", upCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteSiteCascade(t *testing.T) {
|
||||
s := newTestStore(t)
|
||||
|
||||
site := models.Site{Name: "Cascade Test", URL: "https://example.com", Interval: 30}
|
||||
if err := s.AddSite(site); err != nil {
|
||||
t.Fatalf("AddSite: %v", err)
|
||||
}
|
||||
sites, _ := s.GetSites()
|
||||
siteID := sites[0].ID
|
||||
|
||||
if err := s.SaveCheck(siteID, 1000, true); err != nil {
|
||||
t.Fatalf("SaveCheck: %v", err)
|
||||
}
|
||||
if err := s.SaveStateChange(siteID, "UP", "DOWN", "timeout"); err != nil {
|
||||
t.Fatalf("SaveStateChange: %v", err)
|
||||
}
|
||||
mw := models.MaintenanceWindow{
|
||||
MonitorID: siteID,
|
||||
Title: "Test MW",
|
||||
Type: "maintenance",
|
||||
StartTime: time.Now(),
|
||||
}
|
||||
if err := s.AddMaintenanceWindow(mw); err != nil {
|
||||
t.Fatalf("AddMaintenanceWindow: %v", err)
|
||||
}
|
||||
|
||||
if err := s.DeleteSite(siteID); err != nil {
|
||||
t.Fatalf("DeleteSite: %v", err)
|
||||
}
|
||||
|
||||
history, _ := s.LoadAllHistory(100)
|
||||
if len(history[siteID]) != 0 {
|
||||
t.Errorf("expected 0 check_history rows, got %d", len(history[siteID]))
|
||||
}
|
||||
|
||||
changes, _ := s.GetStateChanges(siteID, 100)
|
||||
if len(changes) != 0 {
|
||||
t.Errorf("expected 0 state_changes rows, got %d", len(changes))
|
||||
}
|
||||
|
||||
windows, _ := s.GetActiveMaintenanceWindows()
|
||||
for _, w := range windows {
|
||||
if w.MonitorID == siteID {
|
||||
t.Errorf("orphaned maintenance window found: id=%d", w.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user