Skip to content

Commit 822d3a0

Browse files
committed
Introduce OCI Conformance Test Suite
Added new conformance directory in the project root, with a number of test files written in Go. Tests can be compiled by running `go test -c` in the conformance directory and executing the created conformance.test file. In order for the tests to run, registry providers will need to set up certain environment variables with the root url, the namespace of a repository, and authentication information. Additionally, the OCI_DEBUG variable can be set to "true" for more detailed output. The tests create two report files: report.html and junit.xml. The html report is expandable if more detailed information is needed on failures. Related to #24 Signed-off-by: Peter Engelbert <[email protected]>
1 parent 219f20c commit 822d3a0

16 files changed

+834
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
.idea/
2+
.vscode/
13
output
24
header.html
5+
tags

conformance/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
vendor/
2+
junit.xml
3+
report.html
4+
conformance.test
5+
tags
6+
env.sh
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package conformance
2+
3+
import (
4+
"testing"
5+
6+
g "github.com/onsi/ginkgo"
7+
"github.com/onsi/ginkgo/reporters"
8+
. "github.com/onsi/gomega"
9+
)
10+
11+
func TestConformance(t *testing.T) {
12+
g.Describe(suiteDescription, func() {
13+
test01BaseAPIRoute()
14+
test02BlobUploadStreamed()
15+
test03BlobUploadMonolithic()
16+
test04BlobUploadChunked()
17+
test05ManifestUpload()
18+
test06TagsList()
19+
test07ManifestDelete()
20+
test08BlobDelete()
21+
})
22+
RegisterFailHandler(g.Fail)
23+
reporters := []g.Reporter{newHTMLReporter(reportHTMLFilename), reporters.NewJUnitReporter(reportJUnitFilename)}
24+
g.RunSpecsWithDefaultAndCustomReporters(t, suiteDescription, reporters)
25+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package conformance
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/bloodorangeio/reggie"
7+
g "github.com/onsi/ginkgo"
8+
. "github.com/onsi/gomega"
9+
)
10+
11+
var test01BaseAPIRoute = func() {
12+
g.Context("Base API Route", func() {
13+
g.Specify("GET request to base API route must return 200 response", func() {
14+
req := client.NewRequest(reggie.GET, "/v2/")
15+
resp, err := client.Do(req)
16+
Expect(err).To(BeNil())
17+
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
18+
})
19+
})
20+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package conformance
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/bloodorangeio/reggie"
7+
g "github.com/onsi/ginkgo"
8+
. "github.com/onsi/gomega"
9+
)
10+
11+
var test02BlobUploadStreamed = func() {
12+
g.Context("Blob Upload Streamed", func() {
13+
g.Specify("PATCH request with blob in body should yield 202 response", func() {
14+
req := client.NewRequest(reggie.POST, "/v2/<name>/blobs/uploads/")
15+
resp, err := client.Do(req)
16+
Expect(err).To(BeNil())
17+
lastResponse = resp
18+
19+
req = client.NewRequest(reggie.PATCH, lastResponse.GetRelativeLocation()).
20+
SetHeader("Content-Type", "application/octet-stream").
21+
SetBody(blobA)
22+
resp, err = client.Do(req)
23+
Expect(err).To(BeNil())
24+
Expect(resp.StatusCode()).To(Equal(http.StatusAccepted))
25+
lastResponse = resp
26+
})
27+
28+
g.Specify("PUT request to session URL with digest should yield 201 response", func() {
29+
req := client.NewRequest(reggie.PUT, lastResponse.GetRelativeLocation()).
30+
SetQueryParam("digest", blobADigest).
31+
SetHeader("Content-Type", "application/octet-stream").
32+
SetHeader("Content-Length", blobALength)
33+
resp, err := client.Do(req)
34+
Expect(err).To(BeNil())
35+
Expect(resp.StatusCode()).To(Equal(http.StatusCreated))
36+
})
37+
})
38+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package conformance
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/bloodorangeio/reggie"
7+
g "github.com/onsi/ginkgo"
8+
. "github.com/onsi/gomega"
9+
)
10+
11+
var test03BlobUploadMonolithic = func() {
12+
g.Context("Blob Upload Monolithic", func() {
13+
g.Specify("GET nonexistent blob should result in 404 response", func() {
14+
req := client.NewRequest(reggie.GET, "/v2/<name>/blobs/<digest>",
15+
reggie.WithDigest(dummyDigest))
16+
resp, err := client.Do(req)
17+
Expect(err).To(BeNil())
18+
Expect(resp.StatusCode()).To(Equal(http.StatusNotFound))
19+
})
20+
21+
g.Specify("POST request should yield a session ID", func() {
22+
req := client.NewRequest(reggie.POST, "/v2/<name>/blobs/uploads/")
23+
resp, err := client.Do(req)
24+
Expect(err).To(BeNil())
25+
Expect(resp.StatusCode()).To(Equal(http.StatusAccepted))
26+
lastResponse = resp
27+
})
28+
29+
g.Specify("PUT upload of a blob should yield a 201 Response", func() {
30+
req := client.NewRequest(reggie.PUT, lastResponse.GetRelativeLocation()).
31+
SetHeader("Content-Length", configContentLength).
32+
SetHeader("Content-Type", "application/octet-stream").
33+
SetQueryParam("digest", configDigest).
34+
SetBody(configContent)
35+
resp, err := client.Do(req)
36+
Expect(err).To(BeNil())
37+
Expect(resp.StatusCode()).To(Equal(http.StatusCreated))
38+
})
39+
40+
g.Specify("GET request to existing blob should yield 200 response", func() {
41+
req := client.NewRequest(reggie.GET, "/v2/<name>/blobs/<digest>", reggie.WithDigest(configDigest))
42+
resp, err := client.Do(req)
43+
Expect(err).To(BeNil())
44+
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
45+
})
46+
})
47+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package conformance
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/bloodorangeio/reggie"
7+
g "github.com/onsi/ginkgo"
8+
. "github.com/onsi/gomega"
9+
)
10+
11+
var test04BlobUploadChunked = func() {
12+
g.Context("Blob Upload Chunked", func() {
13+
g.Specify("PATCH request with first chunk should return 202", func() {
14+
req := client.NewRequest(reggie.POST, "/v2/<name>/blobs/uploads/").
15+
SetHeader("Content-Length", "0")
16+
resp, err := client.Do(req)
17+
Expect(err).To(BeNil())
18+
lastResponse = resp
19+
20+
req = client.NewRequest(reggie.PATCH, lastResponse.GetRelativeLocation()).
21+
SetHeader("Content-Type", "application/octet-stream").
22+
SetHeader("Content-Length", blobBChunk1Length).
23+
SetHeader("Content-Range", blobBChunk1Range).
24+
SetBody(blobBChunk1)
25+
resp, err = client.Do(req)
26+
Expect(err).To(BeNil())
27+
Expect(resp.StatusCode()).To(Equal(http.StatusAccepted))
28+
lastResponse = resp
29+
})
30+
31+
g.Specify("PUT request with final chunk should return 201", func() {
32+
req := client.NewRequest(reggie.PUT, lastResponse.GetRelativeLocation()).
33+
SetHeader("Content-Length", blobBChunk2Length).
34+
SetHeader("Content-Range", blobBChunk2Range).
35+
SetHeader("Content-Type", "application/octet-stream").
36+
SetQueryParam("digest", blobBDigest).
37+
SetBody(blobBChunk2)
38+
resp, err := client.Do(req)
39+
Expect(err).To(BeNil())
40+
Expect(resp.StatusCode()).To(Equal(http.StatusCreated))
41+
})
42+
})
43+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package conformance
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
"github.com/bloodorangeio/reggie"
8+
g "github.com/onsi/ginkgo"
9+
. "github.com/onsi/gomega"
10+
)
11+
12+
var test05ManifestUpload = func() {
13+
g.Context("Manifest Upload", func() {
14+
g.Specify("GET nonexistent manifest should return 404", func() {
15+
req := client.NewRequest(reggie.GET, "/v2/<name>/manifests/<reference>",
16+
reggie.WithReference(nonexistentManifest))
17+
resp, err := client.Do(req)
18+
Expect(err).To(BeNil())
19+
Expect(resp.StatusCode()).To(Equal(http.StatusNotFound))
20+
})
21+
22+
g.Specify("PUT should accept a manifest upload", func() {
23+
for i := 0; i < 4; i++ {
24+
req := client.NewRequest(reggie.PUT, "/v2/<name>/manifests/<reference>",
25+
reggie.WithReference(fmt.Sprintf("test%d", i))).
26+
SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
27+
SetBody(manifestContent)
28+
resp, err := client.Do(req)
29+
Expect(err).To(BeNil())
30+
Expect(resp.StatusCode()).To(Equal(http.StatusCreated))
31+
}
32+
})
33+
34+
g.Specify("GET request to manifest URL (digest) should yield 200 response", func() {
35+
req := client.NewRequest(reggie.GET, "/v2/<name>/manifests/<digest>", reggie.WithDigest(manifestDigest)).
36+
SetHeader("Accept", "application/vnd.oci.image.manifest.v1+json")
37+
resp, err := client.Do(req)
38+
Expect(err).To(BeNil())
39+
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
40+
})
41+
})
42+
}

