Skip to content

Commit

Permalink
Merge pull request #82 from bloodorangeio/initial-conformance-test
Browse files Browse the repository at this point in the history
Introduce OCI Conformance Test Suite
  • Loading branch information
vbatts authored Jan 22, 2020
2 parents 219f20c + 822d3a0 commit 668a6a9
Show file tree
Hide file tree
Showing 16 changed files with 834 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
.idea/
.vscode/
output
header.html
tags
6 changes: 6 additions & 0 deletions conformance/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
vendor/
junit.xml
report.html
conformance.test
tags
env.sh
25 changes: 25 additions & 0 deletions conformance/00_conformance_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package conformance

import (
"testing"

g "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/reporters"
. "github.com/onsi/gomega"
)

func TestConformance(t *testing.T) {
g.Describe(suiteDescription, func() {
test01BaseAPIRoute()
test02BlobUploadStreamed()
test03BlobUploadMonolithic()
test04BlobUploadChunked()
test05ManifestUpload()
test06TagsList()
test07ManifestDelete()
test08BlobDelete()
})
RegisterFailHandler(g.Fail)
reporters := []g.Reporter{newHTMLReporter(reportHTMLFilename), reporters.NewJUnitReporter(reportJUnitFilename)}
g.RunSpecsWithDefaultAndCustomReporters(t, suiteDescription, reporters)
}
20 changes: 20 additions & 0 deletions conformance/01_base_api_route_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package conformance

import (
"net/http"

"github.com/bloodorangeio/reggie"
g "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var test01BaseAPIRoute = func() {
g.Context("Base API Route", func() {
g.Specify("GET request to base API route must return 200 response", func() {
req := client.NewRequest(reggie.GET, "/v2/")
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
})
})
}
38 changes: 38 additions & 0 deletions conformance/02_blob_upload_streamed_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package conformance

import (
"net/http"

"github.com/bloodorangeio/reggie"
g "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var test02BlobUploadStreamed = func() {
g.Context("Blob Upload Streamed", func() {
g.Specify("PATCH request with blob in body should yield 202 response", func() {
req := client.NewRequest(reggie.POST, "/v2/<name>/blobs/uploads/")
resp, err := client.Do(req)
Expect(err).To(BeNil())
lastResponse = resp

req = client.NewRequest(reggie.PATCH, lastResponse.GetRelativeLocation()).
SetHeader("Content-Type", "application/octet-stream").
SetBody(blobA)
resp, err = client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusAccepted))
lastResponse = resp
})

g.Specify("PUT request to session URL with digest should yield 201 response", func() {
req := client.NewRequest(reggie.PUT, lastResponse.GetRelativeLocation()).
SetQueryParam("digest", blobADigest).
SetHeader("Content-Type", "application/octet-stream").
SetHeader("Content-Length", blobALength)
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusCreated))
})
})
}
47 changes: 47 additions & 0 deletions conformance/03_blob_upload_monolithic_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package conformance

import (
"net/http"

"github.com/bloodorangeio/reggie"
g "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var test03BlobUploadMonolithic = func() {
g.Context("Blob Upload Monolithic", func() {
g.Specify("GET nonexistent blob should result in 404 response", func() {
req := client.NewRequest(reggie.GET, "/v2/<name>/blobs/<digest>",
reggie.WithDigest(dummyDigest))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusNotFound))
})

g.Specify("POST request should yield a session ID", func() {
req := client.NewRequest(reggie.POST, "/v2/<name>/blobs/uploads/")
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusAccepted))
lastResponse = resp
})

g.Specify("PUT upload of a blob should yield a 201 Response", func() {
req := client.NewRequest(reggie.PUT, lastResponse.GetRelativeLocation()).
SetHeader("Content-Length", configContentLength).
SetHeader("Content-Type", "application/octet-stream").
SetQueryParam("digest", configDigest).
SetBody(configContent)
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusCreated))
})

g.Specify("GET request to existing blob should yield 200 response", func() {
req := client.NewRequest(reggie.GET, "/v2/<name>/blobs/<digest>", reggie.WithDigest(configDigest))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
})
})
}
43 changes: 43 additions & 0 deletions conformance/04_blob_upload_chunked_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package conformance

import (
"net/http"

"github.com/bloodorangeio/reggie"
g "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var test04BlobUploadChunked = func() {
g.Context("Blob Upload Chunked", func() {
g.Specify("PATCH request with first chunk should return 202", func() {
req := client.NewRequest(reggie.POST, "/v2/<name>/blobs/uploads/").
SetHeader("Content-Length", "0")
resp, err := client.Do(req)
Expect(err).To(BeNil())
lastResponse = resp

req = client.NewRequest(reggie.PATCH, lastResponse.GetRelativeLocation()).
SetHeader("Content-Type", "application/octet-stream").
SetHeader("Content-Length", blobBChunk1Length).
SetHeader("Content-Range", blobBChunk1Range).
SetBody(blobBChunk1)
resp, err = client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusAccepted))
lastResponse = resp
})

g.Specify("PUT request with final chunk should return 201", func() {
req := client.NewRequest(reggie.PUT, lastResponse.GetRelativeLocation()).
SetHeader("Content-Length", blobBChunk2Length).
SetHeader("Content-Range", blobBChunk2Range).
SetHeader("Content-Type", "application/octet-stream").
SetQueryParam("digest", blobBDigest).
SetBody(blobBChunk2)
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusCreated))
})
})
}
42 changes: 42 additions & 0 deletions conformance/05_manifest_upload_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package conformance

