1package server
2
3import (
4 "fmt"
5 "net"
6 "os"
7 "path/filepath"
8 "strconv"
9 "strings"
10 "testing"
11
12 "github.com/charmbracelet/keygen"
13 "github.com/charmbracelet/soft-serve/server/config"
14 "github.com/gliderlabs/ssh"
15 "github.com/go-git/go-billy/v5/memfs"
16 "github.com/go-git/go-git/v5"
17 gconfig "github.com/go-git/go-git/v5/config"
18 "github.com/go-git/go-git/v5/plumbing/object"
19 gssh "github.com/go-git/go-git/v5/plumbing/transport/ssh"
20 "github.com/go-git/go-git/v5/storage/memory"
21 "github.com/matryer/is"
22 gossh "golang.org/x/crypto/ssh"
23)
24
25func TestPushRepo(t *testing.T) {
26 is := is.New(t)
27 s, cfg, pkPath := setupServer(t)
28 err := s.Reload()
29 is.NoErr(err)
30 rp := t.TempDir()
31 r, err := git.PlainInit(rp, false)
32 is.NoErr(err)
33 wt, err := r.Worktree()
34 is.NoErr(err)
35 _, err = wt.Filesystem.Create("testfile")
36 is.NoErr(err)
37 _, err = wt.Add("testfile")
38 is.NoErr(err)
39 author := &object.Signature{
40 Name: "test",
41 Email: "",
42 }
43 _, err = wt.Commit("test commit", &git.CommitOptions{
44 All: true,
45 Author: author,
46 Committer: author,
47 })
48 is.NoErr(err)
49 _, err = r.CreateRemote(&gconfig.RemoteConfig{
50 Name: "origin",
51 URLs: []string{fmt.Sprintf("ssh://localhost:%d/%s", cfg.SSH.Port, "testrepo")},
52 })
53 auth, err := gssh.NewPublicKeysFromFile("git", pkPath, "")
54 is.NoErr(err)
55 auth.HostKeyCallbackHelper = gssh.HostKeyCallbackHelper{
56 HostKeyCallback: gossh.InsecureIgnoreHostKey(),
57 }
58 t.Logf("pushing to ssh://localhost:%d/%s", cfg.SSH.Port, "testrepo")
59 err = r.Push(&git.PushOptions{
60 RemoteName: "origin",
61 Auth: auth,
62 })
63 is.NoErr(err)
64}
65
66func TestCloneRepo(t *testing.T) {
67 is := is.New(t)
68 s, cfg, pkPath := setupServer(t)
69 t.Log("starting server")
70 err := s.Reload()
71 t.Log("reloaded server")
72 is.NoErr(err)
73 dst := t.TempDir()
74 t.Cleanup(func() { is.NoErr(os.RemoveAll(dst)) })
75 url := fmt.Sprintf("ssh://localhost:%d/config", cfg.SSH.Port)
76 t.Log("cloning repo")
77 pk, err := gssh.NewPublicKeysFromFile("git", pkPath, "")
78 is.NoErr(err)
79 pk.HostKeyCallbackHelper = gssh.HostKeyCallbackHelper{
80 HostKeyCallback: gossh.InsecureIgnoreHostKey(),
81 }
82 _, err = git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
83 URL: url,
84 Auth: pk,
85 })
86 is.NoErr(err)
87}
88
89func randomPort() int {
90 addr, _ := net.Listen("tcp", ":0") //nolint:gosec
91 _ = addr.Close()
92 return addr.Addr().(*net.TCPAddr).Port
93}
94
95func setupServer(t *testing.T) (*Server, *config.Config, string) {
96 t.Helper()
97 is := is.New(t)
98 pub, pkPath := createKeyPair(t)
99 dp := t.TempDir()
100 is.NoErr(os.Setenv("SOFT_SERVE_DATA_PATH", dp))
101 is.NoErr(os.Setenv("SOFT_SERVE_INITIAL_ADMIN_KEY", authorizedKey(pub)))
102 is.NoErr(os.Setenv("SOFT_SERVE_GIT_ENABLED", "false"))
103 is.NoErr(os.Setenv("SOFT_SERVE_SSH_PORT", strconv.Itoa(randomPort())))
104 // is.NoErr(os.Setenv("SOFT_SERVE_DB_DRIVER", "fake"))
105 t.Cleanup(func() {
106 is.NoErr(os.Unsetenv("SOFT_SERVE_DATA_PATH"))
107 is.NoErr(os.Unsetenv("SOFT_SERVE_SSH_PORT"))
108 is.NoErr(os.Unsetenv("SOFT_SERVE_INITIAL_ADMIN_KEY"))
109 is.NoErr(os.Unsetenv("SOFT_SERVE_GIT_ENABLED"))
110 // is.NoErr(os.Unsetenv("SOFT_SERVE_DB_DRIVER"))
111 is.NoErr(os.RemoveAll(dp))
112 })
113 cfg := config.DefaultConfig() //.WithDB(&fakedb.FakeDB{})
114 s := NewServer(cfg)
115 go func() {
116 t.Log("starting server")
117 s.Start()
118 }()
119 t.Cleanup(func() {
120 s.Close()
121 })
122 return s, cfg, pkPath
123}
124
125func createKeyPair(t *testing.T) (ssh.PublicKey, string) {
126 t.Helper()
127 is := is.New(t)
128 keyDir := t.TempDir()
129 kp, err := keygen.NewWithWrite(filepath.Join(keyDir, "id"), nil, keygen.Ed25519)
130 is.NoErr(err)
131 pubkey, _, _, _, err := ssh.ParseAuthorizedKey(kp.PublicKey())
132 is.NoErr(err)
133 return pubkey, filepath.Join(keyDir, "id_ed25519")
134}
135
136func authorizedKey(pk ssh.PublicKey) string {
137 return strings.TrimSpace(string(gossh.MarshalAuthorizedKey(pk)))
138}