aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pkg/instance/endpoint_test.go588
-rw-r--r--pkg/instance/instance.go69
-rw-r--r--pkg/instance/instance_test.go155
-rw-r--r--pkg/instance/metric.go10
-rw-r--r--pkg/instance/request.go77
-rw-r--r--pkg/instance/request_test.go318
6 files changed, 345 insertions, 872 deletions
diff --git a/pkg/instance/endpoint_test.go b/pkg/instance/endpoint_test.go
index 8511b8d..efcd4c0 100644
--- a/pkg/instance/endpoint_test.go
+++ b/pkg/instance/endpoint_test.go
@@ -2,479 +2,431 @@ package stfe
import (
"bytes"
- "context"
+ "encoding/hex"
"fmt"
+ "io"
"net/http"
"net/http/httptest"
- "reflect"
"testing"
"github.com/golang/mock/gomock"
- cttestdata "github.com/google/certificate-transparency-go/trillian/testdata"
- "github.com/google/trillian"
- "github.com/system-transparency/stfe/pkg/testdata"
+ "github.com/system-transparency/stfe/pkg/mocks"
"github.com/system-transparency/stfe/pkg/types"
)
-func TestEndpointAddEntry(t *testing.T) {
- for _, table := range []struct {
- description string
- breq *bytes.Buffer
- trsp *trillian.QueueLeafResponse
- terr error
- wantCode int
- }{
- {
- description: "invalid: bad request: empty",
- breq: bytes.NewBuffer(nil),
- wantCode: http.StatusBadRequest,
+var (
+ testWitVK = [types.VerificationKeySize]byte{}
+ testConfig = Config{
+ LogID: hex.EncodeToString(types.Hash([]byte("logid"))[:]),
+ TreeID: 0,
+ Prefix: "testonly",
+ MaxRange: 3,
+ Deadline: 10,
+ Interval: 10,
+ Witnesses: map[[types.HashSize]byte][types.VerificationKeySize]byte{
+ *types.Hash(testWitVK[:]): testWitVK,
},
- {
- description: "invalid: bad Trillian response: error",
- breq: testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter, testdata.Ed25519VkSubmitter),
- terr: fmt.Errorf("backend failure"),
- wantCode: http.StatusInternalServerError,
+ }
+ testSTH = &types.SignedTreeHead{
+ TreeHead: types.TreeHead{
+ Timestamp: 0,
+ TreeSize: 0,
+ RootHash: types.Hash(nil),
},
- {
- description: "valid",
- breq: testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter, testdata.Ed25519VkSubmitter),
- trsp: testdata.DefaultTQlr(t, false),
- wantCode: http.StatusOK,
+ SigIdent: []*types.SigIdent{
+ &types.SigIdent{
+ Signature: &[types.SignatureSize]byte{},
+ KeyHash: &[types.HashSize]byte{},
+ },
},
- } {
- func() { // run deferred functions at the end of each iteration
- ti := newTestInstance(t, nil)
- defer ti.ctrl.Finish()
-
- url := EndpointAddEntry.Path("http://example.com", ti.instance.LogParameters.Prefix)
- req, err := http.NewRequest("POST", url, table.breq)
- if err != nil {
- t.Fatalf("must create http request: %v", err)
- }
- req.Header.Set("Content-Type", "application/octet-stream")
- if table.trsp != nil || table.terr != nil {
- ti.client.EXPECT().QueueLeaf(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
- }
+ }
+)
- w := httptest.NewRecorder()
- ti.postHandler(t, EndpointAddEntry).ServeHTTP(w, req)
- if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description)
- }
- }()
+func mustHandle(t *testing.T, i Instance, e types.Endpoint) Handler {
+ for _, handler := range i.Handlers() {
+ if handler.Endpoint == e {
+ return handler
+ }
}
+ t.Fatalf("must handle endpoint: %v", e)
+ return Handler{}
}
-func TestEndpointAddCosignature(t *testing.T) {
+func TestAddLeaf(t *testing.T) {
+ buf := func() io.Reader {
+ // A valid leaf request that was created manually
+ return bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s",
+ types.ShardHint, types.Delim, "0", types.EOL,
+ types.Checksum, types.Delim, "0000000000000000000000000000000000000000000000000000000000000000", types.EOL,
+ types.SignatureOverMessage, types.Delim, "4cb410a4d48f52f761a7c01abcc28fd71811b84ded5403caed5e21b374f6aac9637cecd36828f17529fd503413d30ab66d7bb37a31dbf09a90d23b9241c45009", types.EOL,
+ types.VerificationKey, types.Delim, "f2b7a00b625469d32502e06e8b7fad1ef258d4ad0c6cd87b846142ab681957d5", types.EOL,
+ types.DomainHint, types.Delim, "example.com", types.EOL,
+ ))
+ }
for _, table := range []struct {
description string
- breq *bytes.Buffer
- wantCode int
+ ascii io.Reader // buffer used to populate HTTP request
+ expect bool // set if a mock answer is expected
+ err error // error from Trillian client
+ wantCode int // HTTP status ok
}{
{
- description: "invalid: bad request: empty",
- breq: bytes.NewBuffer(nil),
+ description: "invalid: bad request (parser error)",
+ ascii: bytes.NewBufferString("key=value\n"),
wantCode: http.StatusBadRequest,
},
{
- description: "invalid: signed wrong sth", // newLogParameters() use testdata.Ed25519VkLog as default
- breq: testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog2), &testdata.Ed25519SkWitness, &testdata.Ed25519VkWitness),
- wantCode: http.StatusBadRequest,
+ description: "invalid: bad request (signature error)",
+ ascii: bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s"+"%s%s%s%s",
+ types.ShardHint, types.Delim, "1", types.EOL,
+ types.Checksum, types.Delim, "1111111111111111111111111111111111111111111111111111111111111111", types.EOL,
+ types.SignatureOverMessage, types.Delim, "4cb410a4d48f52f761a7c01abcc28fd71811b84ded5403caed5e21b374f6aac9637cecd36828f17529fd503413d30ab66d7bb37a31dbf09a90d23b9241c45009", types.EOL,
+ types.VerificationKey, types.Delim, "f2b7a00b625469d32502e06e8b7fad1ef258d4ad0c6cd87b846142ab681957d5", types.EOL,
+ types.DomainHint, types.Delim, "example.com", types.EOL,
+ )),
+ wantCode: http.StatusBadRequest,
+ },
+ {
+ description: "invalid: backend failure",
+ ascii: buf(),
+ expect: true,
+ err: fmt.Errorf("something went wrong"),
+ wantCode: http.StatusInternalServerError,
},
{
description: "valid",
- breq: testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness, &testdata.Ed25519VkWitness),
+ ascii: buf(),
+ expect: true,
wantCode: http.StatusOK,
},
} {
- func() { // run deferred functions at the end of each iteration
- ti := newTestInstance(t, nil)
- defer ti.ctrl.Finish()
-
- url := EndpointAddCosignature.Path("http://example.com", ti.instance.LogParameters.Prefix)
- req, err := http.NewRequest("POST", url, table.breq)
+ // Run deferred functions at the end of each iteration
+ func() {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ client := mocks.NewMockClient(ctrl)
+ if table.expect {
+ client.EXPECT().AddLeaf(gomock.Any(), gomock.Any()).Return(table.err)
+ }
+ i := Instance{
+ Config: testConfig,
+ Client: client,
+ }
+
+ // Create HTTP request
+ url := types.EndpointAddLeaf.Path("http://example.com", i.Prefix)
+ req, err := http.NewRequest("POST", url, table.ascii)
if err != nil {
t.Fatalf("must create http request: %v", err)
}
- req.Header.Set("Content-Type", "application/octet-stream")
+ // Run HTTP request
w := httptest.NewRecorder()
- ti.postHandler(t, EndpointAddCosignature).ServeHTTP(w, req)
+ mustHandle(t, i, types.EndpointAddLeaf).ServeHTTP(w, req)
if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description)
+ t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
}
}()
}
}
-func TestEndpointGetLatestSth(t *testing.T) {
+func TestAddCosignature(t *testing.T) {
+ buf := func() io.Reader {
+ return bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%x%s"+"%s%s%x%s",
+ types.Signature, types.Delim, make([]byte, types.SignatureSize), types.EOL,
+ types.KeyHash, types.Delim, *types.Hash(testWitVK[:]), types.EOL,
+ ))
+ }
for _, table := range []struct {
description string
- trsp *trillian.GetLatestSignedLogRootResponse
- terr error
- wantCode int
- wantItem *types.StItem
+ ascii io.Reader // buffer used to populate HTTP request
+ expect bool // set if a mock answer is expected
+ err error // error from Trillian client
+ wantCode int // HTTP status ok
}{
{
- description: "backend failure",
- terr: fmt.Errorf("backend failure"),
- wantCode: http.StatusInternalServerError,
+ description: "invalid: bad request (parser error)",
+ ascii: bytes.NewBufferString("key=value\n"),
+ wantCode: http.StatusBadRequest,
},
{
- description: "valid",
- trsp: testdata.DefaultTSlr(t),
- wantCode: http.StatusOK,
- wantItem: testdata.DefaultSth(t, testdata.Ed25519VkLog),
+ description: "invalid: bad request (unknown witness)",
+ ascii: bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%x%s"+"%s%s%x%s",
+ types.Signature, types.Delim, make([]byte, types.SignatureSize), types.EOL,
+ types.KeyHash, types.Delim, *types.Hash(testWitVK[1:]), types.EOL,
+ )),
+ wantCode: http.StatusBadRequest,
},
- } {
- func() { // run deferred functions at the end of each iteration
- ti := newTestInstance(t, cttestdata.NewSignerWithFixedSig(nil, testdata.Signature))
- ti.ctrl.Finish()
-
- // Setup and run client query
- url := EndpointGetLatestSth.Path("http://example.com", ti.instance.LogParameters.Prefix)
- req, err := http.NewRequest("GET", url, nil)
- if err != nil {
- t.Fatalf("must create http request: %v", err)
- }
- if table.trsp != nil || table.terr != nil {
- ti.client.EXPECT().GetLatestSignedLogRoot(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
- }
-
- w := httptest.NewRecorder()
- ti.getHandler(t, EndpointGetLatestSth).ServeHTTP(w, req)
- if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description)
- }
- if w.Code != http.StatusOK {
- return
- }
-
- var item types.StItem
- if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil {
- t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err)
- }
- if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) {
- t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description)
- }
- }()
- }
-}
-
-func TestEndpointGetStableSth(t *testing.T) {
- for _, table := range []struct {
- description string
- useBadSource bool
- wantCode int
- wantItem *types.StItem
- }{
{
- description: "invalid: sth source failure",
- useBadSource: true,
- wantCode: http.StatusInternalServerError,
+ description: "invalid: backend failure",
+ ascii: buf(),
+ expect: true,
+ err: fmt.Errorf("something went wrong"),
+ wantCode: http.StatusBadRequest,
},
{
description: "valid",
+ ascii: buf(),
+ expect: true,
wantCode: http.StatusOK,
- wantItem: testdata.DefaultSth(t, testdata.Ed25519VkLog),
},
} {
- func() { // run deferred functions at the end of each iteration
- ti := newTestInstance(t, nil)
- ti.ctrl.Finish()
- if table.useBadSource {
- ti.instance.SthSource = &ActiveSthSource{}
- }
-
- // Setup and run client query
- url := EndpointGetStableSth.Path("http://example.com", ti.instance.LogParameters.Prefix)
- req, err := http.NewRequest("GET", url, nil)
+ // Run deferred functions at the end of each iteration
+ func() {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ stateman := mocks.NewMockStateManager(ctrl)
+ if table.expect {
+ stateman.EXPECT().AddCosignature(gomock.Any(), gomock.Any(), gomock.Any()).Return(table.err)
+ }
+ i := Instance{
+ Config: testConfig,
+ Stateman: stateman,
+ }
+
+ // Create HTTP request
+ url := types.EndpointAddCosignature.Path("http://example.com", i.Prefix)
+ req, err := http.NewRequest("POST", url, table.ascii)
if err != nil {
t.Fatalf("must create http request: %v", err)
}
+ // Run HTTP request
w := httptest.NewRecorder()
- ti.getHandler(t, EndpointGetStableSth).ServeHTTP(w, req)
+ mustHandle(t, i, types.EndpointAddCosignature).ServeHTTP(w, req)
if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description)
- }
- if w.Code != http.StatusOK {
- return
- }
-
- var item types.StItem
- if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil {
- t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err)
- }
- if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) {
- t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description)
+ t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
}
}()
}
}
-func TestEndpointGetCosignedSth(t *testing.T) {
+func TestGetTreeHeadLatest(t *testing.T) {
for _, table := range []struct {
- description string
- useBadSource bool
- wantCode int
- wantItem *types.StItem
+ description string
+ expect bool // set if a mock answer is expected
+ rsp *types.SignedTreeHead // signed tree head from Trillian client
+ err error // error from Trillian client
+ wantCode int // HTTP status ok
}{
{
- description: "invalid: sth source failure",
- useBadSource: true,
- wantCode: http.StatusInternalServerError,
+ description: "invalid: backend failure",
+ expect: true,
+ err: fmt.Errorf("something went wrong"),
+ wantCode: http.StatusInternalServerError,
},
{
description: "valid",
+ expect: true,
+ rsp: testSTH,
wantCode: http.StatusOK,
- wantItem: testdata.DefaultCosth(t, testdata.Ed25519VkLog, [][32]byte{testdata.Ed25519VkWitness}),
},
} {
- func() { // run deferred functions at the end of each iteration
- ti := newTestInstance(t, nil)
- ti.ctrl.Finish()
- if table.useBadSource {
- ti.instance.SthSource = &ActiveSthSource{}
+ // Run deferred functions at the end of each iteration
+ func() {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ stateman := mocks.NewMockStateManager(ctrl)
+ if table.expect {
+ stateman.EXPECT().Latest(gomock.Any()).Return(table.rsp, table.err)
+ }
+ i := Instance{
+ Config: testConfig,
+ Stateman: stateman,
}
- // Setup and run client query
- url := EndpointGetCosignedSth.Path("http://example.com", ti.instance.LogParameters.Prefix)
+ // Create HTTP request
+ url := types.EndpointGetTreeHeadLatest.Path("http://example.com", i.Prefix)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
t.Fatalf("must create http request: %v", err)
}
+ // Run HTTP request
w := httptest.NewRecorder()
- ti.getHandler(t, EndpointGetCosignedSth).ServeHTTP(w, req)
+ mustHandle(t, i, types.EndpointGetTreeHeadLatest).ServeHTTP(w, req)
if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description)
- }
- if w.Code != http.StatusOK {
- return
- }
-
- var item types.StItem
- if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil {
- t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err)
- }
- if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) {
- t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description)
+ t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
}
}()
}
}
-func TestEndpointGetProofByHash(t *testing.T) {
+func TestGetTreeToSign(t *testing.T) {
for _, table := range []struct {
description string
- breq *bytes.Buffer
- trsp *trillian.GetInclusionProofByHashResponse
- terr error
- wantCode int
- wantItem *types.StItem
+ expect bool // set if a mock answer is expected
+ rsp *types.SignedTreeHead // signed tree head from Trillian client
+ err error // error from Trillian client
+ wantCode int // HTTP status ok
}{
{
- description: "invalid: bad request: empty",
- breq: bytes.NewBuffer(nil),
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: bad Trillian response: error",
- breq: bytes.NewBuffer(marshal(t, types.GetProofByHashV1{TreeSize: 1, Hash: testdata.LeafHash})),
- terr: fmt.Errorf("backend failure"),
+ description: "invalid: backend failure",
+ expect: true,
+ err: fmt.Errorf("something went wrong"),
wantCode: http.StatusInternalServerError,
},
{
description: "valid",
- breq: bytes.NewBuffer(marshal(t, types.GetProofByHashV1{TreeSize: 1, Hash: testdata.LeafHash})),
- trsp: testdata.DefaultTGipbhr(t),
+ expect: true,
+ rsp: testSTH,
wantCode: http.StatusOK,
- wantItem: testdata.DefaultInclusionProof(t, 1),
},
} {
- func() { // run deferred functions at the end of each iteration
- ti := newTestInstance(t, nil)
- defer ti.ctrl.Finish()
+ // Run deferred functions at the end of each iteration
+ func() {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ stateman := mocks.NewMockStateManager(ctrl)
+ if table.expect {
+ stateman.EXPECT().ToSign(gomock.Any()).Return(table.rsp, table.err)
+ }
+ i := Instance{
+ Config: testConfig,
+ Stateman: stateman,
+ }
- url := EndpointGetProofByHash.Path("http://example.com", ti.instance.LogParameters.Prefix)
- req, err := http.NewRequest("POST", url, table.breq)
+ // Create HTTP request
+ url := types.EndpointGetTreeHeadToSign.Path("http://example.com", i.Prefix)
+ req, err := http.NewRequest("GET", url, nil)
if err != nil {
t.Fatalf("must create http request: %v", err)
}
- req.Header.Set("Content-Type", "application/octet-stream")
- if table.trsp != nil || table.terr != nil {
- ti.client.EXPECT().GetInclusionProofByHash(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
- }
+ // Run HTTP request
w := httptest.NewRecorder()
- ti.postHandler(t, EndpointGetProofByHash).ServeHTTP(w, req)
+ mustHandle(t, i, types.EndpointGetTreeHeadToSign).ServeHTTP(w, req)
if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description)
- }
- if w.Code != http.StatusOK {
- return
- }
-
- var item types.StItem
- if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil {
- t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err)
- }
- if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) {
- t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description)
+ t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
}
}()
}
}
-func TestEndpointGetConsistencyProof(t *testing.T) {
+func TestGetTreeCosigned(t *testing.T) {
for _, table := range []struct {
description string
- breq *bytes.Buffer
- trsp *trillian.GetConsistencyProofResponse
- terr error
- wantCode int
- wantItem *types.StItem
+ expect bool // set if a mock answer is expected
+ rsp *types.SignedTreeHead // signed tree head from Trillian client
+ err error // error from Trillian client
+ wantCode int // HTTP status ok
}{
{
- description: "invalid: bad request: empty",
- breq: bytes.NewBuffer(nil),
- wantCode: http.StatusBadRequest,
- },
- {
- description: "invalid: bad Trillian response: error",
- breq: bytes.NewBuffer(marshal(t, types.GetConsistencyProofV1{First: 1, Second: 2})),
- terr: fmt.Errorf("backend failure"),
+ description: "invalid: backend failure",
+ expect: true,
+ err: fmt.Errorf("something went wrong"),
wantCode: http.StatusInternalServerError,
},
{
description: "valid",
- breq: bytes.NewBuffer(marshal(t, types.GetConsistencyProofV1{First: 1, Second: 2})),
- trsp: testdata.DefaultTGcpr(t),
+ expect: true,
+ rsp: testSTH,
wantCode: http.StatusOK,
- wantItem: testdata.DefaultConsistencyProof(t, 1, 2),
},
} {
- func() { // run deferred functions at the end of each iteration
- ti := newTestInstance(t, nil)
- defer ti.ctrl.Finish()
+ // Run deferred functions at the end of each iteration
+ func() {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ stateman := mocks.NewMockStateManager(ctrl)
+ if table.expect {
+ stateman.EXPECT().Cosigned(gomock.Any()).Return(table.rsp, table.err)
+ }
+ i := Instance{
+ Config: testConfig,
+ Stateman: stateman,
+ }
- url := EndpointGetConsistencyProof.Path("http://example.com", ti.instance.LogParameters.Prefix)
- req, err := http.NewRequest("POST", url, table.breq)
+ // Create HTTP request
+ url := types.EndpointGetTreeHeadCosigned.Path("http://example.com", i.Prefix)
+ req, err := http.NewRequest("GET", url, nil)
if err != nil {
t.Fatalf("must create http request: %v", err)
}
- req.Header.Set("Content-Type", "application/octet-stream")
- if table.trsp != nil || table.terr != nil {
- ti.client.EXPECT().GetConsistencyProof(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
- }
+ // Run HTTP request
w := httptest.NewRecorder()
- ti.postHandler(t, EndpointGetConsistencyProof).ServeHTTP(w, req)
+ mustHandle(t, i, types.EndpointGetTreeHeadCosigned).ServeHTTP(w, req)
if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description)
- }
- if w.Code != http.StatusOK {
- return
- }
-
- var item types.StItem
- if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil {
- t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err)
- }
- if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) {
- t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description)
+ t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
}
}()
}
}
-func TestEndpointGetEntriesV1(t *testing.T) {
+func TestGetConsistencyProof(t *testing.T) {
+ buf := func(oldSize, newSize int) io.Reader {
+ return bytes.NewBufferString(fmt.Sprintf(
+ "%s%s%d%s"+"%s%s%d%s",
+ types.OldSize, types.Delim, oldSize, types.EOL,
+ types.NewSize, types.Delim, newSize, types.EOL,
+ ))
+ }
+ // values in testProof are not relevant for the test, just need a path
+ testProof := &types.ConsistencyProof{
+ OldSize: 1,
+ NewSize: 2,
+ Path: []*[types.HashSize]byte{
+ types.Hash(nil),
+ },
+ }
for _, table := range []struct {
description string
- breq *bytes.Buffer
- trsp *trillian.GetLeavesByRangeResponse
- terr error
- wantCode int
- wantItem *types.StItemList
+ ascii io.Reader // buffer used to populate HTTP request
+ expect bool // set if a mock answer is expected
+ rsp *types.ConsistencyProof // consistency proof from Trillian client
+ err error // error from Trillian client
+ wantCode int // HTTP status ok
}{
{
- description: "invalid: bad request: empty",
- breq: bytes.NewBuffer(nil),
+ description: "invalid: bad request (parser error)",
+ ascii: bytes.NewBufferString("key=value\n"),
wantCode: http.StatusBadRequest,
},
{
- description: "invalid: bad Trillian response: error",
- breq: bytes.NewBuffer(marshal(t, types.GetEntriesV1{Start: 0, End: 0})),
- terr: fmt.Errorf("backend failure"),
- wantCode: http.StatusInternalServerError,
- },
- {
- description: "valid", // remember that newLogParameters() have testdata.MaxRange configured
- breq: bytes.NewBuffer(marshal(t, types.GetEntriesV1{Start: 0, End: uint64(testdata.MaxRange - 1)})),
- trsp: testdata.DefaultTGlbrr(t, 0, testdata.MaxRange-1),
+ description: "valid",
+ ascii: buf(1, 2),
+ expect: true,
+ rsp: testProof,
wantCode: http.StatusOK,
- wantItem: testdata.DefaultStItemList(t, 0, uint64(testdata.MaxRange)-1),
},
} {
- func() { // run deferred functions at the end of each iteration
- ti := newTestInstance(t, nil)
- defer ti.ctrl.Finish()
-
- url := EndpointGetEntries.Path("http://example.com", ti.instance.LogParameters.Prefix)
- req, err := http.NewRequest("POST", url, table.breq)
+ // Run deferred functions at the end of each iteration
+ func() {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+ client := mocks.NewMockClient(ctrl)
+ if table.expect {
+ client.EXPECT().GetConsistencyProof(gomock.Any(), gomock.Any()).Return(table.rsp, table.err)
+ }
+ i := Instance{
+ Config: testConfig,
+ Client: client,
+ }
+
+ // Create HTTP request
+ url := types.EndpointGetConsistencyProof.Path("http://example.com", i.Prefix)
+ req, err := http.NewRequest("POST", url, table.ascii)
if err != nil {
t.Fatalf("must create http request: %v", err)
}
- req.Header.Set("Content-Type", "application/octet-stream")
- if table.trsp != nil || table.terr != nil {
- ti.client.EXPECT().GetLeavesByRange(newDeadlineMatcher(), gomock.Any()).Return(table.trsp, table.terr)
- }
+ // Run HTTP request
w := httptest.NewRecorder()
- ti.postHandler(t, EndpointGetEntries).ServeHTTP(w, req)
+ mustHandle(t, i, types.EndpointGetConsistencyProof).ServeHTTP(w, req)
if got, want := w.Code, table.wantCode; got != want {
- t.Errorf("got error code %d but wanted %d in test %q", got, want, table.description)
- }
- if w.Code != http.StatusOK {
- return
- }
-
- var item types.StItemList
- if err := types.Unmarshal([]byte(w.Body.String()), &item); err != nil {
- t.Errorf("valid response cannot be unmarshalled in test %q: %v", table.description, err)
- }
- if got, want := item, *table.wantItem; !reflect.DeepEqual(got, want) {
- t.Errorf("got item\n%v\n\tbut wanted\n%v\n\tin test %q", got, want, table.description)
+ t.Errorf("got HTTP status code %v but wanted %v in test %q", got, want, table.description)
}
}()
}
}
-// TODO: TestWriteOctetResponse
-func TestWriteOctetResponse(t *testing.T) {
-}
-
-// deadlineMatcher implements gomock.Matcher, such that an error is raised if
-// there is no context.Context deadline set
-type deadlineMatcher struct{}
-
-// newDeadlineMatcher returns a new DeadlineMatcher
-func newDeadlineMatcher() gomock.Matcher {
- return &deadlineMatcher{}
-}
-
-// Matches returns true if the passed interface is a context with a deadline
-func (dm *deadlineMatcher) Matches(i interface{}) bool {
- ctx, ok := i.(context.Context)
- if !ok {
- return false
- }
- _, ok = ctx.Deadline()
- return ok
+func TestGetInclusionProof(t *testing.T) {
}
-// String is needed to implement gomock.Matcher
-func (dm *deadlineMatcher) String() string {
- return fmt.Sprintf("deadlineMatcher{}")
+func TestGetLeaves(t *testing.T) {
}
diff --git a/pkg/instance/instance.go b/pkg/instance/instance.go
index 3441a0a..c2fe8fa 100644
--- a/pkg/instance/instance.go
+++ b/pkg/instance/instance.go
@@ -3,6 +3,7 @@ package stfe
import (
"context"
"crypto"
+ "crypto/ed25519"
"fmt"
"net/http"
"time"
@@ -88,3 +89,71 @@ func (a Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.Error(w, fmt.Sprintf("%s%s%s%s", "Error", types.Delim, err.Error(), types.EOL), statusCode)
}
}
+
+func (i *Instance) leafRequestFromHTTP(r *http.Request) (*types.LeafRequest, error) {
+ var req types.LeafRequest
+ if err := req.UnmarshalASCII(r.Body); err != nil {
+ return nil, fmt.Errorf("UnmarshalASCII: %v", err)
+ }
+
+ vk := ed25519.PublicKey(req.VerificationKey[:])
+ msg := req.Message.Marshal()
+ sig := req.Signature[:]
+ if !ed25519.Verify(vk, msg, sig) {
+ return nil, fmt.Errorf("invalid signature")
+ }
+ // TODO: check shard hint
+ // TODO: check domain hint
+ return &req, nil
+}
+
+func (i *Instance) cosignatureRequestFromHTTP(r *http.Request) (*types.CosignatureRequest, error) {
+ var req types.CosignatureRequest
+ if err := req.UnmarshalASCII(r.Body); err != nil {
+ return nil, fmt.Errorf("unpackOctetPost: %v", err)
+ }
+ if _, ok := i.Witnesses[*req.KeyHash]; !ok {
+ return nil, fmt.Errorf("Unknown witness: %x", req.KeyHash)
+ }
+ return &req, nil
+}
+
+func (i *Instance) consistencyProofRequestFromHTTP(r *http.Request) (*types.ConsistencyProofRequest, error) {
+ var req types.ConsistencyProofRequest
+ if err := req.UnmarshalASCII(r.Body); err != nil {
+ return nil, fmt.Errorf("UnmarshalASCII: %v", err)
+ }
+ if req.OldSize < 1 {
+ return nil, fmt.Errorf("OldSize(%d) must be larger than zero", req.OldSize)
+ }
+ if req.NewSize <= req.OldSize {
+ return nil, fmt.Errorf("NewSize(%d) must be larger than OldSize(%d)", req.NewSize, req.OldSize)
+ }
+ return &req, nil
+}
+
+func (i *Instance) inclusionProofRequestFromHTTP(r *http.Request) (*types.InclusionProofRequest, error) {
+ var req types.InclusionProofRequest
+ if err := req.UnmarshalASCII(r.Body); err != nil {
+ return nil, fmt.Errorf("UnmarshalASCII: %v", err)
+ }
+ if req.TreeSize < 1 {
+ return nil, fmt.Errorf("TreeSize(%d) must be larger than zero", req.TreeSize)
+ }
+ return &req, nil
+}
+
+func (i *Instance) leavesRequestFromHTTP(r *http.Request) (*types.LeavesRequest, error) {
+ var req types.LeavesRequest
+ if err := req.UnmarshalASCII(r.Body); err != nil {
+ return nil, fmt.Errorf("UnmarshalASCII: %v", err)
+ }
+
+ if req.StartSize > req.EndSize {
+ return nil, fmt.Errorf("StartSize(%d) must be less than or equal to EndSize(%d)", req.StartSize, req.EndSize)
+ }
+ if req.EndSize-req.StartSize+1 > uint64(i.MaxRange) {
+ req.EndSize = req.StartSize + uint64(i.MaxRange) - 1
+ }
+ return &req, nil
+}
diff --git a/pkg/instance/instance_test.go b/pkg/instance/instance_test.go
index a7a3d8a..45a2837 100644
--- a/pkg/instance/instance_test.go
+++ b/pkg/instance/instance_test.go
@@ -1,158 +1,9 @@
package stfe
import (
- "crypto"
- "net/http"
- "net/http/httptest"
"testing"
-
- "github.com/golang/mock/gomock"
- "github.com/google/certificate-transparency-go/trillian/mockclient"
- "github.com/system-transparency/stfe/pkg/testdata"
- "github.com/system-transparency/stfe/pkg/types"
)
-type testInstance struct {
- ctrl *gomock.Controller
- client *mockclient.MockTrillianLogClient
- instance *Instance
-}
-
-// newTestInstances sets up a test instance that uses default log parameters
-// with an optional signer, see newLogParameters() for further details. The
-// SthSource is instantiated with an ActiveSthSource that has (i) the default
-// STH as the currently cosigned STH based on testdata.Ed25519VkWitness, and
-// (ii) the default STH without any cosignatures as the currently stable STH.
-func newTestInstance(t *testing.T, signer crypto.Signer) *testInstance {
- t.Helper()
- ctrl := gomock.NewController(t)
- client := mockclient.NewMockTrillianLogClient(ctrl)
- return &testInstance{
- ctrl: ctrl,
- client: client,
- instance: &Instance{
- Client: client,
- LogParameters: newLogParameters(t, signer),
- SthSource: &ActiveSthSource{
- client: client,
- logParameters: newLogParameters(t, signer),
- currCosth: testdata.DefaultCosth(t, testdata.Ed25519VkLog, [][32]byte{testdata.Ed25519VkWitness}),
- nextCosth: testdata.DefaultCosth(t, testdata.Ed25519VkLog, nil),
- cosignatureFrom: make(map[[types.NamespaceFingerprintSize]byte]bool),
- },
- },
- }
-}
-
-// getHandlers returns all endpoints that use HTTP GET as a map to handlers
-func (ti *testInstance) getHandlers(t *testing.T) map[Endpoint]Handler {
- t.Helper()
- return map[Endpoint]Handler{
- EndpointGetLatestSth: Handler{Instance: ti.instance, Handler: getLatestSth, Endpoint: EndpointGetLatestSth, Method: http.MethodGet},
- EndpointGetStableSth: Handler{Instance: ti.instance, Handler: getStableSth, Endpoint: EndpointGetStableSth, Method: http.MethodGet},
- EndpointGetCosignedSth: Handler{Instance: ti.instance, Handler: getCosignedSth, Endpoint: EndpointGetCosignedSth, Method: http.MethodGet},
- }
-}
-
-// postHandlers returns all endpoints that use HTTP POST as a map to handlers
-func (ti *testInstance) postHandlers(t *testing.T) map[Endpoint]Handler {
- t.Helper()
- return map[Endpoint]Handler{
- EndpointAddEntry: Handler{Instance: ti.instance, Handler: addEntry, Endpoint: EndpointAddEntry, Method: http.MethodPost},
- EndpointAddCosignature: Handler{Instance: ti.instance, Handler: addCosignature, Endpoint: EndpointAddCosignature, Method: http.MethodPost},
- EndpointGetConsistencyProof: Handler{Instance: ti.instance, Handler: getConsistencyProof, Endpoint: EndpointGetConsistencyProof, Method: http.MethodPost},
- EndpointGetProofByHash: Handler{Instance: ti.instance, Handler: getProofByHash, Endpoint: EndpointGetProofByHash, Method: http.MethodPost},
- EndpointGetEntries: Handler{Instance: ti.instance, Handler: getEntries, Endpoint: EndpointGetEntries, Method: http.MethodPost},
- }
-}
-
-// getHandler must return a particular HTTP GET handler
-func (ti *testInstance) getHandler(t *testing.T, endpoint Endpoint) Handler {
- t.Helper()
- handler, ok := ti.getHandlers(t)[endpoint]
- if !ok {
- t.Fatalf("must return HTTP GET handler for endpoint: %s", endpoint)
- }
- return handler
-}
-
-// postHandler must return a particular HTTP POST handler
-func (ti *testInstance) postHandler(t *testing.T, endpoint Endpoint) Handler {
- t.Helper()
- handler, ok := ti.postHandlers(t)[endpoint]
- if !ok {
- t.Fatalf("must return HTTP POST handler for endpoint: %s", endpoint)
- }
- return handler
-}
-
-// TestHandlers checks that we configured all endpoints and that there are no
-// unexpected ones.
-func TestHandlers(t *testing.T) {
- endpoints := map[Endpoint]bool{
- EndpointAddEntry: false,
- EndpointAddCosignature: false,
- EndpointGetLatestSth: false,
- EndpointGetStableSth: false,
- EndpointGetCosignedSth: false,
- EndpointGetConsistencyProof: false,
- EndpointGetProofByHash: false,
- EndpointGetEntries: false,
- }
- i := &Instance{nil, newLogParameters(t, nil), nil}
- for _, handler := range i.Handlers() {
- if _, ok := endpoints[handler.Endpoint]; !ok {
- t.Errorf("got unexpected endpoint: %s", handler.Endpoint)
- }
- endpoints[handler.Endpoint] = true
- }
- for endpoint, ok := range endpoints {
- if !ok {
- t.Errorf("endpoint %s is not configured", endpoint)
- }
- }
-}
-
-// TestGetHandlersRejectPost checks that all get handlers reject post requests
-func TestGetHandlersRejectPost(t *testing.T) {
- ti := newTestInstance(t, nil)
- defer ti.ctrl.Finish()
-
- for endpoint, handler := range ti.getHandlers(t) {
- t.Run(string(endpoint), func(t *testing.T) {
- s := httptest.NewServer(handler)
- defer s.Close()
-
- url := endpoint.Path(s.URL, ti.instance.LogParameters.Prefix)
- if rsp, err := http.Post(url, "application/json", nil); err != nil {
- t.Fatalf("http.Post(%s)=(_,%q), want (_,nil)", url, err)
- } else if rsp.StatusCode != http.StatusMethodNotAllowed {
- t.Errorf("http.Post(%s)=(%d,nil), want (%d, nil)", url, rsp.StatusCode, http.StatusMethodNotAllowed)
- }
- })
- }
-}
-
-// TestPostHandlersRejectGet checks that all post handlers reject get requests
-func TestPostHandlersRejectGet(t *testing.T) {
- ti := newTestInstance(t, nil)
- defer ti.ctrl.Finish()
-
- for endpoint, handler := range ti.postHandlers(t) {
- t.Run(string(endpoint), func(t *testing.T) {
- s := httptest.NewServer(handler)
- defer s.Close()
-
- url := endpoint.Path(s.URL, ti.instance.LogParameters.Prefix)
- if rsp, err := http.Get(url); err != nil {
- t.Fatalf("http.Get(%s)=(_,%q), want (_,nil)", url, err)
- } else if rsp.StatusCode != http.StatusMethodNotAllowed {
- t.Errorf("http.Get(%s)=(%d,nil), want (%d, nil)", url, rsp.StatusCode, http.StatusMethodNotAllowed)
- }
- })
- }
-}
-
-// TODO: TestHandlerPath
-func TestHandlerPath(t *testing.T) {
-}
+func TestHandlers(t *testing.T) {}
+func TestPath(t *testing.T) {}
+func TestServeHTTP(t *testing.T) {}
diff --git a/pkg/instance/metric.go b/pkg/instance/metric.go
index 7e3e8b2..db11bd2 100644
--- a/pkg/instance/metric.go
+++ b/pkg/instance/metric.go
@@ -6,11 +6,9 @@ import (
)
var (
- reqcnt monitoring.Counter // number of incoming http requests
- rspcnt monitoring.Counter // number of valid http responses
- latency monitoring.Histogram // request-response latency
- lastSthTimestamp monitoring.Gauge // unix timestamp from the most recent sth
- lastSthSize monitoring.Gauge // tree size of most recent sth
+ reqcnt monitoring.Counter // number of incoming http requests
+ rspcnt monitoring.Counter // number of valid http responses
+ latency monitoring.Histogram // request-response latency
)
func init() {
@@ -18,6 +16,4 @@ func init() {
reqcnt = mf.NewCounter("http_req", "number of http requests", "logid", "endpoint")
rspcnt = mf.NewCounter("http_rsp", "number of http requests", "logid", "endpoint", "status")
latency = mf.NewHistogram("http_latency", "http request-response latency", "logid", "endpoint", "status")
- lastSthTimestamp = mf.NewGauge("last_sth_timestamp", "unix timestamp while handling the most recent sth", "logid")
- lastSthSize = mf.NewGauge("last_sth_size", "most recent sth tree size", "logid")
}
diff --git a/pkg/instance/request.go b/pkg/instance/request.go
deleted file mode 100644
index 7475b26..0000000
--- a/pkg/instance/request.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package stfe
-
-import (
- "crypto/ed25519"
- "fmt"
- "net/http"
-
- "github.com/system-transparency/stfe/pkg/types"
-)
-
-func (i *Instance) leafRequestFromHTTP(r *http.Request) (*types.LeafRequest, error) {
- var req types.LeafRequest
- if err := req.UnmarshalASCII(r.Body); err != nil {
- return nil, fmt.Errorf("UnmarshalASCII: %v", err)
- }
-
- vk := ed25519.PublicKey(req.VerificationKey[:])
- msg := req.Message.Marshal()
- sig := req.Signature[:]
- if !ed25519.Verify(vk, msg, sig) {
- return nil, fmt.Errorf("invalid signature")
- }
- // TODO: check shard hint
- // TODO: check domain hint
- return &req, nil
-}
-
-func (i *Instance) cosignatureRequestFromHTTP(r *http.Request) (*types.CosignatureRequest, error) {
- var req types.CosignatureRequest
- if err := req.UnmarshalASCII(r.Body); err != nil {
- return nil, fmt.Errorf("unpackOctetPost: %v", err)
- }
- if _, ok := i.Witnesses[*req.KeyHash]; !ok {
- return nil, fmt.Errorf("Unknown witness: %x", req.KeyHash)
- }
- return &req, nil
-}
-
-func (i *Instance) consistencyProofRequestFromHTTP(r *http.Request) (*types.ConsistencyProofRequest, error) {
- var req types.ConsistencyProofRequest
- if err := req.UnmarshalASCII(r.Body); err != nil {
- return nil, fmt.Errorf("UnmarshalASCII: %v", err)
- }
- if req.OldSize < 1 {
- return nil, fmt.Errorf("OldSize(%d) must be larger than zero", req.OldSize)
- }
- if req.NewSize <= req.OldSize {
- return nil, fmt.Errorf("NewSize(%d) must be larger than OldSize(%d)", req.NewSize, req.OldSize)
- }
- return &req, nil
-}
-
-func (i *Instance) inclusionProofRequestFromHTTP(r *http.Request) (*types.InclusionProofRequest, error) {
- var req types.InclusionProofRequest
- if err := req.UnmarshalASCII(r.Body); err != nil {
- return nil, fmt.Errorf("UnmarshalASCII: %v", err)
- }
- if req.TreeSize < 1 {
- return nil, fmt.Errorf("TreeSize(%d) must be larger than zero", req.TreeSize)
- }
- return &req, nil
-}
-
-func (i *Instance) leavesRequestFromHTTP(r *http.Request) (*types.LeavesRequest, error) {
- var req types.LeavesRequest
- if err := req.UnmarshalASCII(r.Body); err != nil {
- return nil, fmt.Errorf("UnmarshalASCII: %v", err)
- }
-
- if req.StartSize > req.EndSize {
- return nil, fmt.Errorf("StartSize(%d) must be less than or equal to EndSize(%d)", req.StartSize, req.EndSize)
- }
- if req.EndSize-req.StartSize+1 > uint64(i.MaxRange) {
- req.EndSize = req.StartSize + uint64(i.MaxRange) - 1
- }
- return &req, nil
-}
diff --git a/pkg/instance/request_test.go b/pkg/instance/request_test.go
deleted file mode 100644
index 0a5a908..0000000
--- a/pkg/instance/request_test.go
+++ /dev/null
@@ -1,318 +0,0 @@
-package stfe
-
-import (
- "bytes"
- //"fmt"
- "reflect"
- "testing"
- //"testing/iotest"
-
- "net/http"
-
- "github.com/system-transparency/stfe/pkg/testdata"
- "github.com/system-transparency/stfe/pkg/types"
-)
-
-func TestParseAddEntryV1Request(t *testing.T) {
- lp := newLogParameters(t, nil)
- for _, table := range []struct {
- description string
- breq *bytes.Buffer
- wantErr bool
- }{
- {
- description: "invalid: nothing to unpack",
- breq: bytes.NewBuffer(nil),
- wantErr: true,
- },
- {
- description: "invalid: not a signed checksum entry",
- breq: testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness, &testdata.Ed25519VkWitness),
- wantErr: true,
- },
- {
- description: "invalid: untrusted submitter", // only testdata.Ed25519VkSubmitter is registered by default in newLogParameters()
-
- breq: testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter2, testdata.Ed25519VkSubmitter2),
- wantErr: true,
- },
- {
- description: "invalid: signature does not cover message",
-
- breq: testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter2, testdata.Ed25519VkSubmitter),
- wantErr: true,
- },
- {
- description: "valid",
- breq: testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter, testdata.Ed25519VkSubmitter),
- }, // TODO: add test case that disables submitter policy (i.e., unregistered namespaces are accepted)
- } {
- url := EndpointAddEntry.Path("http://example.com", lp.Prefix)
- req, err := http.NewRequest("POST", url, table.breq)
- if err != nil {
- t.Fatalf("failed creating http request: %v", err)
- }
- req.Header.Set("Content-Type", "application/octet-stream")
-
- _, err = lp.parseAddEntryV1Request(req)
- if got, want := err != nil, table.wantErr; got != want {
- t.Errorf("got errror %v but wanted %v in test %q: %v", got, want, table.description, err)
- }
- }
-}
-
-func TestParseAddCosignatureV1Request(t *testing.T) {
- lp := newLogParameters(t, nil)
- for _, table := range []struct {
- description string
- breq *bytes.Buffer
- wantErr bool
- }{
- {
- description: "invalid: nothing to unpack",
- breq: bytes.NewBuffer(nil),
- wantErr: true,
- },
- {
- description: "invalid: not a cosigned sth",
- breq: testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter, testdata.Ed25519VkSubmitter),
- wantErr: true,
- },
- {
- description: "invalid: no cosignature",
- breq: testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness, nil),
- wantErr: true,
- },
- {
- description: "invalid: untrusted witness", // only testdata.Ed25519VkWitness is registered by default in newLogParameters()
- breq: testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness2, &testdata.Ed25519VkWitness2),
- wantErr: true,
- },
- {
- description: "invalid: signature does not cover message",
- breq: testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness2, &testdata.Ed25519VkWitness),
- wantErr: true,
- },
- {
- description: "valid",
- breq: testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness, &testdata.Ed25519VkWitness),
- }, // TODO: add test case that disables witness policy (i.e., unregistered namespaces are accepted)
- } {
- url := EndpointAddCosignature.Path("http://example.com", lp.Prefix)
- req, err := http.NewRequest("POST", url, table.breq)
- if err != nil {
- t.Fatalf("failed creating http request: %v", err)
- }
- req.Header.Set("Content-Type", "application/octet-stream")
-
- _, err = lp.parseAddCosignatureV1Request(req)
- if got, want := err != nil, table.wantErr; got != want {
- t.Errorf("got errror %v but wanted %v in test %q: %v", got, want, table.description, err)
- }
- }
-}
-
-func TestNewGetConsistencyProofRequest(t *testing.T) {
- lp := newLogParameters(t, nil)
- for _, table := range []struct {
- description string
- req *types.GetConsistencyProofV1
- wantErr bool
- }{
- {
- description: "invalid: nothing to unpack",
- req: nil,
- wantErr: true,
- },
- {
- description: "invalid: first must be larger than zero",
- req: &types.GetConsistencyProofV1{First: 0, Second: 0},
- wantErr: true,
- },
- {
- description: "invalid: second must be larger than first",
- req: &types.GetConsistencyProofV1{First: 2, Second: 1},
- wantErr: true,
- },
- {
- description: "valid",
- req: &types.GetConsistencyProofV1{First: 1, Second: 2},
- },
- } {
- var buf *bytes.Buffer
- if table.req == nil {
- buf = bytes.NewBuffer(nil)
- } else {
- buf = bytes.NewBuffer(marshal(t, *table.req))
- }
-
- url := EndpointGetConsistencyProof.Path("http://example.com", lp.Prefix)
- req, err := http.NewRequest("POST", url, buf)
- if err != nil {
- t.Fatalf("failed creating http request: %v", err)
- }
- req.Header.Set("Content-Type", "application/octet-stream")
-
- _, err = lp.parseGetConsistencyProofV1Request(req)
- if got, want := err != nil, table.wantErr; got != want {
- t.Errorf("got errror %v but wanted %v in test %q: %v", got, want, table.description, err)
- }
- }
-}
-
-func TestNewGetProofByHashRequest(t *testing.T) {
- lp := newLogParameters(t, nil)
- for _, table := range []struct {
- description string
- req *types.GetProofByHashV1
- wantErr bool
- }{
- {
- description: "invalid: nothing to unpack",
- req: nil,
- wantErr: true,
- },
- {
- description: "invalid: no entry in an empty tree",
- req: &types.GetProofByHashV1{TreeSize: 0, Hash: testdata.LeafHash},
- wantErr: true,
- },
- {
- description: "valid",
- req: &types.GetProofByHashV1{TreeSize: 1, Hash: testdata.LeafHash},
- },
- } {
- var buf *bytes.Buffer
- if table.req == nil {
- buf = bytes.NewBuffer(nil)
- } else {
- buf = bytes.NewBuffer(marshal(t, *table.req))
- }
-
- url := EndpointGetProofByHash.Path("http://example.com", lp.Prefix)
- req, err := http.NewRequest("POST", url, buf)
- if err != nil {
- t.Fatalf("failed creating http request: %v", err)
- }
- req.Header.Set("Content-Type", "application/octet-stream")
-
- _, err = lp.parseGetProofByHashV1Request(req)
- if got, want := err != nil, table.wantErr; got != want {
- t.Errorf("got errror %v but wanted %v in test %q: %v", got, want, table.description, err)
- }
- }
-}
-
-func TestParseGetEntriesV1Request(t *testing.T) {
- lp := newLogParameters(t, nil)
- for _, table := range []struct {
- description string
- req *types.GetEntriesV1
- wantErr bool
- wantReq *types.GetEntriesV1
- }{
- {
- description: "invalid: nothing to unpack",
- req: nil,
- wantErr: true,
- },
- {
- description: "invalid: start must be larger than end",
- req: &types.GetEntriesV1{Start: 1, End: 0},
- wantErr: true,
- },
- {
- description: "valid: want truncated range",
- req: &types.GetEntriesV1{Start: 0, End: uint64(testdata.MaxRange)},
- wantReq: &types.GetEntriesV1{Start: 0, End: uint64(testdata.MaxRange) - 1},
- },
- {
- description: "valid",
- req: &types.GetEntriesV1{Start: 0, End: 0},
- wantReq: &types.GetEntriesV1{Start: 0, End: 0},
- },
- } {
- var buf *bytes.Buffer
- if table.req == nil {
- buf = bytes.NewBuffer(nil)
- } else {
- buf = bytes.NewBuffer(marshal(t, *table.req))
- }
-
- url := EndpointGetEntries.Path("http://example.com", lp.Prefix)
- req, err := http.NewRequest("POST", url, buf)
- if err != nil {
- t.Fatalf("failed creating http request: %v", err)
- }
- req.Header.Set("Content-Type", "application/octet-stream")
-
- output, err := lp.parseGetEntriesV1Request(req)
- if got, want := err != nil, table.wantErr; got != want {
- t.Errorf("got errror %v but wanted %v in test %q: %v", got, want, table.description, err)
- }
- if err != nil {
- continue
- }
- if got, want := output, table.wantReq; !reflect.DeepEqual(got, want) {
- t.Errorf("got request\n%v\n\tbut wanted\n%v\n\t in test %q", got, want, table.description)
- }
- }
-}
-
-func TestUnpackOctetPost(t *testing.T) {
- for _, table := range []struct {
- description string
- req *http.Request
- out interface{}
- wantErr bool
- }{
- //{
- // description: "invalid: cannot read request body",
- // req: func() *http.Request {
- // req, err := http.NewRequest(http.MethodPost, "", iotest.ErrReader(fmt.Errorf("bad reader")))
- // if err != nil {
- // t.Fatalf("must make new http request: %v", err)
- // }
- // return req
- // }(),
- // out: &types.StItem{},
- // wantErr: true,
- //}, // testcase requires Go 1.16
- {
- description: "invalid: cannot unmarshal",
- req: func() *http.Request {
- req, err := http.NewRequest(http.MethodPost, "", bytes.NewBuffer(nil))
- if err != nil {
- t.Fatalf("must make new http request: %v", err)
- }
- return req
- }(),
- out: &types.StItem{},
- wantErr: true,
- },
- {
- description: "valid",
- req: func() *http.Request {
- req, err := http.NewRequest(http.MethodPost, "", bytes.NewBuffer([]byte{0}))
- if err != nil {
- t.Fatalf("must make new http request: %v", err)
- }
- return req
- }(),
- out: &struct{ SomeUint8 uint8 }{},
- },
- } {
- err := unpackOctetPost(table.req, table.out)
- if got, want := err != nil, table.wantErr; got != want {
- t.Errorf("got error %v but wanted %v in test %q", got, want, table.description)
- }
- }
-}
-
-func marshal(t *testing.T, out interface{}) []byte {
- b, err := types.Marshal(out)
- if err != nil {
- t.Fatalf("must marshal: %v", err)
- }
- return b
-}