1// Copyright 2019 The Gitea Authors. All rights reserved.
2// SPDX-License-Identifier: MIT
3
4package git
5
6import (
7 "context"
8 "fmt"
9 "os"
10 "strings"
11 "testing"
12
13 "forgejo.org/modules/setting"
14 "forgejo.org/modules/test"
15 "forgejo.org/modules/util"
16
17 "github.com/hashicorp/go-version"
18 "github.com/stretchr/testify/assert"
19 "github.com/stretchr/testify/require"
20)
21
22func testRun(m *testing.M) error {
23 gitHomePath, err := os.MkdirTemp(os.TempDir(), "git-home")
24 if err != nil {
25 return fmt.Errorf("unable to create temp dir: %w", err)
26 }
27 defer util.RemoveAll(gitHomePath)
28 setting.Git.HomePath = gitHomePath
29
30 if err = InitFull(context.Background()); err != nil {
31 return fmt.Errorf("failed to call Init: %w", err)
32 }
33
34 exitCode := m.Run()
35 if exitCode != 0 {
36 return fmt.Errorf("run test failed, ExitCode=%d", exitCode)
37 }
38 return nil
39}
40
41func TestMain(m *testing.M) {
42 if err := testRun(m); err != nil {
43 _, _ = fmt.Fprintf(os.Stderr, "Test failed: %v", err)
44 os.Exit(1)
45 }
46}
47
48func gitConfigContains(sub string) bool {
49 if b, err := os.ReadFile(HomeDir() + "/.gitconfig"); err == nil {
50 return strings.Contains(string(b), sub)
51 }
52 return false
53}
54
55func TestGitConfig(t *testing.T) {
56 assert.False(t, gitConfigContains("key-a"))
57
58 require.NoError(t, configSetNonExist("test.key-a", "val-a"))
59 assert.True(t, gitConfigContains("key-a = val-a"))
60
61 require.NoError(t, configSetNonExist("test.key-a", "val-a-changed"))
62 assert.False(t, gitConfigContains("key-a = val-a-changed"))
63
64 require.NoError(t, configSet("test.key-a", "val-a-changed"))
65 assert.True(t, gitConfigContains("key-a = val-a-changed"))
66
67 require.NoError(t, configAddNonExist("test.key-b", "val-b"))
68 assert.True(t, gitConfigContains("key-b = val-b"))
69
70 require.NoError(t, configAddNonExist("test.key-b", "val-2b"))
71 assert.True(t, gitConfigContains("key-b = val-b"))
72 assert.True(t, gitConfigContains("key-b = val-2b"))
73
74 require.NoError(t, configUnsetAll("test.key-b", "val-b"))
75 assert.False(t, gitConfigContains("key-b = val-b"))
76 assert.True(t, gitConfigContains("key-b = val-2b"))
77
78 require.NoError(t, configUnsetAll("test.key-b", "val-2b"))
79 assert.False(t, gitConfigContains("key-b = val-2b"))
80
81 require.NoError(t, configSet("test.key-x", "*"))
82 assert.True(t, gitConfigContains("key-x = *"))
83 require.NoError(t, configSetNonExist("test.key-x", "*"))
84 require.NoError(t, configUnsetAll("test.key-x", "*"))
85 assert.False(t, gitConfigContains("key-x = *"))
86}
87
88func TestSyncConfig(t *testing.T) {
89 oldGitConfig := setting.GitConfig
90 defer func() {
91 setting.GitConfig = oldGitConfig
92 }()
93
94 setting.GitConfig.Options["sync-test.cfg-key-a"] = "CfgValA"
95 require.NoError(t, syncGitConfig())
96 assert.True(t, gitConfigContains("[sync-test]"))
97 assert.True(t, gitConfigContains("cfg-key-a = CfgValA"))
98}
99
100func TestSyncConfigGPGFormat(t *testing.T) {
101 defer test.MockProtect(&setting.GitConfig)()
102
103 t.Run("No format", func(t *testing.T) {
104 defer test.MockVariableValue(&setting.Repository.Signing.Format, "")()
105 require.NoError(t, syncGitConfig())
106 assert.True(t, gitConfigContains("[gpg]"))
107 assert.True(t, gitConfigContains("format = openpgp"))
108 })
109
110 t.Run("SSH format", func(t *testing.T) {
111 r, err := os.OpenRoot(t.TempDir())
112 require.NoError(t, err)
113 f, err := r.OpenFile("ssh-keygen", os.O_CREATE|os.O_TRUNC, 0o700)
114 require.NoError(t, f.Close())
115 require.NoError(t, err)
116 t.Setenv("PATH", r.Name())
117 defer test.MockVariableValue(&setting.Repository.Signing.Format, "ssh")()
118
119 require.NoError(t, syncGitConfig())
120 assert.True(t, gitConfigContains("[gpg]"))
121 assert.True(t, gitConfigContains("format = ssh"))
122
123 t.Run("Old version", func(t *testing.T) {
124 oldVersion, err := version.NewVersion("2.33.0")
125 require.NoError(t, err)
126 defer test.MockVariableValue(&gitVersion, oldVersion)()
127 require.ErrorContains(t, syncGitConfig(), "ssh signing requires Git >= 2.34.0")
128 })
129
130 t.Run("No ssh-keygen binary", func(t *testing.T) {
131 require.NoError(t, r.Remove("ssh-keygen"))
132 require.ErrorContains(t, syncGitConfig(), "git signing requires a ssh-keygen binary")
133 })
134
135 t.Run("Dynamic ssh-keygen binary location", func(t *testing.T) {
136 f, err := r.OpenFile("ssh-keygen-2", os.O_CREATE|os.O_TRUNC, 0o700)
137 require.NoError(t, f.Close())
138 require.NoError(t, err)
139 defer test.MockVariableValue(&setting.GitConfig.Options, map[string]string{
140 "gpg.ssh.program": "ssh-keygen-2",
141 })()
142 require.NoError(t, syncGitConfig())
143 })
144 })
145
146 t.Run("OpenPGP format", func(t *testing.T) {
147 defer test.MockVariableValue(&setting.Repository.Signing.Format, "openpgp")()
148 require.NoError(t, syncGitConfig())
149 assert.True(t, gitConfigContains("[gpg]"))
150 assert.True(t, gitConfigContains("format = openpgp"))
151 })
152}