Skip to content

Commit 82c11c8

Browse files
authored
Add first high-level integration test for Executor (#331)
* Add first high-level integration test for Executor * Exec bash process in dummydocker * Bump executor timeout to 5s in integration test * add docker.exec symlink for windows * Use OS-specific path separator when setting PATH * .exe to .bat * Try cd'ing into bat file directory * Skip integration test on windows
1 parent 8d8fd88 commit 82c11c8

File tree

4 files changed

+222
-5
lines changed

4 files changed

+222
-5
lines changed

internal/campaigns/executor_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package campaigns
2+
3+
import (
4+
"archive/zip"
5+
"bytes"
6+
"context"
7+
"fmt"
8+
"io/ioutil"
9+
"log"
10+
"mime"
11+
"net/http"
12+
"net/http/httptest"
13+
"os"
14+
"path/filepath"
15+
"runtime"
16+
"testing"
17+
"time"
18+
19+
"github.com/sourcegraph/go-diff/diff"
20+
"github.com/sourcegraph/src-cli/internal/api"
21+
"github.com/sourcegraph/src-cli/internal/campaigns/graphql"
22+
)
23+
24+
func TestExecutor_Integration(t *testing.T) {
25+
if runtime.GOOS == "windows" {
26+
t.Skip("Test doesn't work on Windows because dummydocker is written in bash")
27+
}
28+
29+
addToPath(t, "testdata/dummydocker")
30+
31+
repo := &graphql.Repository{
32+
Name: "github.com/sourcegraph/src-cli",
33+
DefaultBranch: &graphql.Branch{
34+
Name: "main",
35+
Target: struct{ OID string }{OID: "d34db33f"},
36+
},
37+
}
38+
39+
filesInRepo := map[string]string{
40+
"README.md": "# Welcome to the README\n",
41+
"main.go": "package main\n\nfunc main() {\n\tfmt.Println( \"Hello World\")\n}\n",
42+
}
43+
44+
steps := []Step{
45+
{Run: `echo -e "foobar\n" >> README.md`, Container: "alpine:13"},
46+
{Run: `go fmt main.go`, Container: "doesntmatter:13"},
47+
}
48+
49+
h := newZipHandler(t, repo.Name, repo.DefaultBranch.Name, filesInRepo)
50+
ts := httptest.NewServer(h)
51+
defer ts.Close()
52+
53+
var clientBuffer bytes.Buffer
54+
client := api.NewClient(api.ClientOpts{Endpoint: ts.URL, Out: &clientBuffer})
55+
56+
testTempDir, err := ioutil.TempDir("", "executor-integration-test-*")
57+
if err != nil {
58+
t.Fatal(err)
59+
}
60+
61+
creator := &WorkspaceCreator{dir: testTempDir, client: client}
62+
opts := ExecutorOpts{
63+
Cache: &ExecutionNoOpCache{},
64+
Creator: creator,
65+
TempDir: testTempDir,
66+
Parallelism: runtime.GOMAXPROCS(0),
67+
Timeout: 5 * time.Second,
68+
}
69+
70+
called := false
71+
updateFn := func(task *Task, ts TaskStatus) { called = true }
72+
73+
executor := newExecutor(opts, client, updateFn)
74+
75+
template := &ChangesetTemplate{}
76+
executor.AddTask(repo, steps, template)
77+
78+
executor.Start(context.Background())
79+
specs, err := executor.Wait()
80+
if err != nil {
81+
t.Fatal(err)
82+
}
83+
84+
if !called {
85+
t.Fatalf("update was not called")
86+
}
87+
88+
if have, want := len(specs), 1; have != want {
89+
t.Fatalf("wrong number of specs. want=%d, have=%d", want, have)
90+
}
91+
92+
if have, want := len(specs[0].Commits), 1; have != want {
93+
t.Fatalf("wrong number of commits. want=%d, have=%d", want, have)
94+
}
95+
96+
fileDiffs, err := diff.ParseMultiFileDiff([]byte(specs[0].Commits[0].Diff))
97+
if err != nil {
98+
t.Fatalf("failed to parse diff: %s", err)
99+
}
100+
101+
diffsByName := map[string]*diff.FileDiff{}
102+
for _, fd := range fileDiffs {
103+
diffsByName[fd.OrigName] = fd
104+
}
105+
106+
if have, want := len(diffsByName), 2; have != want {
107+
t.Fatalf("wrong number of diffsByName. want=%d, have=%d", want, have)
108+
}
109+
110+
if _, ok := diffsByName["main.go"]; !ok {
111+
t.Errorf("main.go was not changed")
112+
}
113+
if _, ok := diffsByName["README.md"]; !ok {
114+
t.Errorf("README.md was not changed")
115+
}
116+
}
117+
118+
func addToPath(t *testing.T, relPath string) {
119+
t.Helper()
120+
121+
dummyDockerPath, err := filepath.Abs("testdata/dummydocker")
122+
if err != nil {
123+
t.Fatal(err)
124+
}
125+
os.Setenv("PATH", fmt.Sprintf("%s%c%s", dummyDockerPath, os.PathListSeparator, os.Getenv("PATH")))
126+
}
127+
128+
func newZipHandler(t *testing.T, repo, branch string, files map[string]string) http.HandlerFunc {
129+
wantPath := fmt.Sprintf("/%s@%s/-/raw", repo, branch)
130+
131+
downloadName := filepath.Base(repo)
132+
mediaType := mime.FormatMediaType("Attachment", map[string]string{
133+
"filename": downloadName,
134+
})
135+
136+
return func(w http.ResponseWriter, r *http.Request) {
137+
if r.URL.Path != wantPath {
138+
t.Errorf("request has wrong path. want=%q, have=%q", wantPath, r.URL.Path)
139+
w.WriteHeader(http.StatusNotFound)
140+
return
141+
}
142+
143+
w.Header().Set("X-Content-Type-Options", "nosniff")
144+
w.Header().Set("Content-Type", "application/zip")
145+
w.Header().Set("Content-Disposition", mediaType)
146+
147+
zipWriter := zip.NewWriter(w)
148+
149+
for name, body := range files {
150+
f, err := zipWriter.Create(name)
151+
if err != nil {
152+
log.Fatal(err)
153+
}
154+
if _, err := f.Write([]byte(body)); err != nil {
155+
t.Errorf("failed to write body for %s to zip: %s", name, err)
156+
}
157+
}
158+
159+
if err := zipWriter.Close(); err != nil {
160+
t.Fatalf("closing zipWriter failed: %s", err)
161+
}
162+
}
163+
}

internal/campaigns/graphql/repository.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@ fragment repositoryFields on Repository {
1919
}
2020
`
2121

22+
type Branch struct {
23+
Name string
24+
Target struct{ OID string }
25+
}
26+
2227
type Repository struct {
2328
ID string
2429
Name string
2530
URL string
2631
ExternalRepository struct{ ServiceType string }
27-
DefaultBranch *struct {
28-
Name string
29-
Target struct{ OID string }
30-
}
32+
DefaultBranch *Branch
3133
}
3234

3335
func (r *Repository) BaseRef() string {

internal/campaigns/run_steps.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ func runSteps(ctx context.Context, wc *WorkspaceCreator, repo *graphql.Repositor
135135
Stderr: strings.TrimSpace(stderrBuffer.String()),
136136
}
137137
}
138-
logger.Logf("[Step %d] complete in %s", i+1, elapsed)
139138

139+
logger.Logf("[Step %d] complete in %s", i+1, elapsed)
140140
}
141141

142142
if _, err := runGitCmd("add", "--all"); err != nil {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/bin/bash
2+
3+
# This script is used by the executor integration test to simulate Docker.
4+
# It gets put into $PATH as "docker" and accepts the "run" command.
5+
6+
# Depending on the arguments to the "run" command it either acts like it
7+
# created a tempfile, or it executes the script supplied as the last arg to
8+
# "run".
9+
10+
dummy_temp_file="DUMMYDOCKER-TEMP-FILE"
11+
12+
if [[ "${1}" == "run" ]]; then
13+
last_arg="${@: -1}"
14+
15+
case "$last_arg" in
16+
"mktemp")
17+
# If the last arg is "mktemp" we're probing for a shell image and
18+
# want to create a temp file in the container which we can put a script.
19+
echo "${dummy_temp_file}"
20+
exit 0
21+
;;
22+
23+
"${dummy_temp_file}")
24+
# If the last arg is the temp file we "created" earlier, we now want to
25+
# execute it.
26+
#
27+
# But the real script is in the matching host temp file, which should
28+
# be mounted into the temp file inside the container.
29+
#
30+
# We need to find it in the args and then execute it.
31+
32+
host_temp_file=""
33+
for i in "$@";
34+
do
35+
if [[ ${i} =~ ^type=bind,source=(.*),target=${dummy_temp_file},ro$ ]]; then
36+
host_temp_file="${BASH_REMATCH[1]}"
37+
fi
38+
done
39+
[ -z "$host_temp_file" ] && echo "host temp file not found in args" && exit 1;
40+
41+
# Now that we have the path to the host temp file, we can execute it
42+
exec bash ${host_temp_file}
43+
;;
44+
*)
45+
echo "dummydocker doesn't know about this command: $last_arg"
46+
exit 1
47+
;;
48+
esac
49+
fi
50+
51+
echo "dummydocker doesn't know about this command: $1"
52+
exit 1

0 commit comments

Comments
 (0)