handler_test.go 8.12 KB
Newer Older
1 2 3
package http

import (
4
	"bytes"
5
	"errors"
6 7
	"fmt"
	"io"
keks's avatar
keks committed
8
	"io/ioutil"
9
	"net/http/httptest"
10
	"runtime"
11

12 13
	"testing"

Jan Winkelmann's avatar
Jan Winkelmann committed
14
	cmds "github.com/ipfs/go-ipfs-cmds"
Łukasz Magiera's avatar
Łukasz Magiera committed
15
	files "github.com/ipfs/go-ipfs-files"
16 17
)

18 19 20 21 22 23
type VersionOutput struct {
	Version string
	Commit  string
	Repo    string
	System  string
	Golang  string
24 25
}

26 27
type testEnv struct {
	version, commit, repoVersion string
keks's avatar
keks committed
28
	t                            *testing.T
29
	wait                         chan struct{}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
30 31
}

32
func getCommit(env cmds.Environment) (string, bool) {
33 34
	tEnv, ok := env.(testEnv)
	return tEnv.commit, ok
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
35 36
}

37
func getVersion(env cmds.Environment) (string, bool) {
38 39
	tEnv, ok := env.(testEnv)
	return tEnv.version, ok
40 41
}

42
func getRepoVersion(env cmds.Environment) (string, bool) {
43 44
	tEnv, ok := env.(testEnv)
	return tEnv.repoVersion, ok
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
45 46
}

keks's avatar
keks committed
47 48 49 50 51
func getTestingT(env cmds.Environment) (*testing.T, bool) {
	tEnv, ok := env.(testEnv)
	return tEnv.t, ok
}

52 53 54 55 56 57
func getWaitChan(env cmds.Environment) (chan struct{}, bool) {
	tEnv, ok := env.(testEnv)
	return tEnv.wait, ok

}

58 59
var (
	cmdRoot = &cmds.Command{
Steven Allen's avatar
Steven Allen committed
60
		Options: []cmds.Option{
61 62 63 64 65
			// global options, added to every command
			cmds.OptionEncodingType,
			cmds.OptionStreamChannels,
			cmds.OptionTimeout,
		},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66

67
		Subcommands: map[string]*cmds.Command{
68 69 70 71 72
			"error": &cmds.Command{
				Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
					return errors.New("an error occurred")
				},
			},
keks's avatar
keks committed
73 74 75 76 77 78 79
			"lateerror": &cmds.Command{
				Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
					re.Emit("some value")
					return errors.New("an error occurred")
				},
				Type: "",
			},
Steven Allen's avatar
Steven Allen committed
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
			"encode": &cmds.Command{
				Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
					return errors.New("an error occurred")
				},
				Type: "",
				Encoders: cmds.EncoderMap{
					cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, v string) error {
						fmt.Fprintln(w, v)
						return nil
					}),
				},
			},
			"lateencode": &cmds.Command{
				Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
					re.Emit("hello")
					return errors.New("an error occurred")
				},
				Type: "",
				Encoders: cmds.EncoderMap{
					cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, v string) error {
						fmt.Fprintln(w, v)
						if v != "hello" {
							return fmt.Errorf("expected hello, got %s", v)
						}
						return nil
					}),
				},
			},
Steven Allen's avatar
Steven Allen committed
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
			"protoencode": &cmds.Command{
				Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
					return errors.New("an error occurred")
				},
				Type: "",
				Encoders: cmds.EncoderMap{
					cmds.Protobuf: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, v string) error {
						fmt.Fprintln(w, v)
						return nil
					}),
				},
			},
			"protolateencode": &cmds.Command{
				Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
					re.Emit("hello")
					return errors.New("an error occurred")
				},
				Type: "",
				Encoders: cmds.EncoderMap{
					cmds.Protobuf: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, v string) error {
						fmt.Fprintln(w, v)
						return nil
					}),
				},
			},
keks's avatar
keks committed
133 134
			"doubleclose": &cmds.Command{
				Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
keks's avatar
keks committed
135 136 137 138
					t, ok := getTestingT(env)
					if !ok {
						return errors.New("error getting *testing.T")
					}
keks's avatar
keks committed
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155

					re.Emit("some value")

					err := re.Close()
					if err != nil {
						t.Error("unexpected error closing:", err)
					}

					err = re.Close()
					if err != cmds.ErrClosingClosedEmitter {
						t.Error("expected double close error, got:", err)
					}

					return nil
				},
				Type: "",
			},
156

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
			"single": &cmds.Command{
				Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
					t, ok := getTestingT(env)
					if !ok {
						return errors.New("error getting *testing.T")
					}

					wait, ok := getWaitChan(env)
					if !ok {
						return errors.New("error getting wait chan")
					}

					err := cmds.EmitOnce(re, "some value")
					if err != nil {
						t.Error("unexpected emit error:", err)
					}

					err = re.Emit("this should not be emitted")
					if err != cmds.ErrClosedEmitter {
						t.Errorf("expected emit error %q, got: %v", cmds.ErrClosedEmitter, err)
					}

					err = re.Close()
					if err != cmds.ErrClosingClosedEmitter {
						t.Error("expected double close error, got:", err)
					}

					close(wait)

					return nil
				},
				Type: "",
			},
