Test the fetch of one commit over SSH (issue 1474), part 6 of 7
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
//go:build integration
|
||||
|
||||
package buildstep
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
func gitIn(t *testing.T, dir string, args ...string) string {
|
||||
t.Helper()
|
||||
cmd := exec.Command("git", append([]string{"-C", dir, "-c", "user.name=t", "-c", "user.email=t@example.invalid"}, args...)...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("git %v: %v\n%s", args, err, out)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
// 1474/AC5: a local bare repository with a commit only under refs/pull/1/head (a file:// remote, allowed by the
|
||||
// test-only option): the workspace's HEAD is the commit. The forge itself is proven live after the build
|
||||
// modules' delivery (ADR 0466 §5).
|
||||
func TestFetchCommitFromAPullRefOnly(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
bare := filepath.Join(root, "r.git")
|
||||
work := filepath.Join(root, "work")
|
||||
for _, d := range []string{bare, work} {
|
||||
if err := os.MkdirAll(d, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
gitIn(t, bare, "init", "-q", "--bare")
|
||||
gitIn(t, work, "init", "-q")
|
||||
if err := os.WriteFile(filepath.Join(work, "f"), []byte("x"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gitIn(t, work, "add", "f")
|
||||
gitIn(t, work, "commit", "-q", "-m", "pull request head")
|
||||
commit := gitIn(t, work, "rev-parse", "HEAD")
|
||||
gitIn(t, work, "push", "-q", bare, "HEAD:refs/pull/1/head")
|
||||
|
||||
ws := filepath.Join(root, "ws")
|
||||
if err := os.MkdirAll(ws, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := FetchCommit(context.Background(), forgekey.ExecRunner, ws, "file://"+bare, commit, "refs/pull/1/head", AllowFileRemote())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if head := gitIn(t, ws, "rev-parse", "HEAD"); head != commit {
|
||||
t.Errorf("HEAD = %s, want %s", head, commit)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package buildstep
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
const sha = "0123456789abcdef0123456789abcdef01234567"
|
||||
|
||||
// fakeGit records every command and answers by a rule.
|
||||
type fakeGit struct {
|
||||
calls []string
|
||||
envs [][]string
|
||||
rule func(args string) forgekey.Ran
|
||||
}
|
||||
|
||||
func (f *fakeGit) run(ctx context.Context, env []string, name string, args ...string) forgekey.Ran {
|
||||
line := name + " " + strings.Join(args, " ")
|
||||
f.calls = append(f.calls, line)
|
||||
f.envs = append(f.envs, env)
|
||||
if f.rule != nil {
|
||||
if r := f.rule(line); r.Status != 0 || r.Stdout != "" || r.Err != "" {
|
||||
return r
|
||||
}
|
||||
}
|
||||
if len(args) > 4 && args[4] == "rev-parse" {
|
||||
return forgekey.Ran{Stdout: sha + "\n"}
|
||||
}
|
||||
return forgekey.Ran{}
|
||||
}
|
||||
|
||||
const pre = "git -c credential.helper= -C /w "
|
||||
|
||||
// 1474/AC1: the git commands are exactly the sequence, none with a credential or an https remote.
|
||||
func TestFetchCommitRunsTheSequence(t *testing.T) {
|
||||
f := &fakeGit{}
|
||||
if err := FetchCommit(context.Background(), f.run, "/w", "ssh://mesh-forge/novox/mesh-sdk.git", sha, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := []string{
|
||||
pre + "init -q",
|
||||
pre + "remote add origin ssh://mesh-forge/novox/mesh-sdk.git",
|
||||
pre + "fetch --no-tags --depth 1 origin " + sha,
|
||||
pre + "checkout -q --detach " + sha,
|
||||
pre + "rev-parse HEAD",
|
||||
}
|
||||
if strings.Join(f.calls, "\n") != strings.Join(want, "\n") {
|
||||
t.Errorf("commands:\n%s\nwant:\n%s", strings.Join(f.calls, "\n"), strings.Join(want, "\n"))
|
||||
}
|
||||
for i, c := range f.calls {
|
||||
if strings.Contains(c, "https://") || strings.Contains(c, "@") || !strings.Contains(c, "credential.helper= ") {
|
||||
t.Errorf("command %q carries a credential or https", c)
|
||||
}
|
||||
if len(f.envs[i]) != 1 || f.envs[i][0] != "GIT_TERMINAL_PROMPT=0" {
|
||||
t.Errorf("env %v", f.envs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1474/AC2: the depth-1 fetch fails and a pull ref is given: the ref is fetched and the commit verified; with no
|
||||
// ref, the fetch's failure is the answer.
|
||||
func TestFetchCommitFallsBackToThePullRef(t *testing.T) {
|
||||
f := &fakeGit{rule: func(l string) forgekey.Ran {
|
||||
if strings.Contains(l, "--depth 1") {
|
||||
return forgekey.Ran{Status: 128, Stderr: "fatal: couldn't find remote ref " + sha + "\n"}
|
||||
}
|
||||
return forgekey.Ran{}
|
||||
}}
|
||||
if err := FetchCommit(context.Background(), f.run, "/w", "ssh://mesh-forge/o/r.git", sha, "refs/pull/1/head"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := []string{pre + "fetch --no-tags origin refs/pull/1/head", pre + "cat-file -e " + sha + "^{commit}", pre + "checkout -q --detach " + sha}
|
||||
got := strings.Join(f.calls[3:6], "\n")
|
||||
if got != strings.Join(want, "\n") {
|
||||
t.Errorf("after the failed fetch:\n%s\nwant:\n%s", got, strings.Join(want, "\n"))
|
||||
}
|
||||
|
||||
g := &fakeGit{rule: f.rule}
|
||||
err := FetchCommit(context.Background(), g.run, "/w", "ssh://mesh-forge/o/r.git", sha, "")
|
||||
if err == nil || !strings.Contains(err.Error(), "couldn't find remote ref") || len(g.calls) != 3 {
|
||||
t.Errorf("without a ref: %v after %d commands", err, len(g.calls))
|
||||
}
|
||||
|
||||
h := &fakeGit{rule: func(l string) forgekey.Ran {
|
||||
if strings.Contains(l, "--depth 1") || strings.Contains(l, "cat-file") {
|
||||
return forgekey.Ran{Status: 1, Stderr: "no"}
|
||||
}
|
||||
return forgekey.Ran{}
|
||||
}}
|
||||
if err := FetchCommit(context.Background(), h.run, "/w", "ssh://mesh-forge/o/r.git", sha, "refs/pull/1/head"); err == nil {
|
||||
t.Error("a commit that is not in the ref was accepted")
|
||||
}
|
||||
}
|
||||
|
||||
// 1474/AC3: HEAD differs from the commit after the checkout: an error naming both.
|
||||
func TestFetchCommitAnswersAHeadThatIsNotTheCommit(t *testing.T) {
|
||||
other := strings.Repeat("a", 40)
|
||||
f := &fakeGit{rule: func(l string) forgekey.Ran {
|
||||
if strings.HasSuffix(l, "rev-parse HEAD") {
|
||||
return forgekey.Ran{Stdout: other + "\n"}
|
||||
}
|
||||
return forgekey.Ran{}
|
||||
}}
|
||||
err := FetchCommit(context.Background(), f.run, "/w", "ssh://mesh-forge/o/r.git", sha, "")
|
||||
if err == nil || !strings.Contains(err.Error(), other) || !strings.Contains(err.Error(), sha) {
|
||||
t.Errorf("error = %v, want both shas", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 1474/AC4: an https remote, user information, a file remote without the option, or a commit that is not 40
|
||||
// hex digits is refused before anything runs.
|
||||
func TestFetchCommitRefusesBeforeRunningAnything(t *testing.T) {
|
||||
for _, c := range []struct{ remote, commit, ref string }{
|
||||
{"https://forge.example/o/r.git", sha, ""},
|
||||
{"http://forge.example/o/r.git", sha, ""},
|
||||
{"ssh://git@mesh-forge/o/r.git", sha, ""},
|
||||
{"ssh://user:pw@mesh-forge/o/r.git", sha, ""},
|
||||
{"file:///tmp/r.git", sha, ""},
|
||||
{"/tmp/r.git", sha, ""},
|
||||
{"ssh://mesh-forge/o/r.git", "abc123", ""},
|
||||
{"ssh://mesh-forge/o/r.git", strings.ToUpper(sha), ""},
|
||||
{"ssh://mesh-forge/o/r.git", "main", ""},
|
||||
{"ssh://mesh-forge/o/r.git", sha, "--upload-pack=x"},
|
||||
} {
|
||||
f := &fakeGit{}
|
||||
if err := FetchCommit(context.Background(), f.run, "/w", c.remote, c.commit, c.ref); err == nil || len(f.calls) != 0 {
|
||||
t.Errorf("%+v: err %v, %d commands", c, err, len(f.calls))
|
||||
}
|
||||
}
|
||||
f := &fakeGit{}
|
||||
if err := FetchCommit(context.Background(), f.run, "/w", "file:///tmp/r.git", sha, "", AllowFileRemote()); err != nil {
|
||||
t.Errorf("a file remote with the option: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user