package stfe import ( "bytes" "fmt" "strconv" "testing" "net/http" "github.com/system-transparency/stfe/namespace/testdata" ) func TestNewAddCosignatureRequest(t *testing.T) { lp := makeTestLogParameters(t, nil) validSth := NewSignedTreeHeadV1(NewTreeHeadV1(makeTrillianLogRoot(t, testTimestamp, testTreeSize, testNodeHash)), testLogId, testSignature) for _, table := range []struct { description string breq *bytes.Buffer wantErr bool }{ // TODO: test cases for all errors + add wantBytes for valid cases { description: "invalid: unknown witness", breq: mustMakeAddCosiBuffer(t, testdata.Ed25519Sk2, testdata.Ed25519Vk2, validSth), wantErr: true, }, { description: "invalid: bad signature", breq: mustMakeAddCosiBuffer(t, testdata.Ed25519Sk, testdata.Ed25519Vk2, validSth), wantErr: true, }, { description: "valid", breq: mustMakeAddCosiBuffer(t, testdata.Ed25519Sk, testdata.Ed25519Vk, validSth), }, } { 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/json") _, err = lp.newAddCosignatureRequest(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) } } } // TODO: TestNewAddEntryRequest func TestNewAddEntryRequest(t *testing.T) { } func TestNewGetEntriesRequest(t *testing.T) { lp := makeTestLogParameters(t, nil) for _, table := range []struct { description string start string end string wantErr bool }{ { description: "bad request: start must be an integer", start: "start", end: "10", wantErr: true, }, { description: "bad request: end must be an integer", start: "10", end: "end", wantErr: true, }, { description: "bad request: start must not be negative", start: "-1", end: "10", wantErr: true, }, { description: "bad request: start must be larger than end", start: "1", end: "0", wantErr: true, }, { description: "ok request but bad response: expected truncated", start: "0", end: fmt.Sprintf("%d", testMaxRange), }, { description: "ok request and response", start: "0", end: "0", }, { description: "ok request and response", start: "0", end: fmt.Sprintf("%d", testMaxRange-1), }, } { url := EndpointGetEntries.Path("http://example.com/", lp.Prefix) r, err := http.NewRequest("GET", url, nil) if err != nil { t.Fatalf("must make http request in test %q: %v", table.description, err) } q := r.URL.Query() q.Add("start", table.start) q.Add("end", table.end) r.URL.RawQuery = q.Encode() req, err := lp.newGetEntriesRequest(r) if got, want := err != nil, table.wantErr; got != want { t.Errorf("got error is %v but wanted %v in test %q: %v", got, want, table.description, err) } if err != nil { continue } if got, want := req.Start, mustParseInt64(t, table.start); got != want { t.Errorf("got start %d but wanted %d in test %q", got, want, table.description) } if got, want := req.End, min(mustParseInt64(t, table.end), req.Start+testMaxRange-1); got != want { t.Errorf("got end %d but wanted %d in test %q", got, want, table.description) } } } func TestNewGetProofByHashRequest(t *testing.T) { lp := makeTestLogParameters(t, nil) for _, table := range []struct { description string treeSize string hash string wantErr bool }{ { description: "bad request: tree size must be an integer", treeSize: "treeSize", hash: b64(testNodeHash), wantErr: true, }, { description: "bad request: tree size must be larger than zero", treeSize: "0", hash: b64(testNodeHash), wantErr: true, }, { description: "bad request: hash is not base64", treeSize: "1", hash: "<(^_^)>", wantErr: true, }, { description: "bad request: invalid node hash (too small)", treeSize: "1", hash: b64(testNodeHash[1:]), wantErr: true, }, { description: "bad request: invalid node hash (too large)", treeSize: "1", hash: b64(append(testNodeHash, byte(0))), wantErr: true, }, { description: "ok request", treeSize: "1", hash: b64(testNodeHash), }, } { url := EndpointGetProofByHash.Path("http://example.com/", lp.Prefix) r, err := http.NewRequest("GET", url, nil) if err != nil { t.Fatalf("must make http request in test %q: %v", table.description, err) } q := r.URL.Query() q.Add("tree_size", table.treeSize) q.Add("hash", table.hash) r.URL.RawQuery = q.Encode() req, err := lp.newGetProofByHashRequest(r) if got, want := err != nil, table.wantErr; got != want { t.Errorf("got error is %v but wanted %v in test %q: %v", got, want, table.description, err) } if err != nil { continue } if got, want := req.TreeSize, mustParseInt64(t, table.treeSize); got != want { t.Errorf("got treeSize %d but wanted %d in test %q", got, want, table.description) } if got, want := req.Hash, mustDeb64(t, table.hash); !bytes.Equal(got, want) { t.Errorf("got hash %X but wanted %X in test %q", got, want, table.description) } } } func TestNewGetConsistencyProofRequest(t *testing.T) { lp := makeTestLogParameters(t, nil) for _, table := range []struct { description string first string second string wantErr bool }{ { description: "bad reuqest: first must be an integer", first: "first", second: "1", wantErr: true, }, { description: "bad request: second must be an integer", first: "1", second: "second", wantErr: true, }, { description: "bad request: first must be larger than zero", first: "0", second: "2", wantErr: true, }, { description: "bad request: second must be larger than firsst", first: "2", second: "1", wantErr: true, }, { description: "ok request", first: "1", second: "2", }, } { url := EndpointGetConsistencyProof.Path("http://example.com/", lp.Prefix) r, err := http.NewRequest("GET", url, nil) if err != nil { t.Fatalf("must make http request in test %q: %v", table.description, err) } q := r.URL.Query() q.Add("first", table.first) q.Add("second", table.second) r.URL.RawQuery = q.Encode() req, err := lp.newGetConsistencyProofRequest(r) if got, want := err != nil, table.wantErr; got != want { t.Errorf("got error is %v but wanted %v in test %q: %v", got, want, table.description, err) } if err != nil { continue } if got, want := req.First, mustParseInt64(t, table.first); got != want { t.Errorf("got first %d but wanted %d in test %q", got, want, table.description) } if got, want := req.Second, mustParseInt64(t, table.second); got != want { t.Errorf("got second %d but wanted %d in test %q", got, want, table.description) } } } // TODO: refactor TestNewGetEntryResponse func TestNewGetEntryResponse(t *testing.T) { //lp := makeTestLogParameters(t, nil) //var appendix Appendix //leaf, app := makeTestLeaf(t, testPackage, testdata.RootChain, testdata.EndEntityPrivateKey) //if err := appendix.Unmarshal(app); err != nil { // t.Fatalf("must unmarshal appendix: %v", err) //} //if _, err := lp.newGetEntryResponse(leaf, app[1:]); err == nil { // t.Errorf("got no error invalid appendix") //} //// Valid response //rsp, err := lp.newGetEntryResponse(leaf, app) //if err != nil { // t.Errorf("got error %v but wanted none", err) // return //} //if got, want := rsp.Item, leaf; !bytes.Equal(got, want) { // t.Errorf("got leaf %X but wanted %X", got, want) //} //if got, want := rsp.Signature, appendix.Signature; !bytes.Equal(got, want) { // t.Errorf("got signature %X but wanted %X", got, want) //} //if got, want := rsp.SignatureScheme, appendix.SignatureScheme; got != want { // t.Errorf("got signature scheme %d but wanted %d", got, want) //} //if got, want := len(rsp.Chain), len(appendix.Chain); got != want { // t.Errorf("got chain length %d but wanted %d", got, want) //} //for i, n := 0, len(rsp.Chain); i < n; i++ { // if got, want := rsp.Chain[i], appendix.Chain[i].Data; !bytes.Equal(got, want) { // t.Errorf("got chain[%d]=%X but wanted %X", i, got, want) // } //} } // TODO: refactor TestNewGetEntriesResponse func TestNewGetEntriesResponse(t *testing.T) { //lp := makeTestLogParameters(t, nil) //// Invalid //leaf := makeTrillianQueueLeafResponse(t, testPackage, testdata.RootChain, testdata.EndEntityPrivateKey, false).QueuedLeaf.Leaf //leaf.ExtraData = leaf.ExtraData[1:] //if _, err := lp.newGetEntriesResponse([]*trillian.LogLeaf{leaf}); err == nil { // t.Errorf("got no error for invalid appendix") //} //// Valid, including empty //for n, numEntries := 0, 5; n < numEntries; n++ { // leaves := make([]*trillian.LogLeaf, 0, n) // for i := 0; i < n; i++ { // leaves = append(leaves, makeTrillianQueueLeafResponse(t, []byte(fmt.Sprintf("%s-%d", testPackage, i)), testdata.RootChain, testdata.EndEntityPrivateKey, false).QueuedLeaf.Leaf) // } // if rsp, err := lp.newGetEntriesResponse(leaves); err != nil { // t.Errorf("got error for %d valid leaves: %v", n, err) // } else if got, want := len(rsp), n; got != want { // t.Errorf("got %d leaves but wanted %d", got, want) // } // // note that we tested actual leaf contents in TestNewGetEntryResponse //} } // TODO: refactor TestNewGetAnchorsResponse func TestNewGetAnchorsResponse(t *testing.T) { //rawAnchors := makeTestLogParameters(t, nil).newGetAnchorsResponse() //if got, want := len(rawAnchors), testdata.NumTrustAnchors; got != want { // t.Errorf("got %d anchors but wanted %d", got, want) //} //for _, rawAnchor := range rawAnchors { // if _, err := x509.ParseCertificate(rawAnchor); err != nil { // t.Errorf("invalid trust anchor %X: %v", rawAnchor, err) // } //} } func mustParseInt64(t *testing.T, num string) int64 { n, err := strconv.ParseInt(num, 10, 64) if err != nil { t.Fatalf("must parse int: %v", err) } return n } func mustDeb64(t *testing.T, str string) []byte { b, err := deb64(str) if err != nil { t.Fatalf("must base64 decode: %v", err) } return b } func min(a, b int64) int64 { if a < b { return a } return b }