// 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 rafthttp import ( "bytes" "encoding/binary" "io" "net/http" "reflect" "testing" "github.com/coreos/etcd/raft/raftpb" "github.com/coreos/etcd/version" "github.com/coreos/go-semver/semver" ) func TestEntry(t *testing.T) { tests := []raftpb.Entry{ {}, {Term: 1, Index: 1}, {Term: 1, Index: 1, Data: []byte("some data")}, } for i, tt := range tests { b := &bytes.Buffer{} if err := writeEntryTo(b, &tt); err != nil { t.Errorf("#%d: unexpected write ents error: %v", i, err) continue } var ent raftpb.Entry if err := readEntryFrom(b, &ent); err != nil { t.Errorf("#%d: unexpected read ents error: %v", i, err) continue } if !reflect.DeepEqual(ent, tt) { t.Errorf("#%d: ent = %+v, want %+v", i, ent, tt) } } } func TestCompareMajorMinorVersion(t *testing.T) { tests := []struct { va, vb *semver.Version w int }{ // equal to { semver.Must(semver.NewVersion("2.1.0")), semver.Must(semver.NewVersion("2.1.0")), 0, }, // smaller than { semver.Must(semver.NewVersion("2.0.0")), semver.Must(semver.NewVersion("2.1.0")), -1, }, // bigger than { semver.Must(semver.NewVersion("2.2.0")), semver.Must(semver.NewVersion("2.1.0")), 1, }, // ignore patch { semver.Must(semver.NewVersion("2.1.1")), semver.Must(semver.NewVersion("2.1.0")), 0, }, // ignore prerelease { semver.Must(semver.NewVersion("2.1.0-alpha.0")), semver.Must(semver.NewVersion("2.1.0")), 0, }, } for i, tt := range tests { if g := compareMajorMinorVersion(tt.va, tt.vb); g != tt.w { t.Errorf("#%d: compare = %d, want %d", i, g, tt.w) } } } func TestServerVersion(t *testing.T) { tests := []struct { h http.Header wv *semver.Version }{ // backward compatibility with etcd 2.0 { http.Header{}, semver.Must(semver.NewVersion("2.0.0")), }, { http.Header{"X-Server-Version": []string{"2.1.0"}}, semver.Must(semver.NewVersion("2.1.0")), }, { http.Header{"X-Server-Version": []string{"2.1.0-alpha.0+git"}}, semver.Must(semver.NewVersion("2.1.0-alpha.0+git")), }, } for i, tt := range tests { v := serverVersion(tt.h) if v.String() != tt.wv.String() { t.Errorf("#%d: version = %s, want %s", i, v, tt.wv) } } } func TestMinClusterVersion(t *testing.T) { tests := []struct { h http.Header wv *semver.Version }{ // backward compatibility with etcd 2.0 { http.Header{}, semver.Must(semver.NewVersion("2.0.0")), }, { http.Header{"X-Min-Cluster-Version": []string{"2.1.0"}}, semver.Must(semver.NewVersion("2.1.0")), }, { http.Header{"X-Min-Cluster-Version": []string{"2.1.0-alpha.0+git"}}, semver.Must(semver.NewVersion("2.1.0-alpha.0+git")), }, } for i, tt := range tests { v := minClusterVersion(tt.h) if v.String() != tt.wv.String() { t.Errorf("#%d: version = %s, want %s", i, v, tt.wv) } } } func TestCheckVersionCompatibility(t *testing.T) { ls := semver.Must(semver.NewVersion(version.Version)) lmc := semver.Must(semver.NewVersion(version.MinClusterVersion)) tests := []struct { server *semver.Version minCluster *semver.Version wok bool }{ // the same version as local { ls, lmc, true, }, // one version lower { lmc, &semver.Version{}, true, }, // one version higher { &semver.Version{Major: ls.Major + 1}, ls, true, }, // too low version { &semver.Version{Major: lmc.Major - 1}, &semver.Version{}, false, }, // too high version { &semver.Version{Major: ls.Major + 1, Minor: 1}, &semver.Version{Major: ls.Major + 1}, false, }, } for i, tt := range tests { err := checkVersionCompability("", tt.server, tt.minCluster) if ok := err == nil; ok != tt.wok { t.Errorf("#%d: ok = %v, want %v", i, ok, tt.wok) } } } func writeEntryTo(w io.Writer, ent *raftpb.Entry) error { size := ent.Size() if err := binary.Write(w, binary.BigEndian, uint64(size)); err != nil { return err } b, err := ent.Marshal() if err != nil { return err } _, err = w.Write(b) return err } func readEntryFrom(r io.Reader, ent *raftpb.Entry) error { var l uint64 if err := binary.Read(r, binary.BigEndian, &l); err != nil { return err } buf := make([]byte, int(l)) if _, err := io.ReadFull(r, buf); err != nil { return err } return ent.Unmarshal(buf) }