Skip to content
Snippets Groups Projects
Commit b711c44f authored by benjamin's avatar benjamin
Browse files

finished tests

parent 9bea7527
No related branches found
No related tags found
5 merge requests!622Add notifications system with sync,!620Account Sychronization First Pass,!618Make it so that transaction log can run without a remote to write to, and add...,!617Project/haven beta,!523Draft: Account Synchronization Over Remote Storage
...@@ -29,7 +29,23 @@ type Notifications interface { ...@@ -29,7 +29,23 @@ type Notifications interface {
AddToken(newToken, app string) error AddToken(newToken, app string) error
// RemoveToken removes the given Token from the server // RemoveToken removes the given Token from the server
// It will remove all registered identities if it is the last Token // It will remove all registered identities if it is the last Token
RemoveToken() error RemoveToken() error
// SetMaxState sets the maximum functional state of any identity
// downstream moduals will be told to clamp any state greater than maxState
// down to maxState. Depending on UX requirements, they may still show the
// state in an altered manner, for example greying out a description.
// This is designed so when the state is raised, the old configs are
// maintained.
// This will unregister / re-register with the push server when leaving or
// entering the Push maxState.
// The default maxState is Push
// will return an error if the maxState isnt a valid state
SetMaxState(maxState notifications.NotificationState) error
// GetMaxState returns the current MaxState
GetMaxState() notifications.NotificationState
// GetID returns the ID of the notifications object // GetID returns the ID of the notifications object
GetID() int GetID() int
} }
......
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2022 xx foundation //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file. //
////////////////////////////////////////////////////////////////////////////////
package bindings
import (
"encoding/json"
"fmt"
"testing"
"gitlab.com/elixxir/client/v4/e2e/ratchet"
"gitlab.com/xx_network/primitives/id"
)
func TestNotificationReport(t *testing.T) {
var reports []NotificationReport
for i := 0; i < 3; i++ {
nr := NotificationReport{
ForMe: true,
Type: []string{ratchet.E2e},
Source: id.NewIdFromUInt(uint64(i), id.User, t).Bytes(),
}
reports = append(reports, nr)
}
nrs := NotificationReports(reports)
marshal, _ := json.MarshalIndent(nrs, " ", " ")
fmt.Printf("%s\n", marshal)
}
...@@ -2,6 +2,7 @@ package notifications ...@@ -2,6 +2,7 @@ package notifications
import ( import (
"gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/id"
"math"
"testing" "testing"
) )
...@@ -61,3 +62,18 @@ func TestNotificationState_String(t *testing.T) { ...@@ -61,3 +62,18 @@ func TestNotificationState_String(t *testing.T) {
} }
} }
} }
func TestNotificationState_IsValid(t *testing.T) {
inputs := []NotificationState{Mute, WhenOpen, Push, -100, 14, Mute - 1,
Push + 1, 1000, math.MaxInt64, math.MinInt64}
outputs := []bool{true, true, true, false, false, false, false, false,
false, false}
for idx, ns := range inputs {
result := ns.IsValid() == nil
if outputs[idx] != result {
t.Errorf("on test %d output did not match expected, "+
"expected: %t, received: %t", idx, outputs[idx], result)
}
}
}
...@@ -230,6 +230,7 @@ func (m *manager) maxStateUpdate(key string, old, new *versioned.Object, op vers ...@@ -230,6 +230,7 @@ func (m *manager) maxStateUpdate(key string, old, new *versioned.Object, op vers
if key != maxStateKey { if key != maxStateKey {
jww.ERROR.Printf("Got an update for the wrong key, "+ jww.ERROR.Printf("Got an update for the wrong key, "+
"expected: %s, got: %s", maxStateKey, key) "expected: %s, got: %s", maxStateKey, key)
return
} }
if op == versioned.Deleted { if op == versioned.Deleted {
...@@ -242,6 +243,7 @@ func (m *manager) maxStateUpdate(key string, old, new *versioned.Object, op vers ...@@ -242,6 +243,7 @@ func (m *manager) maxStateUpdate(key string, old, new *versioned.Object, op vers
if err := json.Unmarshal(new.Data, &m.maxState); err != nil { if err := json.Unmarshal(new.Data, &m.maxState); err != nil {
jww.WARN.Printf("failed to unmarshal %s, ignoring: %+v", jww.WARN.Printf("failed to unmarshal %s, ignoring: %+v",
maxStateKey, err) maxStateKey, err)
return
} }
for g := range m.callbacks { for g := range m.callbacks {
...@@ -260,7 +262,7 @@ func (m *manager) loadMaxStateUnsafe(obj *versioned.Object) { ...@@ -260,7 +262,7 @@ func (m *manager) loadMaxStateUnsafe(obj *versioned.Object) {
func (m *manager) setMaxStateUnsafe(max NotificationState) { func (m *manager) setMaxStateUnsafe(max NotificationState) {
b, err := json.Marshal(&m.maxState) b, err := json.Marshal(&max)
if err != nil { if err != nil {
jww.FATAL.Panicf("Failed to set max notifications sate to %s:"+ jww.FATAL.Panicf("Failed to set max notifications sate to %s:"+
" %+v", max, err) " %+v", max, err)
......
...@@ -397,6 +397,123 @@ func getGroup(i, numGroups int) int { ...@@ -397,6 +397,123 @@ func getGroup(i, numGroups int) int {
return i % numGroups return i % numGroups
} }
func TestManager_maxStateUpdate(t *testing.T) {
m, _, _ := buildTestingManager(t)
mInternal := m.(*manager)
mInternal.maxStateUpdate("blah", nil, nil, versioned.Deleted)
// key check worked becasue we didnt crash from the delete panic
numGroups := 4
numCB := numGroups / 2
wg := &sync.WaitGroup{}
didRun := make([]bool, numGroups)
expectedRun := []bool{true, false, true, false}
var setMax NotificationState
for i := 0; i < numGroups; i++ {
groupName := fmt.Sprintf("grp_%d", i)
nid := id.NewIdFromUInt(uint64(i), id.User, t)
m.Set(nid, groupName, []byte{0}, NotificationState(i%3))
if i%2 == 0 {
localI := i
cb := func(group Group, created, edits, deletions []*id.ID,
maxState NotificationState) {
if created != nil || edits != nil || deletions != nil {
t.Errorf("actions are not nil")
}
if maxState != setMax {
t.Errorf("max state incorrect, expected %s, "+
"received %s", setMax, maxState)
}
didRun[localI] = true
wg.Done()
}
m.RegisterUpdateCallback(groupName, cb)
}
}
for i := Mute; i <= Push; i++ {
setMax = i
for j := versioned.Created; j <= versioned.Deleted; j++ {
ch := make(chan bool)
didRun = make([]bool, numGroups)
if j != versioned.Deleted {
wg.Add(numCB)
}
go func() {
defer func() {
if r := recover(); r != nil {
ch <- false
}
}()
mInternal.maxStateUpdate(maxStateKey, nil,
makeMaxStateObj(i, t), j)
ch <- true
}()
result := <-ch
if j == versioned.Deleted && result {
t.Errorf("did not panic on deletion")
} else if j != versioned.Deleted && !result {
t.Errorf("panicked on non deletion")
}
if j != versioned.Deleted {
wg.Wait()
if mInternal.maxState != i {
t.Errorf("max state not set to %s, is %s", i, mInternal.maxState)
}
if !reflect.DeepEqual(expectedRun, didRun) {
t.Errorf("wrong callbacks were called, %+v", didRun)
}
}
}
}
}
func makeMaxStateObj(max NotificationState, t *testing.T) *versioned.Object {
b, err := json.Marshal(&max)
if err != nil {
t.Fatalf("Failed to marshal max state: %+v", err)
}
return &versioned.Object{
Version: maxStateKetVersion,
Timestamp: time.Now(),
Data: b,
}
}
func TestManager_setLoadMaxState(t *testing.T) {
m, _, _ := buildTestingManager(t)
mInternal := m.(*manager)
mInternal.maxState = -1
tests := []NotificationState{Mute, WhenOpen, Push}
for _, ns := range tests {
mInternal.maxState = -1
mInternal.setMaxStateUnsafe(ns)
if mInternal.maxState != ns {
t.Errorf("In ram max state did not take with set")
}
mInternal.maxState = -1
obj, err := mInternal.remote.Get(maxStateKey, maxStateKetVersion)
if err != nil {
t.Errorf("Failed to get max state %s from ekv: %+v", ns, err)
}
mInternal.loadMaxStateUnsafe(obj)
if mInternal.maxState != ns {
t.Errorf("In ram max state did not take with load of %s",
string(obj.Data))
}
}
}
func TestManager_upsertNotificationUnsafeRAM(t *testing.T) { func TestManager_upsertNotificationUnsafeRAM(t *testing.T) {
m, _, _ := buildTestingManager(t) m, _, _ := buildTestingManager(t)
mInternal := m.(*manager) mInternal := m.(*manager)
...@@ -645,6 +762,7 @@ type commsMock struct { ...@@ -645,6 +762,7 @@ type commsMock struct {
receivedMessage interface{} receivedMessage interface{}
returnedMessage *messages.Ack returnedMessage *messages.Ack
returnedError error returnedError error
numRuns int
} }
func initCommsMock() *commsMock { func initCommsMock() *commsMock {
...@@ -664,6 +782,7 @@ func (cm *commsMock) RegisterToken(host *connect.Host, ...@@ -664,6 +782,7 @@ func (cm *commsMock) RegisterToken(host *connect.Host,
message *pb.RegisterTokenRequest) (*messages.Ack, error) { message *pb.RegisterTokenRequest) (*messages.Ack, error) {
cm.receivedHost = host cm.receivedHost = host
cm.receivedMessage = message cm.receivedMessage = message
cm.numRuns++
return cm.returnedMessage, cm.returnedError return cm.returnedMessage, cm.returnedError
} }
...@@ -671,6 +790,7 @@ func (cm *commsMock) UnregisterToken(host *connect.Host, ...@@ -671,6 +790,7 @@ func (cm *commsMock) UnregisterToken(host *connect.Host,
message *pb.UnregisterTokenRequest) (*messages.Ack, error) { message *pb.UnregisterTokenRequest) (*messages.Ack, error) {
cm.receivedHost = host cm.receivedHost = host
cm.receivedMessage = message cm.receivedMessage = message
cm.numRuns++
return cm.returnedMessage, cm.returnedError return cm.returnedMessage, cm.returnedError
} }
...@@ -678,6 +798,7 @@ func (cm *commsMock) RegisterTrackedID(host *connect.Host, ...@@ -678,6 +798,7 @@ func (cm *commsMock) RegisterTrackedID(host *connect.Host,
message *pb.RegisterTrackedIdRequest) (*messages.Ack, error) { message *pb.RegisterTrackedIdRequest) (*messages.Ack, error) {
cm.receivedHost = host cm.receivedHost = host
cm.receivedMessage = message cm.receivedMessage = message
cm.numRuns++
return cm.returnedMessage, cm.returnedError return cm.returnedMessage, cm.returnedError
} }
...@@ -685,6 +806,7 @@ func (cm *commsMock) UnregisterTrackedID(host *connect.Host, ...@@ -685,6 +806,7 @@ func (cm *commsMock) UnregisterTrackedID(host *connect.Host,
message *pb.UnregisterTrackedIdRequest) (*messages.Ack, error) { message *pb.UnregisterTrackedIdRequest) (*messages.Ack, error) {
cm.receivedHost = host cm.receivedHost = host
cm.receivedMessage = message cm.receivedMessage = message
cm.numRuns++
return cm.returnedMessage, cm.returnedError return cm.returnedMessage, cm.returnedError
} }
......
package notifications
import (
"encoding/json"
pb "gitlab.com/elixxir/comms/mixmessages"
"gitlab.com/xx_network/primitives/id"
"testing"
)
func TestManager_SetMaxState(t *testing.T) {
m, _, comms := buildTestingManager(t)
mInternal := m.(*manager)
expectedLen := int(Push) + 1
comms.reset()
for i := Mute; i <= Push; i++ {
for x := 0; x < int(i)+1; x++ {
nid := id.NewIdFromUInt(uint64(int(i)*100+x), id.User, t)
if err := m.Set(nid, "a", []byte{0}, i); err != nil {
t.Errorf("errored in set: %+v", err)
}
}
}
if err := m.SetMaxState(Mute); err != nil {
t.Fatalf("errored in setMaxState: %+v", err)
}
unReg := comms.receivedMessage.(*pb.UnregisterTrackedIdRequest)
if len(unReg.Request.TrackedIntermediaryID) != expectedLen {
t.Errorf("wrong number of ids unregistered")
}
if mInternal.maxState != Mute {
t.Errorf("max state at wrong state internally")
}
if loadMaxState(mInternal, t) != Mute {
t.Errorf("max state at wrong state in ekv")
}
comms.reset()
if err := m.SetMaxState(WhenOpen); err != nil {
t.Fatalf("errored in setMaxState: %+v", err)
}
if comms.receivedMessage != nil {
t.Errorf("message sent when it shouldnt be!")
}
if mInternal.maxState != WhenOpen {
t.Errorf("max state at wrong state internally")
}
if loadMaxState(mInternal, t) != WhenOpen {
t.Errorf("max state at wrong state in ekv")
}
comms.reset()
if err := m.SetMaxState(Push); err != nil {
t.Fatalf("errored in setMaxState: %+v", err)
}
reg := comms.receivedMessage.(*pb.RegisterTrackedIdRequest)
if len(reg.Request.TrackedIntermediaryID) != expectedLen {
t.Errorf("wrong number of ids unregistered")
}
if mInternal.maxState != Push {
t.Errorf("max state at wrong state internally")
}
if loadMaxState(mInternal, t) != Push {
t.Errorf("max state at wrong state in ekv")
}
}
func TestManager_GetMaxState(t *testing.T) {
m, _, _ := buildTestingManager(t)
mInternal := m.(*manager)
for i := Mute; i <= Push; i++ {
mInternal.maxState = i
got := m.GetMaxState()
if i != got {
t.Errorf("get didnt match value")
}
}
}
func loadMaxState(m *manager, t *testing.T) NotificationState {
obj, err := m.remote.Get(maxStateKey, maxStateKetVersion)
if err != nil {
t.Fatalf("could not get max state: %+v", err)
}
var ms NotificationState
err = json.Unmarshal(obj.Data, &ms)
if err != nil {
t.Fatalf("could not get max state: %+v", err)
}
return ms
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment