package remote import ( "bytes" "fmt" "io" "net/http" "net/http/httptest" "net/url" "reflect" "testing" "github.com/hashicorp/go-cleanhttp" ) func TestHTTPClient_impl(t *testing.T) { var _ Client = new(HTTPClient) var _ ClientLocker = new(HTTPClient) } func TestHTTPClient(t *testing.T) { handler := new(testHTTPHandler) ts := httptest.NewServer(http.HandlerFunc(handler.Handle)) defer ts.Close() url, err := url.Parse(ts.URL) if err != nil { t.Fatalf("err: %s", err) } // Test basic get/update client := &HTTPClient{URL: url, Client: cleanhttp.DefaultClient()} testClient(t, client) // test just a single PUT p := &HTTPClient{ URL: url, UpdateMethod: "PUT", Client: cleanhttp.DefaultClient(), } testClient(t, p) // Test locking and alternative UpdateMethod a := &HTTPClient{ URL: url, UpdateMethod: "PUT", LockURL: url, LockMethod: "LOCK", UnlockURL: url, UnlockMethod: "UNLOCK", Client: cleanhttp.DefaultClient(), } b := &HTTPClient{ URL: url, UpdateMethod: "PUT", LockURL: url, LockMethod: "LOCK", UnlockURL: url, UnlockMethod: "UNLOCK", Client: cleanhttp.DefaultClient(), } TestRemoteLocks(t, a, b) // test a WebDAV-ish backend davhandler := new(testHTTPHandler) ts = httptest.NewServer(http.HandlerFunc(davhandler.HandleWebDAV)) defer ts.Close() url, err = url.Parse(ts.URL) c := &HTTPClient{ URL: url, UpdateMethod: "PUT", Client: cleanhttp.DefaultClient(), } testClient(t, c) // first time through: 201 testClient(t, c) // second time, with identical data: 204 } func assertError(t *testing.T, err error, expected string) { if err == nil { t.Fatalf("Expected empty config to err") } else if err.Error() != expected { t.Fatalf("Expected err.Error() to be \"%s\", got \"%s\"", expected, err.Error()) } } func TestHTTPClientFactory(t *testing.T) { // missing address _, err := httpFactory(map[string]string{}) assertError(t, err, "missing 'address' configuration") // defaults conf := map[string]string{ "address": "http://127.0.0.1:8888/foo", } c, err := httpFactory(conf) client, _ := c.(*HTTPClient) if client == nil || err != nil { t.Fatal("Unexpected failure, address") } if client.URL.String() != conf["address"] { t.Fatalf("Expected address \"%s\", got \"%s\"", conf["address"], client.URL.String()) } if client.UpdateMethod != "POST" { t.Fatalf("Expected update_method \"%s\", got \"%s\"", "POST", client.UpdateMethod) } if client.LockURL != nil || client.LockMethod != "LOCK" { t.Fatal("Unexpected lock_address or lock_method") } if client.UnlockURL != nil || client.UnlockMethod != "UNLOCK" { t.Fatal("Unexpected unlock_address or unlock_method") } if client.Username != "" || client.Password != "" { t.Fatal("Unexpected username or password") } // custom conf = map[string]string{ "address": "http://127.0.0.1:8888/foo", "update_method": "BLAH", "lock_address": "http://127.0.0.1:8888/bar", "lock_method": "BLIP", "unlock_address": "http://127.0.0.1:8888/baz", "unlock_method": "BLOOP", "username": "user", "password": "pass", } c, err = httpFactory(conf) client, _ = c.(*HTTPClient) if client == nil || err != nil { t.Fatal("Unexpected failure, update_method") } if client.UpdateMethod != "BLAH" { t.Fatalf("Expected update_method \"%s\", got \"%s\"", "BLAH", client.UpdateMethod) } if client.LockURL.String() != conf["lock_address"] || client.LockMethod != "BLIP" { t.Fatalf("Unexpected lock_address \"%s\" vs \"%s\" or lock_method \"%s\" vs \"%s\"", client.LockURL.String(), conf["lock_address"], client.LockMethod, conf["lock_method"]) } if client.UnlockURL.String() != conf["unlock_address"] || client.UnlockMethod != "BLOOP" { t.Fatalf("Unexpected unlock_address \"%s\" vs \"%s\" or unlock_method \"%s\" vs \"%s\"", client.UnlockURL.String(), conf["unlock_address"], client.UnlockMethod, conf["unlock_method"]) } if client.Username != "user" || client.Password != "pass" { t.Fatalf("Unexpected username \"%s\" vs \"%s\" or password \"%s\" vs \"%s\"", client.Username, conf["username"], client.Password, conf["password"]) } } type testHTTPHandler struct { Data []byte Locked bool } func (h *testHTTPHandler) Handle(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": w.Write(h.Data) case "PUT": buf := new(bytes.Buffer) if _, err := io.Copy(buf, r.Body); err != nil { w.WriteHeader(500) } w.WriteHeader(201) h.Data = buf.Bytes() case "POST": buf := new(bytes.Buffer) if _, err := io.Copy(buf, r.Body); err != nil { w.WriteHeader(500) } h.Data = buf.Bytes() case "LOCK": if h.Locked { w.WriteHeader(423) } else { h.Locked = true } case "UNLOCK": h.Locked = false case "DELETE": h.Data = nil w.WriteHeader(200) default: w.WriteHeader(500) w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method))) } } // mod_dav-ish behavior func (h *testHTTPHandler) HandleWebDAV(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": w.Write(h.Data) case "PUT": buf := new(bytes.Buffer) if _, err := io.Copy(buf, r.Body); err != nil { w.WriteHeader(500) } if reflect.DeepEqual(h.Data, buf.Bytes()) { h.Data = buf.Bytes() w.WriteHeader(204) } else { h.Data = buf.Bytes() w.WriteHeader(201) } case "DELETE": h.Data = nil w.WriteHeader(200) default: w.WriteHeader(500) w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method))) } }