aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/state/state_test.go
diff options
context:
space:
mode:
authorOphestra Umiker <cat@ophivana.moe>2024-12-19 21:36:17 +0900
committerOphestra Umiker <cat@ophivana.moe>2024-12-19 21:36:17 +0900
commiteae3034260b3abbb8da1deeddfef135b36eeb390 (patch)
treea3dbc5d679a1137018ccec0886c68853e679b7bb /internal/state/state_test.go
parent5ea7333431942780c79c02a990682e44b6834ed6 (diff)
state: expose aids and use instance id as key
Fortify state store instances was specific to aids due to outdated design decisions carried over from the ego rewrite. That no longer makes sense in the current application, so the interface now enables a single store object to manage all transient state. Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
Diffstat (limited to 'internal/state/state_test.go')
-rw-r--r--internal/state/state_test.go126
1 files changed, 126 insertions, 0 deletions
diff --git a/internal/state/state_test.go b/internal/state/state_test.go
new file mode 100644
index 00000000..0a4ba57b
--- /dev/null
+++ b/internal/state/state_test.go
@@ -0,0 +1,126 @@
+package state_test
+
+import (
+ "math/rand/v2"
+ "reflect"
+ "slices"
+ "testing"
+ "time"
+
+ "git.ophivana.moe/security/fortify/fst"
+ "git.ophivana.moe/security/fortify/internal/state"
+)
+
+func testStore(t *testing.T, s state.Store) {
+ t.Run("list empty store", func(t *testing.T) {
+ if aids, err := s.List(); err != nil {
+ t.Fatalf("List: error = %v", err)
+ } else if len(aids) != 0 {
+ t.Fatalf("List: aids = %#v", aids)
+ }
+ })
+
+ const (
+ insertEntryChecked = iota
+ insertEntryNoCheck
+ insertEntryOtherApp
+
+ tl
+ )
+
+ var tc [tl]state.State
+ for i := 0; i < tl; i++ {
+ makeState(t, &tc[i])
+ }
+
+ do := func(aid int, f func(c state.Cursor)) {
+ if ok, err := s.Do(aid, f); err != nil {
+ t.Fatalf("Do: ok = %v, error = %v", ok, err)
+ }
+ }
+
+ insert := func(i, aid int) {
+ do(aid, func(c state.Cursor) {
+ if err := c.Save(&tc[i]); err != nil {
+ t.Fatalf("Save(&tc[%v]): error = %v", i, err)
+ }
+ })
+ }
+
+ check := func(i, aid int) {
+ do(aid, func(c state.Cursor) {
+ if entries, err := c.Load(); err != nil {
+ t.Fatalf("Load: error = %v", err)
+ } else if got, ok := entries[tc[i].ID]; !ok {
+ t.Fatalf("Load: entry %s missing",
+ &tc[i].ID)
+ } else {
+ got.Time = tc[i].Time
+ if !reflect.DeepEqual(got, &tc[i]) {
+ t.Fatalf("Load: entry %s got %#v, want %#v",
+ &tc[i].ID, got, &tc[i])
+ }
+ }
+ })
+ }
+
+ t.Run("insert entry checked", func(t *testing.T) {
+ insert(insertEntryChecked, 0)
+ check(insertEntryChecked, 0)
+ })
+
+ t.Run("insert entry unchecked", func(t *testing.T) {
+ insert(insertEntryNoCheck, 0)
+ })
+
+ t.Run("insert entry different aid", func(t *testing.T) {
+ insert(insertEntryOtherApp, 1)
+ check(insertEntryOtherApp, 1)
+ })
+
+ t.Run("check previous insertion", func(t *testing.T) {
+ check(insertEntryNoCheck, 0)
+ })
+
+ t.Run("list aids", func(t *testing.T) {
+ if aids, err := s.List(); err != nil {
+ t.Fatalf("List: error = %v", err)
+ } else {
+ slices.Sort(aids)
+ want := []int{0, 1}
+ if slices.Compare(aids, want) != 0 {
+ t.Fatalf("List() = %#v, want %#v", aids, want)
+ }
+ }
+ })
+
+ t.Run("clear aid 1", func(t *testing.T) {
+ do(1, func(c state.Cursor) {
+ if err := c.Destroy(tc[insertEntryOtherApp].ID); err != nil {
+ t.Fatalf("Destroy: error = %v", err)
+ }
+ })
+ do(1, func(c state.Cursor) {
+ if l, err := c.Len(); err != nil {
+ t.Fatalf("Len: error = %v", err)
+ } else if l != 0 {
+ t.Fatalf("Len() = %d, want 0", l)
+ }
+ })
+ })
+
+ t.Run("close store", func(t *testing.T) {
+ if err := s.Close(); err != nil {
+ t.Fatalf("Close: error = %v", err)
+ }
+ })
+}
+
+func makeState(t *testing.T, s *state.State) {
+ if err := fst.NewAppID(&s.ID); err != nil {
+ t.Fatalf("cannot create dummy state: %v", err)
+ }
+ s.Config = fst.Template()
+ s.PID = rand.Int()
+ s.Time = time.Now()
+}