1package ssh
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "log"
8 "os"
9 "testing"
10 "time"
11
12 "github.com/charmbracelet/soft-serve/server/backend"
13 "github.com/charmbracelet/soft-serve/server/backend/sqlite"
14 "github.com/charmbracelet/soft-serve/server/cache"
15 _ "github.com/charmbracelet/soft-serve/server/cache/noop" // noop driver
16 "github.com/charmbracelet/soft-serve/server/test"
17 "github.com/charmbracelet/ssh"
18 bm "github.com/charmbracelet/wish/bubbletea"
19 "github.com/charmbracelet/wish/testsession"
20 "github.com/matryer/is"
21 "github.com/muesli/termenv"
22 gossh "golang.org/x/crypto/ssh"
23)
24
25func TestSession(t *testing.T) {
26 is := is.New(t)
27 t.Run("authorized repo access", func(t *testing.T) {
28 t.Log("setting up")
29 s, close := setup(t)
30 s.Stderr = os.Stderr
31 t.Log("requesting pty")
32 err := s.RequestPty("xterm", 80, 40, nil)
33 is.NoErr(err)
34 go func() {
35 time.Sleep(1 * time.Second)
36 s.Signal(gossh.SIGTERM)
37 // FIXME: exit with code 0 instead of forcibly closing the session
38 s.Close()
39 }()
40 t.Log("waiting for session to exit")
41 _, err = s.Output("test")
42 var ee *gossh.ExitMissingError
43 is.True(errors.As(err, &ee))
44 t.Log("session exited")
45 _ = close()
46 })
47}
48
49func setup(tb testing.TB) (*gossh.Session, func() error) {
50 tb.Helper()
51 is := is.New(tb)
52 dp := tb.TempDir()
53 is.NoErr(os.Setenv("SOFT_SERVE_DATA_PATH", dp))
54 is.NoErr(os.Setenv("SOFT_SERVE_GIT_LISTEN_ADDR", ":9418"))
55 is.NoErr(os.Setenv("SOFT_SERVE_SSH_LISTEN_ADDR", fmt.Sprintf(":%d", test.RandomPort())))
56 tb.Cleanup(func() {
57 is.NoErr(os.Unsetenv("SOFT_SERVE_DATA_PATH"))
58 is.NoErr(os.Unsetenv("SOFT_SERVE_GIT_LISTEN_ADDR"))
59 is.NoErr(os.Unsetenv("SOFT_SERVE_SSH_LISTEN_ADDR"))
60 is.NoErr(os.RemoveAll(dp))
61 })
62 ctx := context.TODO()
63 ca, _ := cache.New(ctx, "noop")
64 ctx = cache.WithContext(ctx, ca)
65 fb, err := sqlite.NewSqliteBackend(ctx)
66 if err != nil {
67 log.Fatal(err)
68 }
69 ctx = backend.WithContext(ctx, fb)
70 return testsession.New(tb, &ssh.Server{
71 Handler: bm.MiddlewareWithProgramHandler(SessionHandler(fb), termenv.ANSI256)(func(s ssh.Session) {
72 _, _, active := s.Pty()
73 if !active {
74 os.Exit(1)
75 }
76 s.Exit(0)
77 }),
78 }, nil), fb.Close
79}