Code

Add an initial version of sample code for my Go talk.
[go-talk.git] / grpc / integration / backend / backend_test.go
1 // Copyright (C) 2016 Sebastian 'tokkee' Harl <sh@tokkee.org>
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions
6 // are met:
7 // 1. Redistributions of source code must retain the above copyright
8 //    notice, this list of conditions and the following disclaimer.
9 // 2. Redistributions in binary form must reproduce the above copyright
10 //    notice, this list of conditions and the following disclaimer in the
11 //    documentation and/or other materials provided with the distribution.
12 //
13 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14 // ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
15 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
17 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 package backend_test
27 import (
28         "fmt"
29         "log"
30         "os"
31         "os/exec"
32         "path/filepath"
33         "strings"
34         "testing"
36         "github.com/golang/protobuf/proto"
37         "golang.org/x/net/context"
38         "google.golang.org/grpc"
40         pb "tokkee.org/go-talk/grpc/proto/backend"
41 )
43 var (
44         server string
45         client string
47         // TODO: probe for an unused port
48         port = "50051"
49 )
51 func init() {
52         gopath := os.Getenv("GOPATH")
53         if gopath == "" {
54                 panic("GOPATH not set")
55         }
57         server = filepath.Join(gopath, "bin", "server")
58         client = filepath.Join(gopath, "bin", "client")
60         for _, path := range []string{server, client} {
61                 if _, err := os.Stat(path); err != nil {
62                         if !os.IsNotExist(err) {
63                                 panic(err)
64                         }
65                         panic(fmt.Sprintf("%s and/or %s not found; run 'go install tokkee.org/go-talk/grpc/server tokkee.org/go-talk/grpc/client'", server, client))
66                 }
67         }
68 }
70 // setup sets up a Backend test instance and returns a client connected to it
71 // and a cleanup function to be called when done.
72 func setup() (pb.BackendClient, func(), error) {
73         srv := exec.Command(server, "--listen=:"+port)
74         if err := srv.Start(); err != nil {
75                 return nil, nil, fmt.Errorf("failed to start server: %v", err)
76         }
78         conn, err := grpc.Dial("localhost:"+port, grpc.WithInsecure())
79         if err != nil {
80                 srv.Process.Kill()
81                 return nil, nil, fmt.Errorf("failed to connect to server: %v", err)
82         }
84         return pb.NewBackendClient(conn), func() {
85                 conn.Close()
87                 if err := srv.Process.Kill(); err != nil {
88                         log.Printf("Failed to kill server process: %v", err)
89                 }
90                 srv.Wait()
91         }, nil
92 }
94 // TestServer runs sample queries against the backend server using the RPC
95 // interface and checks the results.
96 func TestServer(t *testing.T) {
97         ctx := context.Background()
98         c, cleanup, err := setup()
99         if err != nil {
100                 t.Fatalf("Setup failed: %v", err)
101         }
102         defer cleanup()
104         for _, test := range []struct {
105                 query    string
106                 wantErr  bool
107                 expected *pb.QueryReply
108         }{
109                 {
110                         query: "CounT 123456",
111                         expected: &pb.QueryReply{
112                                 Type: "COUNT",
113                                 N:    6,
114                         },
115                 },
116                 {
117                         query: "count abc",
118                         expected: &pb.QueryReply{
119                                 Type: "COUNT",
120                                 N:    3,
121                         },
122                 },
123                 {
124                         query: "count multiple words are supported as well",
125                         expected: &pb.QueryReply{
126                                 Type: "COUNT",
127                                 N:    36,
128                         },
129                 },
130                 {
131                         query: "RANDOM 7",
132                         expected: &pb.QueryReply{
133                                 Type: "RANDOM",
134                                 N:    4,
135                         },
136                 },
137                 {
138                         query: "RANDOM 4",
139                         expected: &pb.QueryReply{
140                                 Type: "RANDOM",
141                                 N:    0,
142                         },
143                 },
144                 {
145                         query:   "RANDOM NAN",
146                         wantErr: true,
147                 },
148                 {
149                         query:   "COUNT",
150                         wantErr: true,
151                 },
152                 {
153                         query:   "INVALID COMMAND",
154                         wantErr: true,
155                 },
156         } {
157                 req := &pb.QueryRequest{Query: test.query}
158                 res, err := c.Query(ctx, req)
159                 if (err != nil) != test.wantErr || !proto.Equal(res, test.expected) {
160                         e := "<nil>"
161                         if test.wantErr {
162                                 e = "<ERR>"
163                         }
164                         t.Errorf("c.Query(%v) = %v, %v; want %v, %s", req, res, err, test.expected, e)
165                 }
166         }
169 // TestClient runs sample queries against the backend server using the client
170 // program and checks the results.
171 func TestClient(t *testing.T) {
172         _, cleanup, err := setup()
173         if err != nil {
174                 t.Fatalf("Setup failed: %v", err)
175         }
176         defer cleanup()
178         for _, test := range []struct {
179                 query    string
180                 wantErr  bool
181                 expected string
182         }{
183                 {
184                         query:    "count 123456",
185                         expected: "COUNT: 6",
186                 },
187                 {
188                         query:    "count abc",
189                         expected: "COUNT: 3",
190                 },
191                 {
192                         query:    "count multiple words are supported as well",
193                         expected: "COUNT: 36",
194                 },
195                 {
196                         query:    "RANDOM 7",
197                         expected: "RANDOM: 4",
198                 },
199                 {
200                         query:    "RANDOM 4",
201                         expected: "RANDOM: 0",
202                 },
203                 {
204                         query:   "RANDOM NAN",
205                         wantErr: true,
206                 },
207                 {
208                         query:   "COUNT",
209                         wantErr: true,
210                 },
211                 {
212                         query:   "INVALID COMMAND",
213                         wantErr: true,
214                 },
215         } {
216                 out, err := exec.Command(client, "--server=localhost:"+port, test.query).CombinedOutput()
217                 if (err != nil) != test.wantErr || !strings.HasSuffix(string(out), test.expected+"\n") {
218                         e := "<nil>"
219                         if test.wantErr {
220                                 e = "<ERR>"
221                         }
222                         t.Errorf("%s %s returned %q, %v; want %q, %s", client, test.query, string(out), err, test.expected, e)
223                 }
224         }