Files
fenrir/internal/app/shared_notifications_test.go

128 lines
4.3 KiB
Go

// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"fmt"
"os"
"reflect"
"testing"
"time"
)
func shared_notification_application(t *testing.T, namespace string) (*application, *fakeVoice, *fakeSounds) {
t.Helper()
a, voice := persistence_application(t, true)
voice.messages = nil
sounds := &fakeSounds{}
a.cues = sounds
a.options.SharedNotifications = true
a.options.NotificationNamespace = namespace
a.share_notification_history(1, true)
t.Cleanup(func() {
a.notificationHistory.Close()
a.notificationClosers.Wait()
})
return a, voice, sounds
}
func wait_shared_notifications(t *testing.T, a *application, count int) {
t.Helper()
deadline := time.NewTimer(3 * time.Second)
defer deadline.Stop()
for {
a.refresh_notification_history()
if len(a.notifications) == count {
return
}
select {
case <-a.notificationHistory.Updates():
case <-deadline.C:
t.Fatalf("notification history has %d entries, want %d", len(a.notifications), count)
}
}
}
func TestSharedNotificationHistoryNeverSpeaksOrPlaysOnOtherReaders(t *testing.T) {
namespace := fmt.Sprintf(".app.%d.%d", os.Getpid(), time.Now().UnixNano())
var readers []*application
var voices []*fakeVoice
var sounds []*fakeSounds
for range 4 {
a, voice, cues := shared_notification_application(t, namespace)
readers, voices, sounds = append(readers, a), append(voices, voice), append(sounds, cues)
}
if response := runtime_request(readers[0], "command notify First target"); response != "" {
t.Fatal(response)
}
for index, a := range readers {
wait_shared_notifications(t, a, 1)
wantSpeech, wantCues := 0, 0
if index == 0 {
wantSpeech, wantCues = 1, 1
}
if len(voices[index].messages) != wantSpeech || len(sounds[index].cues) != wantCues {
t.Fatal("silent history update triggered speech or sound", index, voices[index].messages, sounds[index].cues)
}
}
runtime_request(readers[1], "command notify Second target")
for index, a := range readers {
wait_shared_notifications(t, a, 2)
if !reflect.DeepEqual(a.notifications, readers[0].notifications) {
t.Fatal("readers disagree about history", index)
}
}
if !reflect.DeepEqual(voices[0].messages, []string{"First target"}) || !reflect.DeepEqual(voices[1].messages, []string{"Second target"}) {
t.Fatal("targeted speech was duplicated", voices[0].messages, voices[1].messages)
}
for index := 1; index < len(readers); index++ {
if len(sounds[index].cues) != 0 {
t.Fatal("target switch reset the shared sound cooldown", index, sounds[index].cues)
}
}
// History recall is an explicit user action and still belongs to that reader.
readers[2].last_notification()
if !reflect.DeepEqual(voices[2].messages, []string{"Second target"}) || len(sounds[2].cues) != 0 {
t.Fatal("history recall was missing or replayed the cue")
}
readers[2].muted, voices[2].muted = true, true
runtime_request(readers[2], "command notify Muted target")
readers[3].temporarySilence = true
runtime_request(readers[3], "command notify Silenced target")
for index, a := range readers {
wait_shared_notifications(t, a, 4)
if index >= 2 && len(sounds[index].cues) != 0 {
t.Fatal("muted or silenced target replayed the cue")
}
}
joining, voice, cues := shared_notification_application(t, namespace)
wait_shared_notifications(t, joining, 4)
if len(voice.messages) != 0 || len(cues.cues) != 0 || !reflect.DeepEqual(joining.notifications, readers[0].notifications) {
t.Fatal("joining reader announced existing history or loaded different messages")
}
}
func TestNotificationUserSwitchClearsHistoryAndClosesOldDialog(t *testing.T) {
namespace := fmt.Sprintf(".app.%d.%d", os.Getpid(), time.Now().UnixNano())
a, voice, _ := shared_notification_application(t, namespace)
peer, _, _ := shared_notification_application(t, namespace)
a.notify("Original account", time.Now())
wait_shared_notifications(t, peer, 1)
a.open_notifications()
if !a.ui.active {
t.Fatal("notification dialog did not open")
}
a.select_notification_user(os.Geteuid() + 1)
voice.messages = nil
a.refresh_notification_history()
if len(a.notifications) != 0 || a.ui.active {
t.Fatal("switching accounts exposed the previous history or dialog")
}
a.select_notification_user(os.Geteuid())
wait_shared_notifications(t, a, 1)
if len(voice.messages) != 0 {
t.Fatal("restoring an account spoke its history", voice.messages)
}
}