mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-07 08:57:12 +00:00
140 lines
4.5 KiB
Go
140 lines
4.5 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/notifications"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
)
|
|
|
|
// TestRateLimitPersistence_FullRoundTrip tests the complete data flow:
|
|
// 1. Existing config has RateLimit=120
|
|
// 2. User updates other fields without rateLimit in JSON
|
|
// 3. Backend preserves RateLimit=120
|
|
// 4. GET returns RateLimit=120
|
|
func TestRateLimitPersistence_FullRoundTrip(t *testing.T) {
|
|
// Setup mocks
|
|
mockMonitor := new(MockNotificationMonitor)
|
|
mockManager := new(MockNotificationManager)
|
|
mockPersistence := new(MockNotificationConfigPersistence)
|
|
|
|
mockMonitor.On("GetNotificationManager").Return(mockManager)
|
|
mockMonitor.On("GetConfigPersistence").Return(mockPersistence)
|
|
|
|
h := NewNotificationHandlers(nil, mockMonitor)
|
|
|
|
// Existing config in "database" has RateLimit=120
|
|
existingConfig := notifications.EmailConfig{
|
|
Enabled: true,
|
|
SMTPHost: "smtp.example.com",
|
|
Password: "secret",
|
|
RateLimit: 120,
|
|
}
|
|
|
|
// Test 1: GET returns the rateLimit
|
|
t.Run("GET_returns_rateLimit", func(t *testing.T) {
|
|
mockManager.On("GetEmailConfig").Return(existingConfig).Once()
|
|
|
|
req := httptest.NewRequest("GET", "/api/notifications/email", nil)
|
|
w := httptest.NewRecorder()
|
|
h.GetEmailConfig(w, req)
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
|
|
var resp map[string]interface{}
|
|
_ = json.Unmarshal(w.Body.Bytes(), &resp)
|
|
|
|
// Verify rateLimit is returned (password should be empty for security)
|
|
assert.Equal(t, float64(120), resp["rateLimit"])
|
|
assert.Equal(t, "", resp["password"]) // Redacted
|
|
})
|
|
|
|
// Test 2: PUT without rateLimit preserves existing value
|
|
t.Run("PUT_without_rateLimit_preserves_existing", func(t *testing.T) {
|
|
// Return existing config when handler calls GetEmailConfig
|
|
mockManager.On("GetEmailConfig").Return(existingConfig).Once()
|
|
|
|
// Expect SetEmailConfig to be called WITH RateLimit=120 preserved
|
|
mockManager.On("SetEmailConfig", mock.MatchedBy(func(c notifications.EmailConfig) bool {
|
|
t.Logf("SetEmailConfig called with RateLimit=%d", c.RateLimit)
|
|
return c.RateLimit == 120 && c.SMTPHost == "smtp.newhost.com"
|
|
})).Return().Once()
|
|
|
|
mockPersistence.On("SaveEmailConfig", mock.MatchedBy(func(c notifications.EmailConfig) bool {
|
|
return c.RateLimit == 120
|
|
})).Return(nil).Once()
|
|
|
|
// Request body does NOT include rateLimit
|
|
payload := map[string]interface{}{
|
|
"enabled": true,
|
|
"server": "smtp.newhost.com",
|
|
"password": "newpassword",
|
|
}
|
|
|
|
body, _ := json.Marshal(payload)
|
|
req := httptest.NewRequest("PUT", "/api/notifications/email", bytes.NewReader(body))
|
|
w := httptest.NewRecorder()
|
|
h.UpdateEmailConfig(w, req)
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
mockManager.AssertExpectations(t)
|
|
mockPersistence.AssertExpectations(t)
|
|
})
|
|
|
|
// Test 3: PUT with rateLimit=0 explicitly sets it to 0
|
|
t.Run("PUT_with_explicit_rateLimit_0_sets_to_0", func(t *testing.T) {
|
|
mockManager.On("GetEmailConfig").Return(existingConfig).Once()
|
|
|
|
mockManager.On("SetEmailConfig", mock.MatchedBy(func(c notifications.EmailConfig) bool {
|
|
t.Logf("SetEmailConfig called with RateLimit=%d", c.RateLimit)
|
|
return c.RateLimit == 0 // User explicitly set to 0
|
|
})).Return().Once()
|
|
|
|
mockPersistence.On("SaveEmailConfig", mock.Anything).Return(nil).Once()
|
|
|
|
// Request body INCLUDES rateLimit: 0
|
|
payload := map[string]interface{}{
|
|
"enabled": true,
|
|
"server": "smtp.example.com",
|
|
"rateLimit": 0,
|
|
}
|
|
|
|
body, _ := json.Marshal(payload)
|
|
req := httptest.NewRequest("PUT", "/api/notifications/email", bytes.NewReader(body))
|
|
w := httptest.NewRecorder()
|
|
h.UpdateEmailConfig(w, req)
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
mockManager.AssertExpectations(t)
|
|
})
|
|
|
|
// Test 4: PUT with new rateLimit updates it
|
|
t.Run("PUT_with_new_rateLimit_updates", func(t *testing.T) {
|
|
mockManager.On("GetEmailConfig").Return(existingConfig).Once()
|
|
|
|
mockManager.On("SetEmailConfig", mock.MatchedBy(func(c notifications.EmailConfig) bool {
|
|
t.Logf("SetEmailConfig called with RateLimit=%d", c.RateLimit)
|
|
return c.RateLimit == 60 // User changed to 60
|
|
})).Return().Once()
|
|
|
|
mockPersistence.On("SaveEmailConfig", mock.Anything).Return(nil).Once()
|
|
|
|
payload := map[string]interface{}{
|
|
"enabled": true,
|
|
"server": "smtp.example.com",
|
|
"rateLimit": 60,
|
|
}
|
|
|
|
body, _ := json.Marshal(payload)
|
|
req := httptest.NewRequest("PUT", "/api/notifications/email", bytes.NewReader(body))
|
|
w := httptest.NewRecorder()
|
|
h.UpdateEmailConfig(w, req)
|
|
|
|
assert.Equal(t, 200, w.Code)
|
|
mockManager.AssertExpectations(t)
|
|
})
|
|
}
|