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