|
1 | 1 | package campaigns |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
| 5 | + "context" |
4 | 6 | "io/ioutil" |
| 7 | + "net/http" |
| 8 | + "net/http/httptest" |
5 | 9 | "os" |
6 | 10 | "path/filepath" |
7 | 11 | "testing" |
| 12 | + "time" |
| 13 | + |
| 14 | + "github.com/google/go-cmp/cmp" |
| 15 | + "github.com/sourcegraph/src-cli/internal/api" |
| 16 | + "github.com/sourcegraph/src-cli/internal/campaigns/graphql" |
8 | 17 | ) |
9 | 18 |
|
| 19 | +func TestWorkspaceCreator_Create(t *testing.T) { |
| 20 | + workspaceTmpDir := func(t *testing.T) string { |
| 21 | + testTempDir, err := ioutil.TempDir("", "executor-integration-test-*") |
| 22 | + if err != nil { |
| 23 | + t.Fatal(err) |
| 24 | + } |
| 25 | + t.Cleanup(func() { os.Remove(testTempDir) }) |
| 26 | + |
| 27 | + return testTempDir |
| 28 | + } |
| 29 | + |
| 30 | + repo := &graphql.Repository{ |
| 31 | + ID: "src-cli", |
| 32 | + Name: "github.com/sourcegraph/src-cli", |
| 33 | + DefaultBranch: &graphql.Branch{Name: "main", Target: struct{ OID string }{OID: "d34db33f"}}, |
| 34 | + } |
| 35 | + |
| 36 | + archive := mockRepoArchive{ |
| 37 | + repo: repo, |
| 38 | + files: map[string]string{ |
| 39 | + "README.md": "# Welcome to the README\n", |
| 40 | + }, |
| 41 | + } |
| 42 | + |
| 43 | + t.Run("success", func(t *testing.T) { |
| 44 | + requestsReceived := 0 |
| 45 | + callback := func(_ http.ResponseWriter, _ *http.Request) { |
| 46 | + requestsReceived += 1 |
| 47 | + } |
| 48 | + |
| 49 | + ts := httptest.NewServer(newZipArchivesMux(t, callback, archive)) |
| 50 | + defer ts.Close() |
| 51 | + |
| 52 | + var clientBuffer bytes.Buffer |
| 53 | + client := api.NewClient(api.ClientOpts{Endpoint: ts.URL, Out: &clientBuffer}) |
| 54 | + |
| 55 | + testTempDir := workspaceTmpDir(t) |
| 56 | + |
| 57 | + creator := &WorkspaceCreator{dir: testTempDir, client: client} |
| 58 | + workspace, err := creator.Create(context.Background(), repo) |
| 59 | + if err != nil { |
| 60 | + t.Fatalf("unexpected error: %s", err) |
| 61 | + } |
| 62 | + |
| 63 | + wantZipFile := "github.com-sourcegraph-src-cli-d34db33f.zip" |
| 64 | + ok, err := dirContains(creator.dir, wantZipFile) |
| 65 | + if err != nil { |
| 66 | + t.Fatal(err) |
| 67 | + } |
| 68 | + if !ok { |
| 69 | + t.Fatalf("temp dir doesnt contain zip file") |
| 70 | + } |
| 71 | + |
| 72 | + haveUnzippedFiles, err := readWorkspaceFiles(workspace) |
| 73 | + if err != nil { |
| 74 | + t.Fatalf("error walking workspace: %s", err) |
| 75 | + } |
| 76 | + |
| 77 | + if !cmp.Equal(archive.files, haveUnzippedFiles) { |
| 78 | + t.Fatalf("wrong files in workspace:\n%s", cmp.Diff(archive.files, haveUnzippedFiles)) |
| 79 | + } |
| 80 | + |
| 81 | + // Create it a second time and make sure that the server wasn't called |
| 82 | + _, err = creator.Create(context.Background(), repo) |
| 83 | + if err != nil { |
| 84 | + t.Fatalf("unexpected error: %s", err) |
| 85 | + } |
| 86 | + |
| 87 | + if requestsReceived != 1 { |
| 88 | + t.Fatalf("wrong number of requests received: %d", requestsReceived) |
| 89 | + } |
| 90 | + |
| 91 | + // Third time, but this time with cleanup, _after_ unzipping |
| 92 | + creator.deleteZips = true |
| 93 | + _, err = creator.Create(context.Background(), repo) |
| 94 | + if err != nil { |
| 95 | + t.Fatalf("unexpected error: %s", err) |
| 96 | + } |
| 97 | + |
| 98 | + if requestsReceived != 1 { |
| 99 | + t.Fatalf("wrong number of requests received: %d", requestsReceived) |
| 100 | + } |
| 101 | + |
| 102 | + ok, err = dirContains(creator.dir, wantZipFile) |
| 103 | + if err != nil { |
| 104 | + t.Fatal(err) |
| 105 | + } |
| 106 | + if ok { |
| 107 | + t.Fatalf("temp dir contains zip file but should not") |
| 108 | + } |
| 109 | + }) |
| 110 | + |
| 111 | + t.Run("canceled", func(t *testing.T) { |
| 112 | + // We create a context that is canceled after the server sent down the |
| 113 | + // first file to simulate a slow download that's aborted by the user hitting Ctrl-C. |
| 114 | + |
| 115 | + firstFileWritten := make(chan struct{}) |
| 116 | + callback := func(w http.ResponseWriter, r *http.Request) { |
| 117 | + // We flush the headers and the first file |
| 118 | + w.(http.Flusher).Flush() |
| 119 | + |
| 120 | + // Wait a bit for the client to start writing the file |
| 121 | + time.Sleep(50 * time.Millisecond) |
| 122 | + |
| 123 | + // Cancel the context to simulate the Ctrl-C |
| 124 | + firstFileWritten <- struct{}{} |
| 125 | + |
| 126 | + <-r.Context().Done() |
| 127 | + } |
| 128 | + |
| 129 | + ctx, cancel := context.WithCancel(context.Background()) |
| 130 | + go func() { |
| 131 | + <-firstFileWritten |
| 132 | + cancel() |
| 133 | + }() |
| 134 | + |
| 135 | + ts := httptest.NewServer(newZipArchivesMux(t, callback, archive)) |
| 136 | + defer ts.Close() |
| 137 | + |
| 138 | + var clientBuffer bytes.Buffer |
| 139 | + client := api.NewClient(api.ClientOpts{Endpoint: ts.URL, Out: &clientBuffer}) |
| 140 | + |
| 141 | + testTempDir := workspaceTmpDir(t) |
| 142 | + |
| 143 | + creator := &WorkspaceCreator{dir: testTempDir, client: client} |
| 144 | + |
| 145 | + _, err := creator.Create(ctx, repo) |
| 146 | + if err == nil { |
| 147 | + t.Fatalf("error is nil") |
| 148 | + } |
| 149 | + |
| 150 | + zipFile := "github.com-sourcegraph-src-cli-d34db33f.zip" |
| 151 | + ok, err := dirContains(creator.dir, zipFile) |
| 152 | + if err != nil { |
| 153 | + t.Fatal(err) |
| 154 | + } |
| 155 | + if ok { |
| 156 | + t.Fatalf("zip file in temp dir was not cleaned up") |
| 157 | + } |
| 158 | + }) |
| 159 | +} |
| 160 | + |
10 | 161 | func TestMkdirAll(t *testing.T) { |
11 | 162 | // TestEnsureAll does most of the heavy lifting here; we're just testing the |
12 | 163 | // MkdirAll scenarios here around whether the directory exists. |
@@ -139,3 +290,46 @@ func isDir(t *testing.T, path string) bool { |
139 | 290 |
|
140 | 291 | return st.IsDir() |
141 | 292 | } |
| 293 | + |
| 294 | +func readWorkspaceFiles(workspace string) (map[string]string, error) { |
| 295 | + files := map[string]string{} |
| 296 | + |
| 297 | + err := filepath.Walk(workspace, func(path string, info os.FileInfo, err error) error { |
| 298 | + if err != nil { |
| 299 | + return err |
| 300 | + } |
| 301 | + if info.IsDir() { |
| 302 | + return nil |
| 303 | + } |
| 304 | + |
| 305 | + content, err := ioutil.ReadFile(path) |
| 306 | + if err != nil { |
| 307 | + return err |
| 308 | + } |
| 309 | + |
| 310 | + rel, err := filepath.Rel(workspace, path) |
| 311 | + if err != nil { |
| 312 | + return err |
| 313 | + } |
| 314 | + |
| 315 | + files[rel] = string(content) |
| 316 | + return nil |
| 317 | + }) |
| 318 | + |
| 319 | + return files, err |
| 320 | +} |
| 321 | + |
| 322 | +func dirContains(dir, filename string) (bool, error) { |
| 323 | + files, err := ioutil.ReadDir(dir) |
| 324 | + if err != nil { |
| 325 | + return false, err |
| 326 | + } |
| 327 | + |
| 328 | + for _, f := range files { |
| 329 | + if f.Name() == filename { |
| 330 | + return true, nil |
| 331 | + } |
| 332 | + } |
| 333 | + |
| 334 | + return false, nil |
| 335 | +} |
0 commit comments