conformance/06_tags_list_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package conformance
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
"strconv"
7+
8+
"github.com/bloodorangeio/reggie"
9+
g "github.com/onsi/ginkgo"
10+
. "github.com/onsi/gomega"
11+
)
12+
13+
var test06TagsList = func() {
14+
g.Context("Tags List", func() {
15+
g.Specify("GET request to list tags should yield 200 response", func() {
16+
req := client.NewRequest(reggie.GET, "/v2/<name>/tags/list")
17+
resp, err := client.Do(req)
18+
Expect(err).To(BeNil())
19+
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
20+
lastResponse = resp
21+
tagList := &TagList{}
22+
jsonData := []byte(resp.String())
23+
err = json.Unmarshal(jsonData, tagList)
24+
Expect(err).To(BeNil())
25+
numTags = len(tagList.Tags)
26+
})
27+
28+
g.Specify("GET request to manifest URL (tag) should yield 200 response", func() {
29+
tl := &TagList{}
30+
jsonData := lastResponse.Body()
31+
err := json.Unmarshal(jsonData, tl)
32+
Expect(err).To(BeNil())
33+
Expect(tl.Tags).ToNot(BeEmpty())
34+
req := client.NewRequest(reggie.GET, "/v2/<name>/manifests/<reference>",
35+
reggie.WithReference(tl.Tags[0])).
36+
SetHeader("Accept", "application/vnd.oci.image.manifest.v1+json")
37+
resp, err := client.Do(req)
38+
Expect(err).To(BeNil())
39+
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
40+
})
41+
42+
g.Specify("GET number of tags should be limitable by `n` query parameter", func() {
43+
numResults := numTags / 2
44+
req := client.NewRequest(reggie.GET, "/v2/<name>/tags/list").
45+
SetQueryParam("n", strconv.Itoa(numResults))
46+
resp, err := client.Do(req)
47+
Expect(err).To(BeNil())
48+
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
49+
jsonData := resp.Body()
50+
tagList := &TagList{}
51+
err = json.Unmarshal(jsonData, tagList)
52+
Expect(err).To(BeNil())
53+
Expect(len(tagList.Tags)).To(Equal(numResults))
54+
lastTagList = *tagList
55+
})
56+
57+
g.Specify("GET start of tag is set by `last` query parameter", func() {
58+
numResults := numTags / 2
59+
req := client.NewRequest(reggie.GET, "/v2/<name>/tags/list").
60+
SetQueryParam("n", strconv.Itoa(numResults)).
61+
SetQueryParam("last", lastTagList.Tags[numResults-1])
62+
resp, err := client.Do(req)
63+
Expect(err).To(BeNil())
64+
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
65+
jsonData := resp.Body()
66+
tagList := &TagList{}
67+
err = json.Unmarshal(jsonData, tagList)
68+
Expect(err).To(BeNil())
69+
Expect(tagList.Tags).To(ContainElement("test3"))
70+
})
71+
})
72+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package conformance
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
7+
"github.com/bloodorangeio/reggie"
8+
g "github.com/onsi/ginkgo"
9+
. "github.com/onsi/gomega"
10+
)
11+
12+
var test07ManifestDelete = func() {
13+
g.Context("Manifest Delete", func() {
14+
g.Specify("DELETE request to manifest URL should yield 202 response", func() {
15+
req := client.NewRequest(reggie.DELETE, "/v2/<name>/manifests/<digest>", reggie.WithDigest(manifestDigest))
16+
resp, err := client.Do(req)
17+
Expect(err).To(BeNil())
18+
Expect(resp.StatusCode()).To(Equal(http.StatusAccepted))
19+
})
20+
21+
g.Specify("GET request to deleted manifest URL should yield 404 response", func() {
22+
req := client.NewRequest(reggie.GET, "/v2/<name>/manifests/<digest>", reggie.WithDigest(manifestDigest))
23+
resp, err := client.Do(req)
24+
Expect(err).To(BeNil())
25+
Expect(resp.StatusCode()).To(Equal(http.StatusNotFound))
26+
})
27+
28+
g.Specify("GET request to tags list should reflect manifest deletion", func() {
29+
req := client.NewRequest(reggie.GET, "/v2/<name>/tags/list")
30+
resp, err := client.Do(req)
31+
Expect(err).To(BeNil())
32+
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
33+
tagList := &TagList{}
34+
jsonData := []byte(resp.String())
35+
err = json.Unmarshal(jsonData, tagList)
36+
Expect(err).To(BeNil())
37+
Expect(len(tagList.Tags)).To(BeNumerically("<", numTags))
38+
})
39+
})
40+
}

0 commit comments

Comments
 (0)