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