1// Copyright 2015 The etcd Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package rafthttp 16 17import ( 18 "bytes" 19 "encoding/binary" 20 "io" 21 "net/http" 22 "reflect" 23 "testing" 24 25 "go.etcd.io/etcd/raft/raftpb" 26 "go.etcd.io/etcd/version" 27 28 "github.com/coreos/go-semver/semver" 29) 30 31func TestEntry(t *testing.T) { 32 tests := []raftpb.Entry{ 33 {}, 34 {Term: 1, Index: 1}, 35 {Term: 1, Index: 1, Data: []byte("some data")}, 36 } 37 for i, tt := range tests { 38 b := &bytes.Buffer{} 39 if err := writeEntryTo(b, &tt); err != nil { 40 t.Errorf("#%d: unexpected write ents error: %v", i, err) 41 continue 42 } 43 var ent raftpb.Entry 44 if err := readEntryFrom(b, &ent); err != nil { 45 t.Errorf("#%d: unexpected read ents error: %v", i, err) 46 continue 47 } 48 if !reflect.DeepEqual(ent, tt) { 49 t.Errorf("#%d: ent = %+v, want %+v", i, ent, tt) 50 } 51 } 52} 53 54func TestCompareMajorMinorVersion(t *testing.T) { 55 tests := []struct { 56 va, vb *semver.Version 57 w int 58 }{ 59 // equal to 60 { 61 semver.Must(semver.NewVersion("2.1.0")), 62 semver.Must(semver.NewVersion("2.1.0")), 63 0, 64 }, 65 // smaller than 66 { 67 semver.Must(semver.NewVersion("2.0.0")), 68 semver.Must(semver.NewVersion("2.1.0")), 69 -1, 70 }, 71 // bigger than 72 { 73 semver.Must(semver.NewVersion("2.2.0")), 74 semver.Must(semver.NewVersion("2.1.0")), 75 1, 76 }, 77 // ignore patch 78 { 79 semver.Must(semver.NewVersion("2.1.1")), 80 semver.Must(semver.NewVersion("2.1.0")), 81 0, 82 }, 83 // ignore prerelease 84 { 85 semver.Must(semver.NewVersion("2.1.0-alpha.0")), 86 semver.Must(semver.NewVersion("2.1.0")), 87 0, 88 }, 89 } 90 for i, tt := range tests { 91 if g := compareMajorMinorVersion(tt.va, tt.vb); g != tt.w { 92 t.Errorf("#%d: compare = %d, want %d", i, g, tt.w) 93 } 94 } 95} 96 97func TestServerVersion(t *testing.T) { 98 tests := []struct { 99 h http.Header 100 wv *semver.Version 101 }{ 102 // backward compatibility with etcd 2.0 103 { 104 http.Header{}, 105 semver.Must(semver.NewVersion("2.0.0")), 106 }, 107 { 108 http.Header{"X-Server-Version": []string{"2.1.0"}}, 109 semver.Must(semver.NewVersion("2.1.0")), 110 }, 111 { 112 http.Header{"X-Server-Version": []string{"2.1.0-alpha.0+git"}}, 113 semver.Must(semver.NewVersion("2.1.0-alpha.0+git")), 114 }, 115 } 116 for i, tt := range tests { 117 v := serverVersion(tt.h) 118 if v.String() != tt.wv.String() { 119 t.Errorf("#%d: version = %s, want %s", i, v, tt.wv) 120 } 121 } 122} 123 124func TestMinClusterVersion(t *testing.T) { 125 tests := []struct { 126 h http.Header 127 wv *semver.Version 128 }{ 129 // backward compatibility with etcd 2.0 130 { 131 http.Header{}, 132 semver.Must(semver.NewVersion("2.0.0")), 133 }, 134 { 135 http.Header{"X-Min-Cluster-Version": []string{"2.1.0"}}, 136 semver.Must(semver.NewVersion("2.1.0")), 137 }, 138 { 139 http.Header{"X-Min-Cluster-Version": []string{"2.1.0-alpha.0+git"}}, 140 semver.Must(semver.NewVersion("2.1.0-alpha.0+git")), 141 }, 142 } 143 for i, tt := range tests { 144 v := minClusterVersion(tt.h) 145 if v.String() != tt.wv.String() { 146 t.Errorf("#%d: version = %s, want %s", i, v, tt.wv) 147 } 148 } 149} 150 151func TestCheckVersionCompatibility(t *testing.T) { 152 ls := semver.Must(semver.NewVersion(version.Version)) 153 lmc := semver.Must(semver.NewVersion(version.MinClusterVersion)) 154 tests := []struct { 155 server *semver.Version 156 minCluster *semver.Version 157 wok bool 158 }{ 159 // the same version as local 160 { 161 ls, 162 lmc, 163 true, 164 }, 165 // one version lower 166 { 167 lmc, 168 &semver.Version{}, 169 true, 170 }, 171 // one version higher 172 { 173 &semver.Version{Major: ls.Major + 1}, 174 ls, 175 true, 176 }, 177 // too low version 178 { 179 &semver.Version{Major: lmc.Major - 1}, 180 &semver.Version{}, 181 false, 182 }, 183 // too high version 184 { 185 &semver.Version{Major: ls.Major + 1, Minor: 1}, 186 &semver.Version{Major: ls.Major + 1}, 187 false, 188 }, 189 } 190 for i, tt := range tests { 191 _, _, err := checkVersionCompatibility("", tt.server, tt.minCluster) 192 if ok := err == nil; ok != tt.wok { 193 t.Errorf("#%d: ok = %v, want %v", i, ok, tt.wok) 194 } 195 } 196} 197 198func writeEntryTo(w io.Writer, ent *raftpb.Entry) error { 199 size := ent.Size() 200 if err := binary.Write(w, binary.BigEndian, uint64(size)); err != nil { 201 return err 202 } 203 b, err := ent.Marshal() 204 if err != nil { 205 return err 206 } 207 _, err = w.Write(b) 208 return err 209} 210 211func readEntryFrom(r io.Reader, ent *raftpb.Entry) error { 212 var l uint64 213 if err := binary.Read(r, binary.BigEndian, &l); err != nil { 214 return err 215 } 216 buf := make([]byte, int(l)) 217 if _, err := io.ReadFull(r, buf); err != nil { 218 return err 219 } 220 return ent.Unmarshal(buf) 221} 222