128 lines
4.3 KiB
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)
|
|
}
|
|
}
|