import (
"fmt"
"net/http"

"github.com/bloodorangeio/reggie"
g "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var test05ManifestUpload = func() {
g.Context("Manifest Upload", func() {
g.Specify("GET nonexistent manifest should return 404", func() {
req := client.NewRequest(reggie.GET, "/v2/<name>/manifests/<reference>",
reggie.WithReference(nonexistentManifest))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusNotFound))
})

g.Specify("PUT should accept a manifest upload", func() {
for i := 0; i < 4; i++ {
req := client.NewRequest(reggie.PUT, "/v2/<name>/manifests/<reference>",
reggie.WithReference(fmt.Sprintf("test%d", i))).
SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
SetBody(manifestContent)
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusCreated))
}
})

g.Specify("GET request to manifest URL (digest) should yield 200 response", func() {
req := client.NewRequest(reggie.GET, "/v2/<name>/manifests/<digest>", reggie.WithDigest(manifestDigest)).
SetHeader("Accept", "application/vnd.oci.image.manifest.v1+json")
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
})
})
}
72 changes: 72 additions & 0 deletions conformance/06_tags_list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package conformance

import (
"encoding/json"
"net/http"
"strconv"

"github.com/bloodorangeio/reggie"
g "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var test06TagsList = func() {
g.Context("Tags List", func() {
g.Specify("GET request to list tags should yield 200 response", func() {
req := client.NewRequest(reggie.GET, "/v2/<name>/tags/list")
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
lastResponse = resp
tagList := &TagList{}
jsonData := []byte(resp.String())
err = json.Unmarshal(jsonData, tagList)
Expect(err).To(BeNil())
numTags = len(tagList.Tags)
})

g.Specify("GET request to manifest URL (tag) should yield 200 response", func() {
tl := &TagList{}
jsonData := lastResponse.Body()
err := json.Unmarshal(jsonData, tl)
Expect(err).To(BeNil())
Expect(tl.Tags).ToNot(BeEmpty())
req := client.NewRequest(reggie.GET, "/v2/<name>/manifests/<reference>",
reggie.WithReference(tl.Tags[0])).
SetHeader("Accept", "application/vnd.oci.image.manifest.v1+json")
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
})

g.Specify("GET number of tags should be limitable by `n` query parameter", func() {
numResults := numTags / 2
req := client.NewRequest(reggie.GET, "/v2/<name>/tags/list").
SetQueryParam("n", strconv.Itoa(numResults))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
jsonData := resp.Body()
tagList := &TagList{}
err = json.Unmarshal(jsonData, tagList)
Expect(err).To(BeNil())
Expect(len(tagList.Tags)).To(Equal(numResults))
lastTagList = *tagList
})

g.Specify("GET start of tag is set by `last` query parameter", func() {
numResults := numTags / 2
req := client.NewRequest(reggie.GET, "/v2/<name>/tags/list").
SetQueryParam("n", strconv.Itoa(numResults)).
SetQueryParam("last", lastTagList.Tags[numResults-1])
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
jsonData := resp.Body()
tagList := &TagList{}
err = json.Unmarshal(jsonData, tagList)
Expect(err).To(BeNil())
Expect(tagList.Tags).To(ContainElement("test3"))
})
})
}
40 changes: 40 additions & 0 deletions conformance/07_manifest_delete_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package conformance

import (
"encoding/json"
"net/http"

"github.com/bloodorangeio/reggie"
g "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var test07ManifestDelete = func() {
g.Context("Manifest Delete", func() {
g.Specify("DELETE request to manifest URL should yield 202 response", func() {
req := client.NewRequest(reggie.DELETE, "/v2/<name>/manifests/<digest>", reggie.WithDigest(manifestDigest))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusAccepted))
})

g.Specify("GET request to deleted manifest URL should yield 404 response", func() {
req := client.NewRequest(reggie.GET, "/v2/<name>/manifests/<digest>", reggie.WithDigest(manifestDigest))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusNotFound))
})

g.Specify("GET request to tags list should reflect manifest deletion", func() {
req := client.NewRequest(reggie.GET, "/v2/<name>/tags/list")
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
tagList := &TagList{}
jsonData := []byte(resp.String())
err = json.Unmarshal(jsonData, tagList)
Expect(err).To(BeNil())
Expect(len(tagList.Tags)).To(BeNumerically("<", numTags))
})
})
}
27 changes: 27 additions & 0 deletions conformance/08_blob_delete_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package conformance

import (
"net/http"

"github.com/bloodorangeio/reggie"
g "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var test08BlobDelete = func() {
g.Context("Blob Delete", func() {
g.Specify("DELETE request to blob URL should yield 202 response", func() {
req := client.NewRequest(reggie.DELETE, "/v2/<name>/blobs/<digest>", reggie.WithDigest(configDigest))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusAccepted))
})

g.Specify("GET request to deleted blob URL should yield 404 response", func() {
req := client.NewRequest(reggie.GET, "/v2/<name>/blobs/<digest>", reggie.WithDigest(configDigest))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusNotFound))
})
})
}
Loading

0 comments on commit 668a6a9

Please sign in to comment.