190 191 192 193 194 195 196 197

			"reader": &cmds.Command{
				Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
					buf := bytes.NewBufferString("the reader call returns a reader.")
					return re.Emit(buf)
				},
			},

keks's avatar
keks committed
198
			"echo": &cmds.Command{
Steven Allen's avatar
Steven Allen committed
199 200
				Arguments: []cmds.Argument{
					cmds.FileArg("file", true, false, "a file"),
keks's avatar
keks committed
201 202 203 204 205 206 207 208
				},
				Type: "",
				Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
					err := re.Emit("i received:")
					if err != nil {
						return err
					}

Łukasz Magiera's avatar
Łukasz Magiera committed
209 210 211
					it := req.Files.Entries()
					if !it.Next() {
						return it.Err()
keks's avatar
keks committed
212 213
					}

Łukasz Magiera's avatar
Łukasz Magiera committed
214
					data, err := ioutil.ReadAll(files.FileFromEntry(it))
keks's avatar
keks committed
215 216 217 218 219 220 221 222
					if err != nil {
						return err
					}

					return re.Emit(string(data))
				},
			},

223
			"version": &cmds.Command{
Steven Allen's avatar
Steven Allen committed
224
				Helptext: cmds.HelpText{
225 226 227 228
					Tagline:          "Show ipfs version information.",
					ShortDescription: "Returns the current version of ipfs and exits.",
				},
				Type: VersionOutput{},
Steven Allen's avatar
Steven Allen committed
229 230 231 232 233
				Options: []cmds.Option{
					cmds.BoolOption("number", "n", "Only show the version number."),
					cmds.BoolOption("commit", "Show the commit hash."),
					cmds.BoolOption("repo", "Show repo version."),
					cmds.BoolOption("all", "Show all version information"),
234
				},
235
				Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) error {
236 237
					version, ok := getVersion(env)
					if !ok {
Steven Allen's avatar
Steven Allen committed
238
						return cmds.Errorf(cmds.ErrNormal, "couldn't get version")
239 240 241 242
					}

					repoVersion, ok := getRepoVersion(env)
					if !ok {
Steven Allen's avatar
Steven Allen committed
243
						return cmds.Errorf(cmds.ErrNormal, "couldn't get repo version")
244 245 246 247
					}

					commit, ok := getCommit(env)
					if !ok {
Steven Allen's avatar
Steven Allen committed
248
						return cmds.Errorf(cmds.ErrNormal, "couldn't get commit info")
249 250 251 252 253 254 255 256 257
					}

					re.Emit(&VersionOutput{
						Version: version,
						Commit:  commit,
						Repo:    repoVersion,
						System:  runtime.GOARCH + "/" + runtime.GOOS, //TODO: Precise version here
						Golang:  runtime.Version(),
					})
258
					return nil
259 260
				},
				Encoders: cmds.EncoderMap{
261
					cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, v *VersionOutput) error {
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289

						if repo, ok := req.Options["repo"].(bool); ok && repo {
							_, err := fmt.Fprintf(w, "%v\n", v.Repo)
							return err
						}

						var commitTxt string
						if commit, ok := req.Options["commit"].(bool); ok && commit {
							commitTxt = "-" + v.Commit
						}

						if number, ok := req.Options["number"].(bool); ok && number {
							_, err := fmt.Fprintf(w, "%v%v\n", v.Version, commitTxt)
							return err
						}

						if all, ok := req.Options["all"].(bool); ok && all {
							_, err := fmt.Fprintf(w, "go-ipfs version: %s-%s\n"+
								"Repo version: %s\nSystem version: %s\nGolang version: %s\n",
								v.Version, v.Commit, v.Repo, v.System, v.Golang)

							return err
						}

						_, err := fmt.Fprintf(w, "ipfs version %s%s\n", v.Version, commitTxt)
						return err
					}),
				},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
290
			},
291
		},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
292
	}
293
)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
294

295
func getTestServer(t *testing.T, origins []string, handledMethods []string) (cmds.Environment, *httptest.Server) {
296 297
	if len(origins) == 0 {
		origins = defaultOrigins
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
298 299
	}

300 301 302 303
	env := testEnv{
		version:     "0.1.2",
		commit:      "c0mm17", // yes, I know there's no 'm' in hex.
		repoVersion: "4",
keks's avatar
keks committed
304
		t:           t,
305
		wait:        make(chan struct{}),
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
306 307
	}

308 309 310 311 312 313 314 315 316
	srvCfg := originCfg(origins)

	if len(handledMethods) == 0 {
		srvCfg.HandledMethods = []string{"GET", "POST"}
	} else {
		srvCfg.HandledMethods = handledMethods
	}

	return env, httptest.NewServer(NewHandler(env, cmdRoot, srvCfg))
317 318
}

319 320 321
func errEq(err1, err2 error) bool {
	if err1 == nil && err2 == nil {
		return true
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
322 323
	}

324 325
	if err1 == nil || err2 == nil {
		return false
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
326 327
	}

328
	return err1.Error() == err2.Error()
329
}