File view with blame information shown in the left gutter beside each line.

barerepo / server / e2e/artifact_test.go
183 lines · 6.6kb · 133728eaa05486504991b230f9d4c7e986b7defc
log files threads runs releases config jump to file t
133728e barerepo 1mo
1
package e2e
133728e barerepo 1mo
2
133728e barerepo 1mo
3
import (
133728e barerepo 1mo
4
"bytes"
133728e barerepo 1mo
5
"context"
133728e barerepo 1mo
6
"net/http"
133728e barerepo 1mo
7
"net/url"
133728e barerepo 1mo
8
"os/exec"
133728e barerepo 1mo
9
"strconv"
133728e barerepo 1mo
10
"strings"
133728e barerepo 1mo
11
"testing"
133728e barerepo 1mo
12
133728e barerepo 1mo
13
"github.com/barerepo/server/internal/token"
133728e barerepo 1mo
14
)
133728e barerepo 1mo
15
133728e barerepo 1mo
16
// Chapter 22.5: a run attaches its output to a release, and the job token carries the permission.
133728e barerepo 1mo
17
func TestABuildAttachesAFileToARelease(t *testing.T) {
133728e barerepo 1mo
18
if _, err := exec.LookPath("git"); err != nil {
133728e barerepo 1mo
19
t.Skip("git is not installed")
133728e barerepo 1mo
20
}
133728e barerepo 1mo
21
in := newInstance(t)
133728e barerepo 1mo
22
john := in.account("john")
133728e barerepo 1mo
23
work := seed(t, in, john, "john", "johnbot")
133728e barerepo 1mo
24
run(t, work, "git", "tag", "-a", "v1.0.0", "-m", "first release")
133728e barerepo 1mo
25
run(t, work, "git", "push", "-q", in.url(john, "/john/johnbot"), "v1.0.0")
133728e barerepo 1mo
26
133728e barerepo 1mo
27
jobID, jobToken := takeJob(t, in, "john", "john/johnbot")
133728e barerepo 1mo
28
133728e barerepo 1mo
29
body := bytes.Repeat([]byte("a compiled binary, more or less\n"), 100)
133728e barerepo 1mo
30
if code := upload(t, in, jobToken, jobID, "v1.0.0", "johnbot-linux-amd64", body); code != http.StatusNoContent {
133728e barerepo 1mo
31
t.Fatalf("the upload answered %d", code)
133728e barerepo 1mo
32
}
133728e barerepo 1mo
33
133728e barerepo 1mo
34
// The releases page names the file and its size, as releases.html has it.
133728e barerepo 1mo
35
code, _, page := get(t, in.http.URL+"/john/johnbot/releases")
133728e barerepo 1mo
36
if code != http.StatusOK {
133728e barerepo 1mo
37
t.Fatalf("the releases page answered %d", code)
133728e barerepo 1mo
38
}
133728e barerepo 1mo
39
if !strings.Contains(page, "johnbot-linux-amd64") {
133728e barerepo 1mo
40
t.Errorf("the releases page does not name the attached file:\n%s", page)
133728e barerepo 1mo
41
}
133728e barerepo 1mo
42
if !strings.Contains(page, "3.1kb") {
133728e barerepo 1mo
43
t.Errorf("the releases page does not give the file's size:\n%s", page)
133728e barerepo 1mo
44
}
133728e barerepo 1mo
45
133728e barerepo 1mo
46
// And it downloads, byte for byte, as an attachment and never as a page.
133728e barerepo 1mo
47
url := in.http.URL + "/john/johnbot/release/v1.0.0/johnbot-linux-amd64"
133728e barerepo 1mo
48
resp, err := http.Get(url)
133728e barerepo 1mo
49
if err != nil {
133728e barerepo 1mo
50
t.Fatal(err)
133728e barerepo 1mo
51
}
133728e barerepo 1mo
52
defer resp.Body.Close()
133728e barerepo 1mo
53
got := readAll(t, resp)
133728e barerepo 1mo
54
if got != string(body) {
133728e barerepo 1mo
55
t.Errorf("the download is %d bytes, wanted %d", len(got), len(body))
133728e barerepo 1mo
56
}
133728e barerepo 1mo
57
if d := resp.Header.Get("Content-Disposition"); !strings.HasPrefix(d, "attachment") {
133728e barerepo 1mo
58
t.Errorf("an attached file is served as %q, which a browser may render", d)
133728e barerepo 1mo
59
}
133728e barerepo 1mo
60
if resp.Header.Get("X-Content-Type-Options") != "nosniff" {
133728e barerepo 1mo
61
t.Error("the download does not say nosniff, so a browser may guess its type")
133728e barerepo 1mo
62
}
133728e barerepo 1mo
63
}
133728e barerepo 1mo
64
133728e barerepo 1mo
65
// A tag that is not in the repository has no release to attach to.
133728e barerepo 1mo
66
func TestAnArtifactNeedsARealTag(t *testing.T) {
133728e barerepo 1mo
67
if _, err := exec.LookPath("git"); err != nil {
133728e barerepo 1mo
68
t.Skip("git is not installed")
133728e barerepo 1mo
69
}
133728e barerepo 1mo
70
ctx := context.Background()
133728e barerepo 1mo
71
in := newInstance(t)
133728e barerepo 1mo
72
john := in.account("john")
133728e barerepo 1mo
73
seed(t, in, john, "john", "johnbot")
133728e barerepo 1mo
74
133728e barerepo 1mo
75
jobID, jobToken := takeJob(t, in, "john", "john/johnbot")
133728e barerepo 1mo
76
133728e barerepo 1mo
77
if code := upload(t, in, jobToken, jobID, "v9.9.9", "x", []byte("hi")); code != http.StatusNotFound {
133728e barerepo 1mo
78
t.Errorf("attaching to a tag that is not here answered %d", code)
133728e barerepo 1mo
79
}
133728e barerepo 1mo
80
// A name that would leave the directory is not a file name.
133728e barerepo 1mo
81
if code := upload(t, in, jobToken, jobID, "v1.0.0", "../escape", []byte("hi")); code == http.StatusNoContent {
133728e barerepo 1mo
82
t.Error("a file name with a path in it was accepted")
133728e barerepo 1mo
83
}
133728e barerepo 1mo
84
// A token for the repository that is not this job's token is not enough. 22.5.
133728e barerepo 1mo
85
other, _, err := in.db.CreateToken(ctx, token.Git, "john", "john/johnbot", "a plain git token")
133728e barerepo 1mo
86
if err != nil {
133728e barerepo 1mo
87
t.Fatal(err)
133728e barerepo 1mo
88
}
133728e barerepo 1mo
89
if code := upload(t, in, other, jobID, "v1.0.0", "x", []byte("hi")); code != http.StatusUnauthorized {
133728e barerepo 1mo
90
t.Errorf("a token that is not this job's uploaded anyway: %d", code)
133728e barerepo 1mo
91
}
133728e barerepo 1mo
92
}
133728e barerepo 1mo
93
133728e barerepo 1mo
94
func upload(t *testing.T, in *instance, token string, jobID int64, tag, file string, body []byte) int {
133728e barerepo 1mo
95
t.Helper()
133728e barerepo 1mo
96
req, err := http.NewRequest(http.MethodPost, in.http.URL+"/runner/artifact", bytes.NewReader(body))
133728e barerepo 1mo
97
if err != nil {
133728e barerepo 1mo
98
t.Fatal(err)
133728e barerepo 1mo
99
}
133728e barerepo 1mo
100
req.Header.Set("Barerepo-Token", token)
133728e barerepo 1mo
101
req.Header.Set("Barerepo-Job", strconv.FormatInt(jobID, 10))
133728e barerepo 1mo
102
req.Header.Set("Barerepo-Tag", tag)
133728e barerepo 1mo
103
req.Header.Set("Barerepo-File", file)
133728e barerepo 1mo
104
resp, err := http.DefaultClient.Do(req)
133728e barerepo 1mo
105
if err != nil {
133728e barerepo 1mo
106
t.Fatal(err)
133728e barerepo 1mo
107
}
133728e barerepo 1mo
108
defer resp.Body.Close()
133728e barerepo 1mo
109
return resp.StatusCode
133728e barerepo 1mo
110
}
133728e barerepo 1mo
111
133728e barerepo 1mo
112
// takeJob makes a runner, queues a job, takes it, and issues the job token the poll would have.
133728e barerepo 1mo
113
func takeJob(t *testing.T, in *instance, account, repo string) (int64, string) {
133728e barerepo 1mo
114
id, jobToken, _ := takeJobAs(t, in, account, repo)
133728e barerepo 1mo
115
return id, jobToken
133728e barerepo 1mo
116
}
133728e barerepo 1mo
117
133728e barerepo 1mo
118
// takeJobAs also hands back the runner's own token, which is what the log and done calls carry.
133728e barerepo 1mo
119
func takeJobAs(t *testing.T, in *instance, account, repo string) (int64, string, string) {
133728e barerepo 1mo
120
t.Helper()
133728e barerepo 1mo
121
ctx := context.Background()
133728e barerepo 1mo
122
runnerToken, tok, err := in.db.CreateToken(ctx, token.Runner, account, repo, "a runner")
133728e barerepo 1mo
123
if err != nil {
133728e barerepo 1mo
124
t.Fatal(err)
133728e barerepo 1mo
125
}
133728e barerepo 1mo
126
runner, err := in.db.AttachRunner(ctx, tok.ID, repo, "uproar.local", "linux", "amd64", nil)
133728e barerepo 1mo
127
if err != nil {
133728e barerepo 1mo
128
t.Fatal(err)
133728e barerepo 1mo
129
}
133728e barerepo 1mo
130
if _, err := in.db.QueueJob(ctx, repo, "refs/heads/master", "abc", "go build", ""); err != nil {
133728e barerepo 1mo
131
t.Fatal(err)
133728e barerepo 1mo
132
}
133728e barerepo 1mo
133
job, err := in.db.TakeJob(ctx, repo, *runner)
133728e barerepo 1mo
134
if err != nil || job == nil {
133728e barerepo 1mo
135
t.Fatalf("the runner could not take a job: %v", err)
133728e barerepo 1mo
136
}
133728e barerepo 1mo
137
jobToken, _, err := in.db.CreateToken(ctx, token.Git, account, repo,
133728e barerepo 1mo
138
"job "+strconv.FormatInt(job.ID, 10))
133728e barerepo 1mo
139
if err != nil {
133728e barerepo 1mo
140
t.Fatal(err)
133728e barerepo 1mo
141
}
133728e barerepo 1mo
142
return job.ID, jobToken, runnerToken
133728e barerepo 1mo
143
}
133728e barerepo 1mo
144
133728e barerepo 1mo
145
// A repository that moves takes its attached files, which are not in git and move with nothing else.
133728e barerepo 1mo
146
func TestARenameCarriesTheAttachedFiles(t *testing.T) {
133728e barerepo 1mo
147
if _, err := exec.LookPath("git"); err != nil {
133728e barerepo 1mo
148
t.Skip("git is not installed")
133728e barerepo 1mo
149
}
133728e barerepo 1mo
150
in := newInstance(t)
133728e barerepo 1mo
151
john := in.account("john")
133728e barerepo 1mo
152
in.account("lisa")
133728e barerepo 1mo
153
work := seed(t, in, john, "john", "johnbot")
133728e barerepo 1mo
154
run(t, work, "git", "tag", "-a", "v1.0.0", "-m", "first release")
133728e barerepo 1mo
155
run(t, work, "git", "push", "-q", in.url(john, "/john/johnbot"), "v1.0.0")
133728e barerepo 1mo
156
133728e barerepo 1mo
157
jobID, jobToken := takeJob(t, in, "john", "john/johnbot")
133728e barerepo 1mo
158
if code := upload(t, in, jobToken, jobID, "v1.0.0", "bot-linux", []byte("binary")); code != http.StatusNoContent {
133728e barerepo 1mo
159
t.Fatalf("the upload answered %d", code)
133728e barerepo 1mo
160
}
133728e barerepo 1mo
161
133728e barerepo 1mo
162
resp := post(t, in, "john", "/john/johnbot/rename", url.Values{"name": {"ircbot"}})
133728e barerepo 1mo
163
if resp.StatusCode != http.StatusFound {
133728e barerepo 1mo
164
t.Fatalf("the rename answered %d", resp.StatusCode)
133728e barerepo 1mo
165
}
133728e barerepo 1mo
166
if _, _, page := get(t, in.http.URL+"/john/ircbot/releases"); !strings.Contains(page, "bot-linux") {
133728e barerepo 1mo
167
t.Errorf("the rename left the attached file behind:\n%s", page)
133728e barerepo 1mo
168
}
133728e barerepo 1mo
169
code, _, body := get(t, in.http.URL+"/john/ircbot/release/v1.0.0/bot-linux")
133728e barerepo 1mo
170
if code != http.StatusOK || body != "binary" {
133728e barerepo 1mo
171
t.Errorf("the file does not download at the new name: %d %q", code, body)
133728e barerepo 1mo
172
}
133728e barerepo 1mo
173
133728e barerepo 1mo
174
// A transfer moves the owner, which is the other half of the path the files live under.
133728e barerepo 1mo
175
resp = post(t, in, "john", "/john/ircbot/transfer",
133728e barerepo 1mo
176
url.Values{"owner": {"lisa"}, "confirm": {"ircbot"}})
133728e barerepo 1mo
177
if resp.StatusCode != http.StatusFound {
133728e barerepo 1mo
178
t.Fatalf("the transfer answered %d", resp.StatusCode)
133728e barerepo 1mo
179
}
133728e barerepo 1mo
180
if _, _, page := get(t, in.http.URL+"/lisa/ircbot/releases"); !strings.Contains(page, "bot-linux") {
133728e barerepo 1mo
181
t.Errorf("the transfer left the attached file behind:\n%s", page)
133728e barerepo 1mo
182
}
133728e barerepo 1mo
183
}
history · rawbarerepo 0.1.0