mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
193 lines
4.6 KiB
Go
193 lines
4.6 KiB
Go
// Copyright 2015 The etcd Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package v2http
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sort"
|
|
"testing"
|
|
|
|
"go.etcd.io/etcd/etcdserver"
|
|
"go.etcd.io/etcd/etcdserver/api/membership"
|
|
"go.etcd.io/etcd/etcdserver/api/v2error"
|
|
"go.etcd.io/etcd/etcdserver/etcdserverpb"
|
|
"go.etcd.io/etcd/pkg/types"
|
|
"go.etcd.io/etcd/raft/raftpb"
|
|
|
|
"github.com/coreos/go-semver/semver"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type fakeCluster struct {
|
|
id uint64
|
|
clientURLs []string
|
|
members map[uint64]*membership.Member
|
|
}
|
|
|
|
func (c *fakeCluster) ID() types.ID { return types.ID(c.id) }
|
|
func (c *fakeCluster) ClientURLs() []string { return c.clientURLs }
|
|
func (c *fakeCluster) Members() []*membership.Member {
|
|
var ms membership.MembersByID
|
|
for _, m := range c.members {
|
|
ms = append(ms, m)
|
|
}
|
|
sort.Sort(ms)
|
|
return []*membership.Member(ms)
|
|
}
|
|
func (c *fakeCluster) Member(id types.ID) *membership.Member { return c.members[uint64(id)] }
|
|
func (c *fakeCluster) Version() *semver.Version { return nil }
|
|
|
|
// errServer implements the etcd.Server interface for testing.
|
|
// It returns the given error from any Do/Process/AddMember/RemoveMember calls.
|
|
type errServer struct {
|
|
err error
|
|
fakeServer
|
|
}
|
|
|
|
func (fs *errServer) Do(ctx context.Context, r etcdserverpb.Request) (etcdserver.Response, error) {
|
|
return etcdserver.Response{}, fs.err
|
|
}
|
|
func (fs *errServer) Process(ctx context.Context, m raftpb.Message) error {
|
|
return fs.err
|
|
}
|
|
func (fs *errServer) AddMember(ctx context.Context, m membership.Member) ([]*membership.Member, error) {
|
|
return nil, fs.err
|
|
}
|
|
func (fs *errServer) RemoveMember(ctx context.Context, id uint64) ([]*membership.Member, error) {
|
|
return nil, fs.err
|
|
}
|
|
func (fs *errServer) UpdateMember(ctx context.Context, m membership.Member) ([]*membership.Member, error) {
|
|
return nil, fs.err
|
|
}
|
|
func (fs *errServer) PromoteMember(ctx context.Context, id uint64) ([]*membership.Member, error) {
|
|
return nil, fs.err
|
|
}
|
|
|
|
func TestWriteError(t *testing.T) {
|
|
// nil error should not panic
|
|
rec := httptest.NewRecorder()
|
|
r := new(http.Request)
|
|
writeError(zap.NewExample(), rec, r, nil)
|
|
h := rec.Header()
|
|
if len(h) > 0 {
|
|
t.Fatalf("unexpected non-empty headers: %#v", h)
|
|
}
|
|
b := rec.Body.String()
|
|
if len(b) > 0 {
|
|
t.Fatalf("unexpected non-empty body: %q", b)
|
|
}
|
|
|
|
tests := []struct {
|
|
err error
|
|
wcode int
|
|
wi string
|
|
}{
|
|
{
|
|
v2error.NewError(v2error.EcodeKeyNotFound, "/foo/bar", 123),
|
|
http.StatusNotFound,
|
|
"123",
|
|
},
|
|
{
|
|
v2error.NewError(v2error.EcodeTestFailed, "/foo/bar", 456),
|
|
http.StatusPreconditionFailed,
|
|
"456",
|
|
},
|
|
{
|
|
err: errors.New("something went wrong"),
|
|
wcode: http.StatusInternalServerError,
|
|
},
|
|
}
|
|
|
|
for i, tt := range tests {
|
|
rw := httptest.NewRecorder()
|
|
writeError(zap.NewExample(), rw, r, tt.err)
|
|
if code := rw.Code; code != tt.wcode {
|
|
t.Errorf("#%d: code=%d, want %d", i, code, tt.wcode)
|
|
}
|
|
if idx := rw.Header().Get("X-Etcd-Index"); idx != tt.wi {
|
|
t.Errorf("#%d: X-Etcd-Index=%q, want %q", i, idx, tt.wi)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAllowMethod(t *testing.T) {
|
|
tests := []struct {
|
|
m string
|
|
ms []string
|
|
w bool
|
|
wh string
|
|
}{
|
|
// Accepted methods
|
|
{
|
|
m: "GET",
|
|
ms: []string{"GET", "POST", "PUT"},
|
|
w: true,
|
|
},
|
|
{
|
|
m: "POST",
|
|
ms: []string{"POST"},
|
|
w: true,
|
|
},
|
|
// Made-up methods no good
|
|
{
|
|
m: "FAKE",
|
|
ms: []string{"GET", "POST", "PUT"},
|
|
w: false,
|
|
wh: "GET,POST,PUT",
|
|
},
|
|
// Empty methods no good
|
|
{
|
|
m: "",
|
|
ms: []string{"GET", "POST"},
|
|
w: false,
|
|
wh: "GET,POST",
|
|
},
|
|
// Empty accepted methods no good
|
|
{
|
|
m: "GET",
|
|
ms: []string{""},
|
|
w: false,
|
|
wh: "",
|
|
},
|
|
// No methods accepted
|
|
{
|
|
m: "GET",
|
|
ms: []string{},
|
|
w: false,
|
|
wh: "",
|
|
},
|
|
}
|
|
|
|
for i, tt := range tests {
|
|
rw := httptest.NewRecorder()
|
|
g := allowMethod(rw, tt.m, tt.ms...)
|
|
if g != tt.w {
|
|
t.Errorf("#%d: got allowMethod()=%t, want %t", i, g, tt.w)
|
|
}
|
|
if !tt.w {
|
|
if rw.Code != http.StatusMethodNotAllowed {
|
|
t.Errorf("#%d: code=%d, want %d", i, rw.Code, http.StatusMethodNotAllowed)
|
|
}
|
|
gh := rw.Header().Get("Allow")
|
|
if gh != tt.wh {
|
|
t.Errorf("#%d: Allow header=%q, want %q", i, gh, tt.wh)
|
|
}
|
|
}
|
|
}
|
|
}
|