diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-10-18 20:45:42 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-10-18 20:45:42 +0900 |
| commit | a103c4a7c70f67faa33665cd886545ddc7e09c89 (patch) | |
| tree | 6bcefae7da52c2aa89ee059f84085cfbc149a413 /internal/app/hsu_test.go | |
| parent | 67ec82ae1b60c9f9db0f15dd06e0246312343bea (diff) | |
internal/app/hsu: check behaviour
The stub exec.ExitError is hairy as usual, but internal/app is not cross-platform, so this is okay.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/app/hsu_test.go')
| -rw-r--r-- | internal/app/hsu_test.go | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/internal/app/hsu_test.go b/internal/app/hsu_test.go new file mode 100644 index 00000000..fad724da --- /dev/null +++ b/internal/app/hsu_test.go @@ -0,0 +1,84 @@ +package app + +import ( + "os" + "os/exec" + "reflect" + "strconv" + "syscall" + "testing" + "unsafe" + + "hakurei.app/container/stub" + "hakurei.app/hst" +) + +func TestHsu(t *testing.T) { + t.Parallel() + + t.Run("ensure dispatcher", func(t *testing.T) { + hsu := new(Hsu) + hsu.ensureDispatcher() + + k := direct{} + if !reflect.DeepEqual(hsu.k, k) { + t.Errorf("ensureDispatcher: k = %#v, want %#v", hsu.k, k) + } + }) + + fCheckID := func(k *kstub) error { + hsu := &Hsu{k: k} + id, err := hsu.ID() + k.Verbose(id) + if id0, err0 := hsu.ID(); id0 != id || !reflect.DeepEqual(err0, err) { + t.Fatalf("ID: id0 = %d, err0 = %#v, id = %d, err = %#v", id0, err0, id, err) + } + return err + } + + checkSimple(t, "Hsu.ID", []simpleTestCase{ + {"hsu nonexistent", fCheckID, stub.Expect{Calls: []stub.Call{ + call("mustHsuPath", stub.ExpectArgs{}, m("/run/wrappers/bin/hsu"), nil), + call("cmdOutput", stub.ExpectArgs{"/run/wrappers/bin/hsu", os.Stderr, []string{}, "/"}, ([]byte)(nil), os.ErrNotExist), + call("verbose", stub.ExpectArgs{[]any{-1}}, nil, nil), + }}, &hst.AppError{ + Step: "obtain uid from hsu", + Err: os.ErrNotExist, + Msg: "the setuid helper is missing: /run/wrappers/bin/hsu", + }}, + + {"access", fCheckID, stub.Expect{Calls: []stub.Call{ + call("mustHsuPath", stub.ExpectArgs{}, m("/run/wrappers/bin/hsu"), nil), + call("cmdOutput", stub.ExpectArgs{"/run/wrappers/bin/hsu", os.Stderr, []string{}, "/"}, ([]byte)(nil), makeExitError(1<<8)), + call("verbose", stub.ExpectArgs{[]any{-1}}, nil, nil), + }}, &hst.AppError{ + Step: "obtain uid from hsu", + Err: ErrHsuAccess, + }}, + + {"invalid output", fCheckID, stub.Expect{Calls: []stub.Call{ + call("mustHsuPath", stub.ExpectArgs{}, m("/run/wrappers/bin/hsu"), nil), + call("cmdOutput", stub.ExpectArgs{"/run/wrappers/bin/hsu", os.Stderr, []string{}, "/"}, []byte{0}, nil), + call("verbose", stub.ExpectArgs{[]any{0}}, nil, nil), + }}, &hst.AppError{ + Step: "obtain uid from hsu", + Err: &strconv.NumError{Func: "Atoi", Num: "\x00", Err: strconv.ErrSyntax}, + Msg: "invalid uid string from hsu", + }}, + + {"success", fCheckID, stub.Expect{Calls: []stub.Call{ + call("mustHsuPath", stub.ExpectArgs{}, m("/run/wrappers/bin/hsu"), nil), + call("cmdOutput", stub.ExpectArgs{"/run/wrappers/bin/hsu", os.Stderr, []string{}, "/"}, []byte{'0'}, nil), + call("verbose", stub.ExpectArgs{[]any{0}}, nil, nil), + }}, nil}, + }) +} + +// makeExitError populates syscall.WaitStatus in an [exec.ExitError]. +// Do not reuse this function in a cross-platform package. +func makeExitError(status syscall.WaitStatus) error { + ps := new(os.ProcessState) + statusV := reflect.ValueOf(ps).Elem().FieldByName("status") + *reflect.NewAt(statusV.Type(), unsafe.Pointer(statusV.UnsafeAddr())).Interface().(*syscall.WaitStatus) = status + return &exec.ExitError{ProcessState: ps} +} |
