diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-08-30 18:46:43 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-08-30 18:46:43 +0900 |
| commit | f656968350be67b3458db01a8d75a8216f3238a8 (patch) | |
| tree | 74ec7b67f66c642804323f4e59c2f796e3758f85 /internal/store/segment_test.go | |
| parent | d4fab811d3857c36f1d8271a940ffca113e012b7 (diff) | |
internal/store: destroy stale instances
This recovers from inconsistent state on power loss and /tmp/ is not wiped on startup, or if hakurei somehow crashes.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/store/segment_test.go')
| -rw-r--r-- | internal/store/segment_test.go | 78 |
1 files changed, 69 insertions, 9 deletions
diff --git a/internal/store/segment_test.go b/internal/store/segment_test.go index a244fcdc..6316a278 100644 --- a/internal/store/segment_test.go +++ b/internal/store/segment_test.go @@ -2,6 +2,7 @@ package store_test import ( "errors" + "fmt" "io" "iter" "os" @@ -55,7 +56,7 @@ func TestStateEntryHandle(t *testing.T) { if err := save(&eh, nil); !reflect.DeepEqual(err, wantErr()) { t.Errorf("save: error = %v, want %v", err, wantErr()) } - if _, err := eh.Load(nil); !reflect.DeepEqual(err, wantErr()) { + if _, err := eh.Load(nil, nil); !reflect.DeepEqual(err, wantErr()) { t.Errorf("load: error = %v, want %v", err, wantErr()) } }) @@ -95,8 +96,10 @@ func TestStateEntryHandle(t *testing.T) { t.Run("saveload", func(t *testing.T) { t.Parallel() - eh := store.EntryHandle{Pathname: check.MustAbs(t.TempDir()).Append("entry"), - ID: store.NewTemplateState().ID} + eh := store.EntryHandle{ + Pathname: check.MustAbs(t.TempDir()).Append("entry"), + ID: store.NewTemplateState().ID, + } if err := save(&eh, store.NewTemplateState()); err != nil { t.Fatalf("save: error = %v", err) @@ -125,7 +128,7 @@ func TestStateEntryHandle(t *testing.T) { t.Run("load header only", func(t *testing.T) { t.Parallel() - if et, err := eh.Load(nil); err != nil { + if et, err := eh.Load(nil, nil); err != nil { t.Fatalf("load: error = %v", err) } else if want := store.NewTemplateState().Enablements.Unwrap(); et != want { t.Errorf("load: et = %x, want %x", et, want) @@ -136,7 +139,7 @@ func TestStateEntryHandle(t *testing.T) { t.Parallel() var got hst.State - if _, err := eh.Load(&got); err != nil { + if _, err := eh.Load(&got, nil); err != nil { t.Fatalf("load: error = %v", err) } else if want := store.NewTemplateState(); !reflect.DeepEqual(&got, want) { t.Errorf("load: %#v, want %#v", &got, want) @@ -145,11 +148,64 @@ func TestStateEntryHandle(t *testing.T) { t.Run("load inconsistent", func(t *testing.T) { t.Parallel() - wantErr := &hst.AppError{Step: "validate state identifier", Err: os.ErrInvalid, - Msg: "state entry 00000000000000000000000000000000 has unexpected id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"} + wantErr := &hst.AppError{ + Step: "validate state identifier", + Err: os.ErrInvalid, + Msg: "state entry 00000000000000000000000000000000 has unexpected id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + } ehi := store.EntryHandle{Pathname: eh.Pathname} - if _, err := ehi.Load(new(hst.State)); !reflect.DeepEqual(err, wantErr) { + if _, err := ehi.Load(new(hst.State), nil); !reflect.DeepEqual(err, wantErr) { + t.Errorf("load: error = %#v, want %#v", err, wantErr) + } + }) + + t.Run("stale fault", func(t *testing.T) { + t.Parallel() + wantErr := &hst.AppError{ + Step: "check monitor process", + Err: syscall.EFAULT, + } + + if _, err := eh.Load(new(hst.State), func(pid int, sig syscall.Signal) (err error) { + if pid != store.NewTemplateState().PID { + return fmt.Errorf("bad pid %d", pid) + } + if sig != 0 { + return fmt.Errorf("bad signal %d", sig) + } + return syscall.EFAULT + }); !reflect.DeepEqual(err, wantErr) { + t.Errorf("load: error = %#v, want %#v", err, wantErr) + } + }) + + t.Run("stale", func(t *testing.T) { + t.Parallel() + wantErr := &hst.AppError{ + Step: "load stale entry", + Err: syscall.ESRCH, + Msg: "stale entry aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + } + + ehi := store.EntryHandle{ + Pathname: check.MustAbs(t.TempDir()).Append("entry"), + ID: eh.ID, + } + + if err := save(&ehi, store.NewTemplateState()); err != nil { + t.Fatalf("save: error = %v", err) + } + + if _, err := ehi.Load(new(hst.State), func(pid int, sig syscall.Signal) (err error) { + if pid != store.NewTemplateState().PID { + return fmt.Errorf("bad pid %d", pid) + } + if sig != 0 { + return fmt.Errorf("bad signal %d", sig) + } + return syscall.ESRCH + }); !reflect.DeepEqual(err, wantErr) { t.Errorf("load: error = %#v, want %#v", err, wantErr) } }) @@ -229,7 +285,9 @@ func TestSegmentHandle(t *testing.T) { } } - slices.SortFunc(got, func(a, b *store.EntryHandle) int { return strings.Compare(a.Pathname.String(), b.Pathname.String()) }) + slices.SortFunc(got, func(a, b *store.EntryHandle) int { + return strings.Compare(a.Pathname.String(), b.Pathname.String()) + }) want := tc.want(func(err error, name string) *store.EntryHandle { eh := store.EntryHandle{DecodeErr: err, Pathname: segment.Append(name)} if err == nil { @@ -247,6 +305,8 @@ func TestSegmentHandle(t *testing.T) { } t.Run("nonexistent", func(t *testing.T) { + t.Parallel() + var wantErr = &hst.AppError{Step: "read store segment entries", Err: &os.PathError{ Op: "open", Path: "/proc/nonexistent", |
