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