diff options
author | Rasmus Dahlberg <rasmus.dahlberg@kau.se> | 2021-02-25 14:36:35 +0100 |
---|---|---|
committer | Rasmus Dahlberg <rasmus.dahlberg@kau.se> | 2021-02-25 14:36:35 +0100 |
commit | c05c22ddbc771e7713849cae40f9d91bfafa0503 (patch) | |
tree | b97d11ab2a914806e6f671f9aff1cab9767b2eab /endpoint_test.go | |
parent | c9b4b43654f0ff26207cc63449f13298cd3c56e8 (diff) |
major refactor based on README.md and TODOs
Updated types, improved units tests, isolated most test data to have
it in one place, renamed and created new files to improve readability,
and fixed a bunch of minor TODOs.
Diffstat (limited to 'endpoint_test.go')
-rw-r--r-- | endpoint_test.go | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/endpoint_test.go b/endpoint_test.go new file mode 100644 index 0000000..7772837 --- /dev/null +++ b/endpoint_test.go @@ -0,0 +1,504 @@ +package stfe + +import ( + "bytes" + "fmt" + "reflect" + "testing" + + "net/http" + "net/http/httptest" + + "github.com/golang/mock/gomock" + cttestdata "github.com/google/certificate-transparency-go/trillian/testdata" + "github.com/google/trillian" + "github.com/system-transparency/stfe/testdata" + "github.com/system-transparency/stfe/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, + }, + { + description: "invalid: bad Trillian response: error", + breq: testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter, testdata.Ed25519VkSubmitter), + terr: fmt.Errorf("backend failure"), + wantCode: http.StatusInternalServerError, + }, + { + description: "valid", + breq: testdata.AddSignedChecksumBuffer(t, testdata.Ed25519SkSubmitter, testdata.Ed25519VkSubmitter), + trsp: testdata.DefaultTQlr(t, false), + wantCode: http.StatusOK, + }, + } { + 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(gomock.Any(), gomock.Any()).Return(table.trsp, table.terr) // TODO: deadline matcher? + } + + 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 TestEndpointAddCosignature(t *testing.T) { + for _, table := range []struct { + description string + breq *bytes.Buffer + wantCode int + }{ + { + description: "invalid: bad request: empty", + breq: bytes.NewBuffer(nil), + 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: "valid", + breq: testdata.AddCosignatureBuffer(t, testdata.DefaultSth(t, testdata.Ed25519VkLog), &testdata.Ed25519SkWitness, &testdata.Ed25519VkWitness), + 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) + if err != nil { + t.Fatalf("must create http request: %v", err) + } + req.Header.Set("Content-Type", "application/octet-stream") + + w := httptest.NewRecorder() + ti.postHandler(t, 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) + } + }() + } +} + +func TestEndpointGetLatestSth(t *testing.T) { + for _, table := range []struct { + description string + trsp *trillian.GetLatestSignedLogRootResponse + terr error + wantCode int + wantItem *types.StItem + }{ + { + description: "backend failure", + terr: fmt.Errorf("backend failure"), + wantCode: http.StatusInternalServerError, + }, + { + description: "valid", + trsp: testdata.DefaultTSlr(t), + wantCode: http.StatusOK, + wantItem: testdata.DefaultSth(t, testdata.Ed25519VkLog), + }, + } { + 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(gomock.Any(), gomock.Any()).Return(table.trsp, table.terr) // TODO: deadline matcher? + } + + 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: "valid", + 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) + if err != nil { + t.Fatalf("must create http request: %v", err) + } + + w := httptest.NewRecorder() + ti.getHandler(t, EndpointGetStableSth).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 TestEndpointGetCosignedSth(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: "valid", + 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{} + } + + // Setup and run client query + url := EndpointGetCosignedSth.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) + } + + w := httptest.NewRecorder() + ti.getHandler(t, EndpointGetCosignedSth).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 TestEndpointGetProofByHash(t *testing.T) { + for _, table := range []struct { + description string + breq *bytes.Buffer + trsp *trillian.GetInclusionProofByHashResponse + terr error + wantCode int + wantItem *types.StItem + }{ + { + 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"), + wantCode: http.StatusInternalServerError, + }, + { + description: "valid", + breq: bytes.NewBuffer(marshal(t, types.GetProofByHashV1{TreeSize: 1, Hash: testdata.LeafHash})), + trsp: testdata.DefaultTGipbhr(t), + 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() + + url := EndpointGetProofByHash.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().GetInclusionProofByHash(gomock.Any(), gomock.Any()).Return(table.trsp, table.terr) // TODO: deadline matcher? + } + + w := httptest.NewRecorder() + ti.postHandler(t, EndpointGetProofByHash).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 TestEndpointGetConsistencyProof(t *testing.T) { + for _, table := range []struct { + description string + breq *bytes.Buffer + trsp *trillian.GetConsistencyProofResponse + terr error + wantCode int + wantItem *types.StItem + }{ + { + 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"), + wantCode: http.StatusInternalServerError, + }, + { + description: "valid", + breq: bytes.NewBuffer(marshal(t, types.GetConsistencyProofV1{First: 1, Second: 2})), + trsp: testdata.DefaultTGcpr(t), + 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() + + url := EndpointGetConsistencyProof.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().GetConsistencyProof(gomock.Any(), gomock.Any()).Return(table.trsp, table.terr) // TODO: deadline matcher? + } + + w := httptest.NewRecorder() + ti.postHandler(t, 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.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 TestEndpointGetEntriesV1(t *testing.T) { + for _, table := range []struct { + description string + breq *bytes.Buffer + trsp *trillian.GetLeavesByRangeResponse + terr error + wantCode int + wantItem *types.StItemList + }{ + { + description: "invalid: bad request: empty", + breq: bytes.NewBuffer(nil), + 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), + 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) + 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(gomock.Any(), gomock.Any()).Return(table.trsp, table.terr) // TODO: deadline matcher? + } + + w := httptest.NewRecorder() + ti.postHandler(t, EndpointGetEntries).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) + } + }() + } +} + +func TestEndpointPath(t *testing.T) { + base, prefix, proto := "http://example.com", "test", "st/v1" + for _, table := range []struct { + endpoint Endpoint + want string + }{ + { + endpoint: EndpointAddEntry, + want: "http://example.com/test/st/v1/add-entry", + }, + { + endpoint: EndpointAddCosignature, + want: "http://example.com/test/st/v1/add-cosignature", + }, + { + endpoint: EndpointGetLatestSth, + want: "http://example.com/test/st/v1/get-latest-sth", + }, + { + endpoint: EndpointGetStableSth, + want: "http://example.com/test/st/v1/get-stable-sth", + }, + { + endpoint: EndpointGetCosignedSth, + want: "http://example.com/test/st/v1/get-cosigned-sth", + }, + { + endpoint: EndpointGetConsistencyProof, + want: "http://example.com/test/st/v1/get-consistency-proof", + }, + { + endpoint: EndpointGetProofByHash, + want: "http://example.com/test/st/v1/get-proof-by-hash", + }, + { + endpoint: EndpointGetEntries, + want: "http://example.com/test/st/v1/get-entries", + }, + } { + if got, want := table.endpoint.Path(base+"/"+prefix+"/"+proto), table.want; got != want { + t.Errorf("got endpoint\n%s\n\tbut wanted\n%s\n\twith one component", got, want) + } + if got, want := table.endpoint.Path(base, prefix, proto), table.want; got != want { + t.Errorf("got endpoint\n%s\n\tbut wanted\n%s\n\tmultiple components", got, want) + } + } +} + +// TODO: TestWriteOctetResponse +func TestWriteOctetResponse(t *testing.T) { +} |