package artifact_test
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitlab-pages/internal/artifact"
)
func TestTryMakeRequest(t *testing.T) {
content := "
Title of the document"
contentType := "text/html; charset=utf-8"
testServer := makeArtifactServerStub(t, content, contentType)
defer testServer.Close()
cases := []struct {
Path string
Token string
Status int
Content string
Length string
CacheControl string
ContentType string
Description string
}{
{
"/200.html",
"",
http.StatusOK,
content,
"90",
"max-age=3600",
"text/html; charset=utf-8",
"basic successful request",
},
{
"/200.html",
"token",
http.StatusOK,
content,
"90",
"",
"text/html; charset=utf-8",
"basic successful request",
},
{
"/max-caching.html",
"",
http.StatusIMUsed,
content,
"90",
"max-age=3600",
"text/html; charset=utf-8",
"max caching request",
},
{
"/non-caching.html",
"",
http.StatusTeapot,
content,
"90",
"",
"text/html; charset=utf-8",
"no caching request",
},
}
for _, c := range cases {
t.Run(c.Description, func(t *testing.T) {
result := httptest.NewRecorder()
reqURL, err := url.Parse("/-/subgroup/project/-/jobs/1/artifacts" + c.Path)
require.NoError(t, err)
r := &http.Request{URL: reqURL}
art := artifact.New(testServer.URL, 1, "gitlab-example.io")
require.True(t, art.TryMakeRequest("group.gitlab-example.io", result, r, c.Token, func(resp *http.Response) bool { return false }))
require.Equal(t, c.Status, result.Code)
require.Equal(t, c.ContentType, result.Header().Get("Content-Type"))
require.Equal(t, c.Length, result.Header().Get("Content-Length"))
require.Equal(t, c.CacheControl, result.Header().Get("Cache-Control"))
require.Equal(t, c.Content, string(result.Body.Bytes()))
})
}
}
// provide stub for testing different artifact responses
func makeArtifactServerStub(t *testing.T, content string, contentType string) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", contentType)
switch r.URL.RawPath {
case "/projects/group%2Fsubgroup%2Fproject/jobs/1/artifacts/200.html":
w.WriteHeader(http.StatusOK)
case "/projects/group%2Fsubgroup%2Fproject/jobs/1/artifacts/max-caching.html":
w.WriteHeader(http.StatusIMUsed)
case "/projects/group%2Fsubgroup%2Fproject/jobs/1/artifacts/non-caching.html":
w.WriteHeader(http.StatusTeapot)
case "/projects/group%2Fsubgroup%2Fproject/jobs/1/artifacts/500.html":
w.WriteHeader(http.StatusInternalServerError)
case "/projects/group%2Fsubgroup%2Fgroup%2Fproject/jobs/1/artifacts/404.html":
w.WriteHeader(http.StatusNotFound)
default:
t.Log("Surprising r.URL.RawPath", r.URL.RawPath)
w.WriteHeader(999)
}
fmt.Fprint(w, content)
}))
}
func TestBuildURL(t *testing.T) {
cases := []struct {
RawServer string
Host string
Path string
Expected string
PagesDomain string
Ok bool
Description string
}{
{
"https://gitlab.com/api/v4",
"group.gitlab.io",
"/-/project/-/jobs/1/artifacts/",
`https://gitlab.com/api/v4/projects/group%2Fproject/jobs/1/artifacts/`,
"gitlab.io",
true,
"Basic case",
},
{
"https://gitlab.com/api/v4/",
"group.gitlab.io",
"/-/project/-/jobs/1/artifacts/",
"https://gitlab.com/api/v4/projects/group%2Fproject/jobs/1/artifacts/",
"gitlab.io",
true,
"API URL has trailing slash",
},
{
"https://gitlab.com/api/v4/",
"GROUP.GITLAB.IO",
"/-/SUBGROUP/PROJECT/-/JOBS/1/ARTIFACTS/PATH/TO/FILE.txt",
"https://gitlab.com/api/v4/projects/GROUP%2FSUBGROUP%2FPROJECT/jobs/1/artifacts/PATH/TO/FILE.txt",
"gitlab.io",
true,
"Uppercase names",
},
{
"https://gitlab.com/api/v4",
"group.gitlab.io",
"/-/project/-/jobs/1foo1/artifacts/",
"",
"gitlab.io",
false,
"Job ID has letters",
},
{
"https://gitlab.com/api/v4",
"group.gitlab.io",
"/-/project/-/jobs/1$1/artifacts/",
"",
"gitlab.io",
false,
"Job ID has special characters",
},
{
"https://gitlab.com/api/v4",
"group.gitlab.io",
"/-/project/-/jobs/1/artifacts/path/to/file.txt",
"https://gitlab.com/api/v4/projects/group%2Fproject/jobs/1/artifacts/path/to/file.txt",
"gitlab.io",
true,
"Artifact in subdirectory",
},
{
"https://gitlab.com/api/v4",
"group.gitlab.io",
"/-/subgroup1/sub.group2/project/-/jobs/1/artifacts/path/to/file.txt",
"https://gitlab.com/api/v4/projects/group%2Fsubgroup1%2Fsub.group2%2Fproject/jobs/1/artifacts/path/to/file.txt",
"gitlab.io",
true,
"Basic subgroup case",
},
{
"https://gitlab.com/api/v4",
"group.gitlab.io",
"/-//project/-/jobs/1/artifacts/",
"https://gitlab.com/api/v4/projects/group%2Fproject/jobs/1/artifacts/",
"gitlab.io",
true,
"Leading / in remainder of project path",
},
{
"https://gitlab.com/api/v4",
"group.gitlab.io",
"/-/subgroup/project//-/jobs/1/artifacts/",
"https://gitlab.com/api/v4/projects/group%2Fsubgroup%2Fproject/jobs/1/artifacts/",
"gitlab.io",
true,
"Trailing / in remainder of project path",
},
{
"https://gitlab.com/api/v4",
"group.gitlab.io",
"/-//subgroup/project//-/jobs/1/artifacts/",
"https://gitlab.com/api/v4/projects/group%2Fsubgroup%2Fproject/jobs/1/artifacts/",
"gitlab.io",
true,
"Leading and trailing /",
},
{
"https://gitlab.com/api/v4",
"group.name.gitlab.io",
"/-/subgroup/project/-/jobs/1/artifacts/",
"https://gitlab.com/api/v4/projects/group.name%2Fsubgroup%2Fproject/jobs/1/artifacts/",
"gitlab.io",
true,
"Toplevel group has period",
},
{
"https://gitlab.com/api/v4",
"gitlab.io.gitlab.io",
"/-/project/-/jobs/1/artifacts/",
"https://gitlab.com/api/v4/projects/gitlab.io%2Fproject/jobs/1/artifacts/",
"gitlab.io",
true,
"Toplevel group matches pages domain",
},
{
"https://gitlab.com/api/v4",
"group.gitlab.io",
"/-/project/-/jobs/1/artifacts",
"",
"gitlab.io",
false,
"No artifact specified",
},
{
"https://gitlab.com/api/v4",
"group.gitlab.io",
"/index.html",
"",
"example.com",
false,
"non matching domain and request",
},
}
for _, c := range cases {
t.Run(c.Description, func(t *testing.T) {
a := artifact.New(c.RawServer, 1, c.PagesDomain)
u, ok := a.BuildURL(c.Host, c.Path)
msg := c.Description + " - generated URL: "
if u != nil {
msg = msg + u.String()
}
require.Equal(t, c.Ok, ok, msg)
if c.Ok {
require.Equal(t, c.Expected, u.String(), c.Description)
}
})
}
}