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