Notifications repository + service (#4565)
* Notifications repository * feat: notifications repo + service * fix: remove use of notifications/notifications.go * Update persistence/notificationsrepository/notificationsrepository.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: remove unused method --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
package notificationsrepository
|
||||
|
||||
const (
|
||||
// BrowserPushNotification represents a push notification for a browser.
|
||||
BrowserPushNotification = "BROWSER_PUSH_NOTIFICATION"
|
||||
)
|
||||
@@ -0,0 +1,231 @@
|
||||
package notificationsrepository
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/models"
|
||||
"github.com/owncast/owncast/persistence/configrepository"
|
||||
)
|
||||
|
||||
var (
|
||||
integrationTestDatastore *data.Datastore
|
||||
integrationRepo NotificationsRepository
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Use the shared test datastore from the main test file
|
||||
// This ensures we're using the same database setup
|
||||
integrationTestDatastore = testDatastore
|
||||
if integrationTestDatastore != nil {
|
||||
integrationRepo = New(integrationTestDatastore)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationSetup(t *testing.T) {
|
||||
// This test ensures integration setup works properly
|
||||
// The actual setup is done in the init function below
|
||||
if integrationTestDatastore == nil {
|
||||
integrationTestDatastore = data.GetDatastore()
|
||||
Setup(integrationTestDatastore)
|
||||
integrationRepo = New(integrationTestDatastore)
|
||||
}
|
||||
|
||||
if integrationRepo == nil {
|
||||
t.Error("Integration repository should be initialized")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrowserPushSetupIntegration(t *testing.T) {
|
||||
// Test that browser push keys are generated during setup
|
||||
configRepo := configrepository.Get()
|
||||
pubKey, err := configRepo.GetBrowserPushPublicKey()
|
||||
if err != nil {
|
||||
t.Errorf("Should be able to get browser push public key: %v", err)
|
||||
}
|
||||
|
||||
privKey, err := configRepo.GetBrowserPushPrivateKey()
|
||||
if err != nil {
|
||||
t.Errorf("Should be able to get browser push private key: %v", err)
|
||||
}
|
||||
|
||||
// Keys should be generated during setup
|
||||
if pubKey == "" || privKey == "" {
|
||||
t.Error("Browser push keys should be generated during setup")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrowserPushConfigurationIntegration(t *testing.T) {
|
||||
configRepo := configrepository.Get()
|
||||
|
||||
// Test that browser push is enabled by default
|
||||
browserConfig := configRepo.GetBrowserPushConfig()
|
||||
if !browserConfig.Enabled {
|
||||
t.Error("Browser push should be enabled by default")
|
||||
}
|
||||
|
||||
// Test that initial notification configuration flag is set
|
||||
hasConfigured := configRepo.GetHasPerformedInitialNotificationsConfig()
|
||||
if !hasConfigured {
|
||||
t.Error("Should have performed initial notifications configuration")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscordConfigurationIntegration(t *testing.T) {
|
||||
configRepo := configrepository.Get()
|
||||
|
||||
// Test Discord configuration setup
|
||||
discordConfig := models.DiscordConfiguration{
|
||||
Enabled: true,
|
||||
Webhook: "https://discord.com/api/webhooks/test",
|
||||
GoLiveMessage: "Test stream is live!",
|
||||
}
|
||||
|
||||
err := configRepo.SetDiscordConfig(discordConfig)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to set Discord configuration: %v", err)
|
||||
}
|
||||
|
||||
// Test with disabled Discord
|
||||
disabledConfig := models.DiscordConfiguration{
|
||||
Enabled: false,
|
||||
Webhook: "",
|
||||
}
|
||||
err = configRepo.SetDiscordConfig(disabledConfig)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to disable Discord configuration: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationWorkflowIntegration(t *testing.T) {
|
||||
channel := BrowserPushNotification
|
||||
destination := "integration-test-endpoint"
|
||||
|
||||
// Add a notification
|
||||
err := integrationRepo.AddNotification(channel, destination)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add notification: %v", err)
|
||||
}
|
||||
|
||||
// Verify it was added
|
||||
destinations, err := integrationRepo.GetNotificationDestinationsForChannel(channel)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get notification destinations: %v", err)
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, dest := range destinations {
|
||||
if dest == destination {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Error("Notification destination should be found after adding")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
err = integrationRepo.RemoveNotificationForChannel(channel, destination)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to remove notification: %v", err)
|
||||
}
|
||||
|
||||
// Verify removal
|
||||
destinationsAfter, err := integrationRepo.GetNotificationDestinationsForChannel(channel)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get notification destinations after removal: %v", err)
|
||||
}
|
||||
|
||||
for _, dest := range destinationsAfter {
|
||||
if dest == destination {
|
||||
t.Error("Notification destination should be removed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryGetSingleton(t *testing.T) {
|
||||
// Test that Get() returns the same instance
|
||||
repo1 := Get()
|
||||
repo2 := Get()
|
||||
|
||||
if repo1 != repo2 {
|
||||
t.Error("Get() should return the same singleton instance")
|
||||
}
|
||||
|
||||
// Test that it implements the interface
|
||||
var _ NotificationsRepository = repo1
|
||||
}
|
||||
|
||||
func TestDatabaseTransactionIntegrity(t *testing.T) {
|
||||
channel := "TRANSACTION_TEST_CHANNEL"
|
||||
destinations := []string{"dest1", "dest2", "dest3"}
|
||||
|
||||
// Add multiple notifications
|
||||
for _, dest := range destinations {
|
||||
err := integrationRepo.AddNotification(channel, dest)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add notification %s: %v", dest, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify all were added
|
||||
retrievedDests, err := integrationRepo.GetNotificationDestinationsForChannel(channel)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to retrieve destinations: %v", err)
|
||||
}
|
||||
|
||||
if len(retrievedDests) != len(destinations) {
|
||||
t.Errorf("Expected %d destinations, got %d", len(destinations), len(retrievedDests))
|
||||
}
|
||||
|
||||
// Remove them one by one and verify state consistency
|
||||
for i, dest := range destinations {
|
||||
err := integrationRepo.RemoveNotificationForChannel(channel, dest)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to remove notification %s: %v", dest, err)
|
||||
}
|
||||
|
||||
remaining, err := integrationRepo.GetNotificationDestinationsForChannel(channel)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get remaining destinations: %v", err)
|
||||
}
|
||||
|
||||
expectedRemaining := len(destinations) - (i + 1)
|
||||
if len(remaining) != expectedRemaining {
|
||||
t.Errorf("Expected %d remaining destinations, got %d", expectedRemaining, len(remaining))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorHandling(t *testing.T) {
|
||||
// Test with empty channel
|
||||
err := integrationRepo.AddNotification("", "test-destination")
|
||||
if err != nil {
|
||||
// Empty channel might be allowed - this documents the behavior
|
||||
t.Logf("Empty channel behavior: %v", err)
|
||||
}
|
||||
|
||||
// Test with empty destination
|
||||
err = integrationRepo.AddNotification("TEST_CHANNEL", "")
|
||||
if err != nil {
|
||||
// Empty destination might be allowed - this documents the behavior
|
||||
t.Logf("Empty destination behavior: %v", err)
|
||||
}
|
||||
|
||||
// Test removing from empty channel
|
||||
err = integrationRepo.RemoveNotificationForChannel("", "test-destination")
|
||||
if err != nil {
|
||||
t.Logf("Remove from empty channel behavior: %v", err)
|
||||
}
|
||||
|
||||
// Test getting destinations for empty channel
|
||||
destinations, err := integrationRepo.GetNotificationDestinationsForChannel("")
|
||||
if err != nil {
|
||||
t.Errorf("Getting destinations for empty channel should not error: %v", err)
|
||||
}
|
||||
|
||||
if len(destinations) > 0 {
|
||||
t.Logf("Empty channel returned %d destinations", len(destinations))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package notificationsrepository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/owncast/owncast/config"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/db"
|
||||
"github.com/owncast/owncast/models"
|
||||
"github.com/owncast/owncast/persistence/configrepository"
|
||||
"github.com/owncast/owncast/persistence/tables"
|
||||
|
||||
"github.com/owncast/owncast/notifications/browser"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type NotificationsRepository interface {
|
||||
AddNotification(channel, destination string) error
|
||||
RemoveNotificationForChannel(channel, destination string) error
|
||||
GetNotificationDestinationsForChannel(channel string) ([]string, error)
|
||||
}
|
||||
|
||||
// SqlNotificationsRepository handles database operations for notifications.
|
||||
type SqlNotificationsRepository struct {
|
||||
datastore *data.Datastore
|
||||
}
|
||||
|
||||
// NOTE: This is temporary during the transition period.
|
||||
var temporaryGlobalInstance NotificationsRepository
|
||||
|
||||
// Get will return the notifications repository.
|
||||
func Get() NotificationsRepository {
|
||||
if temporaryGlobalInstance == nil {
|
||||
i := New(data.GetDatastore())
|
||||
temporaryGlobalInstance = i
|
||||
}
|
||||
return temporaryGlobalInstance
|
||||
}
|
||||
|
||||
// Setup will perform any pre-use setup for the notifier.
|
||||
func Setup(datastore *data.Datastore) {
|
||||
tables.CreateNotificationsTable(datastore.DB)
|
||||
initializeBrowserPushIfNeeded()
|
||||
}
|
||||
|
||||
func initializeBrowserPushIfNeeded() {
|
||||
configRepository := configrepository.Get()
|
||||
|
||||
pubKey, _ := configRepository.GetBrowserPushPublicKey()
|
||||
privKey, _ := configRepository.GetBrowserPushPrivateKey()
|
||||
|
||||
// We need browser push keys so people can register for pushes.
|
||||
if pubKey == "" || privKey == "" {
|
||||
browserPrivateKey, browserPublicKey, err := browser.GenerateBrowserPushKeys()
|
||||
if err != nil {
|
||||
log.Errorln("unable to initialize browser push notification keys", err)
|
||||
}
|
||||
|
||||
if err := configRepository.SetBrowserPushPrivateKey(browserPrivateKey); err != nil {
|
||||
log.Errorln("unable to set browser push private key", err)
|
||||
}
|
||||
|
||||
if err := configRepository.SetBrowserPushPublicKey(browserPublicKey); err != nil {
|
||||
log.Errorln("unable to set browser push public key", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Enable browser push notifications by default.
|
||||
if !configRepository.GetHasPerformedInitialNotificationsConfig() {
|
||||
_ = configRepository.SetBrowserPushConfig(models.BrowserNotificationConfiguration{Enabled: true, GoLiveMessage: config.GetDefaults().FederationGoLiveMessage})
|
||||
_ = configRepository.SetHasPerformedInitialNotificationsConfig(true)
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new instance of the NotificationsRepository.
|
||||
func New(datastore *data.Datastore) NotificationsRepository {
|
||||
return &SqlNotificationsRepository{
|
||||
datastore: datastore,
|
||||
}
|
||||
}
|
||||
|
||||
// AddNotification saves a new user notification destination.
|
||||
func (n *SqlNotificationsRepository) AddNotification(channel, destination string) error {
|
||||
return n.datastore.GetQueries().AddNotification(context.Background(), db.AddNotificationParams{
|
||||
Channel: channel,
|
||||
Destination: destination,
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveNotificationForChannel removes a notification destination.
|
||||
func (n *SqlNotificationsRepository) RemoveNotificationForChannel(channel, destination string) error {
|
||||
log.Debugln("Removing notification for channel", channel)
|
||||
|
||||
return n.datastore.GetQueries().RemoveNotificationDestinationForChannel(context.Background(), db.RemoveNotificationDestinationForChannelParams{
|
||||
Channel: channel,
|
||||
Destination: destination,
|
||||
})
|
||||
}
|
||||
|
||||
// GetNotificationDestinationsForChannel will return a collection of
|
||||
// destinations to notify for a given channel.
|
||||
func (n *SqlNotificationsRepository) GetNotificationDestinationsForChannel(channel string) ([]string, error) {
|
||||
result, err := n.datastore.GetQueries().GetNotificationDestinationsForChannel(context.Background(), channel)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to query notification destinations for channel "+channel)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
package notificationsrepository
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/owncast/owncast/core/data"
|
||||
)
|
||||
|
||||
var (
|
||||
testDatastore *data.Datastore
|
||||
testRepo NotificationsRepository
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Create an in-memory database for testing
|
||||
if err := data.SetupPersistence(":memory:"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Get the shared datastore instance
|
||||
testDatastore = data.GetDatastore()
|
||||
|
||||
// Setup the notifications repository
|
||||
Setup(testDatastore)
|
||||
testRepo = New(testDatastore)
|
||||
|
||||
// Run tests
|
||||
m.Run()
|
||||
}
|
||||
|
||||
func TestAddNotification(t *testing.T) {
|
||||
channel := "TEST_CHANNEL_ADD_UNIQUE"
|
||||
destination := "test-destination-add-unique-123"
|
||||
|
||||
// Test adding a notification
|
||||
err := testRepo.AddNotification(channel, destination)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add notification: %v", err)
|
||||
}
|
||||
|
||||
// Verify the notification was added
|
||||
destinations, err := testRepo.GetNotificationDestinationsForChannel(channel)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get notification destinations: %v", err)
|
||||
}
|
||||
|
||||
if len(destinations) != 1 {
|
||||
t.Errorf("Expected 1 destination, got %d", len(destinations))
|
||||
}
|
||||
|
||||
if destinations[0] != destination {
|
||||
t.Errorf("Expected destination %s, got %s", destination, destinations[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddMultipleNotifications(t *testing.T) {
|
||||
channel := "MULTI_TEST_CHANNEL"
|
||||
destinations := []string{"dest-1", "dest-2", "dest-3"}
|
||||
|
||||
// Add multiple notifications
|
||||
for _, dest := range destinations {
|
||||
err := testRepo.AddNotification(channel, dest)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add notification for destination %s: %v", dest, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify all notifications were added
|
||||
retrievedDests, err := testRepo.GetNotificationDestinationsForChannel(channel)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get notification destinations: %v", err)
|
||||
}
|
||||
|
||||
if len(retrievedDests) != len(destinations) {
|
||||
t.Errorf("Expected %d destinations, got %d", len(destinations), len(retrievedDests))
|
||||
}
|
||||
|
||||
// Check that all destinations are present
|
||||
for _, expectedDest := range destinations {
|
||||
found := false
|
||||
for _, retrievedDest := range retrievedDests {
|
||||
if retrievedDest == expectedDest {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected destination %s not found in retrieved destinations", expectedDest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveNotificationForChannel(t *testing.T) {
|
||||
channel := "REMOVE_TEST_CHANNEL"
|
||||
destination1 := "remove-dest-1"
|
||||
destination2 := "remove-dest-2"
|
||||
|
||||
// Add two notifications
|
||||
err := testRepo.AddNotification(channel, destination1)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add notification: %v", err)
|
||||
}
|
||||
|
||||
err = testRepo.AddNotification(channel, destination2)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add notification: %v", err)
|
||||
}
|
||||
|
||||
// Verify both were added
|
||||
destinations, err := testRepo.GetNotificationDestinationsForChannel(channel)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get notification destinations: %v", err)
|
||||
}
|
||||
|
||||
if len(destinations) != 2 {
|
||||
t.Errorf("Expected 2 destinations before removal, got %d", len(destinations))
|
||||
}
|
||||
|
||||
// Remove one notification
|
||||
err = testRepo.RemoveNotificationForChannel(channel, destination1)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to remove notification: %v", err)
|
||||
}
|
||||
|
||||
// Verify only one remains
|
||||
destinations, err = testRepo.GetNotificationDestinationsForChannel(channel)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get notification destinations after removal: %v", err)
|
||||
}
|
||||
|
||||
if len(destinations) != 1 {
|
||||
t.Errorf("Expected 1 destination after removal, got %d", len(destinations))
|
||||
}
|
||||
|
||||
if destinations[0] != destination2 {
|
||||
t.Errorf("Expected remaining destination %s, got %s", destination2, destinations[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNotificationDestinationsForNonExistentChannel(t *testing.T) {
|
||||
channel := "NON_EXISTENT_CHANNEL"
|
||||
|
||||
destinations, err := testRepo.GetNotificationDestinationsForChannel(channel)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get notification destinations for non-existent channel: %v", err)
|
||||
}
|
||||
|
||||
if len(destinations) != 0 {
|
||||
t.Errorf("Expected 0 destinations for non-existent channel, got %d", len(destinations))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveNonExistentNotification(t *testing.T) {
|
||||
channel := "NON_EXISTENT_REMOVE_CHANNEL"
|
||||
destination := "non-existent-destination"
|
||||
|
||||
// Try to remove a notification that doesn't exist
|
||||
err := testRepo.RemoveNotificationForChannel(channel, destination)
|
||||
if err != nil {
|
||||
t.Errorf("Removing non-existent notification should not return error, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrowserPushNotificationConstant(t *testing.T) {
|
||||
// Test that the constant is defined and has the expected value
|
||||
if BrowserPushNotification == "" {
|
||||
t.Error("BrowserPushNotification constant should not be empty")
|
||||
}
|
||||
|
||||
expectedValue := "BROWSER_PUSH_NOTIFICATION"
|
||||
if BrowserPushNotification != expectedValue {
|
||||
t.Errorf("Expected BrowserPushNotification to be %s, got %s", expectedValue, BrowserPushNotification)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationRepositoryInterface(t *testing.T) {
|
||||
// Test that our implementation satisfies the interface
|
||||
var _ NotificationsRepository = &SqlNotificationsRepository{}
|
||||
|
||||
// Test that we can get the repository instance
|
||||
repo := Get()
|
||||
if repo == nil {
|
||||
t.Error("Get() should return a non-nil repository instance")
|
||||
}
|
||||
|
||||
// Test that New creates a valid repository
|
||||
newRepo := New(testDatastore)
|
||||
if newRepo == nil {
|
||||
t.Error("New() should return a non-nil repository instance")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddDuplicateNotification(t *testing.T) {
|
||||
channel := "DUPLICATE_TEST_CHANNEL"
|
||||
destination := "duplicate-dest"
|
||||
|
||||
// Add the same notification twice
|
||||
err := testRepo.AddNotification(channel, destination)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add notification first time: %v", err)
|
||||
}
|
||||
|
||||
err = testRepo.AddNotification(channel, destination)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add duplicate notification: %v", err)
|
||||
}
|
||||
|
||||
// Check how many destinations we have (should handle duplicates gracefully)
|
||||
destinations, err := testRepo.GetNotificationDestinationsForChannel(channel)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get notification destinations: %v", err)
|
||||
}
|
||||
|
||||
// The behavior for duplicates depends on database constraints
|
||||
// This test documents the current behavior
|
||||
if len(destinations) == 0 {
|
||||
t.Error("Should have at least one destination even with duplicates")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelIsolation(t *testing.T) {
|
||||
channel1 := "CHANNEL_1"
|
||||
channel2 := "CHANNEL_2"
|
||||
destination1 := "dest-for-channel-1"
|
||||
destination2 := "dest-for-channel-2"
|
||||
|
||||
// Add notifications to different channels
|
||||
err := testRepo.AddNotification(channel1, destination1)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add notification to channel1: %v", err)
|
||||
}
|
||||
|
||||
err = testRepo.AddNotification(channel2, destination2)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add notification to channel2: %v", err)
|
||||
}
|
||||
|
||||
// Verify channel isolation
|
||||
destinations1, err := testRepo.GetNotificationDestinationsForChannel(channel1)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get destinations for channel1: %v", err)
|
||||
}
|
||||
|
||||
destinations2, err := testRepo.GetNotificationDestinationsForChannel(channel2)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get destinations for channel2: %v", err)
|
||||
}
|
||||
|
||||
// Each channel should only have its own destination
|
||||
if len(destinations1) != 1 || destinations1[0] != destination1 {
|
||||
t.Errorf("Channel1 should only have destination1, got: %v", destinations1)
|
||||
}
|
||||
|
||||
if len(destinations2) != 1 || destinations2[0] != destination2 {
|
||||
t.Errorf("Channel2 should only have destination2, got: %v", destinations2)
|
||||
}
|
||||
|
||||
// Remove from one channel shouldn't affect the other
|
||||
err = testRepo.RemoveNotificationForChannel(channel1, destination1)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to remove notification from channel1: %v", err)
|
||||
}
|
||||
|
||||
// Verify channel2 is unaffected
|
||||
destinations2After, err := testRepo.GetNotificationDestinationsForChannel(channel2)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get destinations for channel2 after removal: %v", err)
|
||||
}
|
||||
|
||||
if len(destinations2After) != 1 || destinations2After[0] != destination2 {
|
||||
t.Errorf("Channel2 should still have destination2 after channel1 removal, got: %v", destinations2After)
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark tests
|
||||
func BenchmarkAddNotification(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
channel := fmt.Sprintf("BENCH_CHANNEL_%d", i)
|
||||
destination := fmt.Sprintf("bench_destination_%d", i)
|
||||
err := testRepo.AddNotification(channel, destination)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to add notification: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetNotificationDestinationsForChannel(b *testing.B) {
|
||||
// Setup test data
|
||||
channel := "BENCH_GET_CHANNEL"
|
||||
for i := 0; i < 100; i++ {
|
||||
destination := fmt.Sprintf("bench_get_destination_%d", i)
|
||||
testRepo.AddNotification(channel, destination)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := testRepo.GetNotificationDestinationsForChannel(channel)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to get notification destinations: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRemoveNotificationForChannel(b *testing.B) {
|
||||
// Setup test data
|
||||
channel := "BENCH_REMOVE_CHANNEL"
|
||||
destinations := make([]string, b.N)
|
||||
for i := 0; i < b.N; i++ {
|
||||
destination := fmt.Sprintf("bench_remove_destination_%d", i)
|
||||
destinations[i] = destination
|
||||
testRepo.AddNotification(channel, destination)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := testRepo.RemoveNotificationForChannel(channel, destinations[i])
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to remove notification: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user