// Copyright 2019 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package graphql import ( "fmt" "io/ioutil" "net/http" "strings" "testing" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/stretchr/testify/assert" ) func TestBuildSchema(t *testing.T) { stack, err := node.New(&node.DefaultConfig) if err != nil { t.Fatalf("could not create new node: %v", err) } // Make sure the schema can be parsed and matched up to the object model. if err := newHandler(stack, nil, []string{}, []string{}); err != nil { t.Errorf("Could not construct GraphQL handler: %v", err) } } // Tests that a graphQL request is successfully handled when graphql is enabled on the specified endpoint func TestGraphQLHTTPOnSamePort_GQLRequest_Successful(t *testing.T) { stack := createNode(t, true) defer stack.Close() // start node if err := stack.Start(); err != nil { t.Fatalf("could not start node: %v", err) } // create http request body := strings.NewReader("{\"query\": \"{block{number}}\",\"variables\": null}") gqlReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/graphql", "127.0.0.1:9393"), body) if err != nil { t.Error("could not issue new http request ", err) } gqlReq.Header.Set("Content-Type", "application/json") // read from response resp := doHTTPRequest(t, gqlReq) bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("could not read from response body: %v", err) } expected := "{\"data\":{\"block\":{\"number\":\"0x0\"}}}" assert.Equal(t, 200, resp.StatusCode) assert.Equal(t, expected, string(bodyBytes)) } // Tests that a graphQL request is not handled successfully when graphql is not enabled on the specified endpoint func TestGraphQLHTTPOnSamePort_GQLRequest_Unsuccessful(t *testing.T) { stack := createNode(t, false) defer stack.Close() if err := stack.Start(); err != nil { t.Fatalf("could not start node: %v", err) } // create http request body := strings.NewReader("{\"query\": \"{block{number}}\",\"variables\": null}") gqlReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s/graphql", "127.0.0.1:9393"), body) if err != nil { t.Error("could not issue new http request ", err) } gqlReq.Header.Set("Content-Type", "application/json") // read from response resp := doHTTPRequest(t, gqlReq) bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("could not read from response body: %v", err) } // make sure the request is not handled successfully assert.Equal(t, 404, resp.StatusCode) assert.Equal(t, "404 page not found\n", string(bodyBytes)) } // Tests that 400 is returned when an invalid RPC request is made. func TestGraphQL_BadRequest(t *testing.T) { stack := createNode(t, true) defer stack.Close() // start node if err := stack.Start(); err != nil { t.Fatalf("could not start node: %v", err) } // create http request body := strings.NewReader("{\"query\": \"{bleh{number}}\",\"variables\": null}") gqlReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/graphql", "127.0.0.1:9393"), body) if err != nil { t.Error("could not issue new http request ", err) } gqlReq.Header.Set("Content-Type", "application/json") // read from response resp := doHTTPRequest(t, gqlReq) bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("could not read from response body: %v", err) } expected := "{\"errors\":[{\"message\":\"Cannot query field \\\"bleh\\\" on type \\\"Query\\\".\",\"locations\":[{\"line\":1,\"column\":2}]}]}" assert.Equal(t, expected, string(bodyBytes)) assert.Equal(t, 400, resp.StatusCode) } func createNode(t *testing.T, gqlEnabled bool) *node.Node { stack, err := node.New(&node.Config{ HTTPHost: "127.0.0.1", HTTPPort: 9393, WSHost: "127.0.0.1", WSPort: 9393, }) if err != nil { t.Fatalf("could not create node: %v", err) } if !gqlEnabled { return stack } createGQLService(t, stack, "127.0.0.1:9393") return stack } func createGQLService(t *testing.T, stack *node.Node, endpoint string) { // create backend (use a config which is light on mem consumption) ethConf := ð.Config{ Genesis: core.DeveloperGenesisBlock(15, common.Address{}), Miner: miner.Config{ Etherbase: common.HexToAddress("0xaabb"), }, Ethash: ethash.Config{ PowMode: ethash.ModeTest, }, NetworkId: 1337, TrieCleanCache: 5, TrieCleanCacheJournal: "triecache", TrieCleanCacheRejournal: 60 * time.Minute, TrieDirtyCache: 5, TrieTimeout: 60 * time.Minute, SnapshotCache: 5, } ethBackend, err := eth.New(stack, ethConf) if err != nil { t.Fatalf("could not create eth backend: %v", err) } // create gql service err = New(stack, ethBackend.APIBackend, []string{}, []string{}) if err != nil { t.Fatalf("could not create graphql service: %v", err) } } func doHTTPRequest(t *testing.T, req *http.Request) *http.Response { client := &http.Client{} resp, err := client.Do(req) if err != nil { t.Fatal("could not issue a GET request to the given endpoint", err) } return resp }