1/* 2** Zabbix 3** Copyright (C) 2001-2021 Zabbix SIA 4** 5** This program is free software; you can redistribute it and/or modify 6** it under the terms of the GNU General Public License as published by 7** the Free Software Foundation; either version 2 of the License, or 8** (at your option) any later version. 9** 10** This program is distributed in the hope that it will be useful, 11** but WITHOUT ANY WARRANTY; without even the implied warranty of 12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13** GNU General Public License for more details. 14** 15** You should have received a copy of the GNU General Public License 16** along with this program; if not, write to the Free Software 17** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18**/ 19 20package memcached 21 22import ( 23 "errors" 24 "io" 25 "reflect" 26 "testing" 27 "time" 28 29 "zabbix.com/pkg/zbxerr" 30 31 "zabbix.com/pkg/plugin" 32 "zabbix.com/plugins/memcached/mockserver" 33) 34 35func handleStat(req *mockserver.MCRequest, w io.Writer) (ret *mockserver.MCResponse) { 36 ret = &mockserver.MCResponse{} 37 ret.Status = mockserver.Success 38 39 switch string(req.Key) { 40 case statsTypeGeneral: 41 ret.Key = []byte("version") 42 ret.Body = []byte("1.4.15") 43 _, _ = ret.Transmit(w) 44 case statsTypeItems: 45 ret.Key = []byte("items:1:number") 46 ret.Body = []byte("1") 47 _, _ = ret.Transmit(w) 48 case statsTypeSlabs: 49 ret.Key = []byte("1:chunk_size") 50 ret.Body = []byte("96") 51 _, _ = ret.Transmit(w) 52 ret.Key = []byte("1:total_pages") 53 ret.Body = []byte("1") 54 _, _ = ret.Transmit(w) 55 case statsTypeSizes: 56 ret.Key = []byte("96") 57 ret.Body = []byte("1") 58 _, _ = ret.Transmit(w) 59 case statsTypeSettings: 60 ret.Key = []byte("maxconns") 61 ret.Body = []byte("1024") 62 _, _ = ret.Transmit(w) 63 default: 64 ret.Status = mockserver.UnknownCommand 65 return 66 } 67 68 ret.Key = nil 69 ret.Body = nil 70 71 return ret 72} 73 74func handleNoOp(_ *mockserver.MCRequest, _ io.Writer) *mockserver.MCResponse { 75 return &mockserver.MCResponse{} 76} 77 78func TestPlugin_Start(t *testing.T) { 79 p := Plugin{} 80 81 t.Run("Connection manager must be initialized", func(t *testing.T) { 82 p.Start() 83 if p.connMgr == nil { 84 t.Error("Connection manager is not initialized") 85 } 86 }) 87} 88 89func TestPlugin_Export(t *testing.T) { 90 ms, err := mockserver.NewMockServer() 91 if err != nil { 92 panic(err) 93 } 94 95 ms.RegisterHandler(mockserver.Stat, handleStat) 96 ms.RegisterHandler(mockserver.Noop, handleNoOp) 97 98 go ms.ListenAndServe() 99 100 time.Sleep(1 * time.Second) 101 102 type args struct { 103 key string 104 params []string 105 ctx plugin.ContextProvider 106 } 107 108 p := Plugin{} 109 p.Init(pluginName) 110 p.Configure(&plugin.GlobalOptions{Timeout: 30}, nil) 111 112 p.Start() 113 defer p.Stop() 114 115 tests := []struct { 116 name string 117 p *Plugin 118 args args 119 wantResult interface{} 120 wantErr error 121 }{ 122 { 123 name: "Too many parameters", 124 p: &p, 125 args: args{keyPing, []string{"tcp://127.0.0.1", "", "", "excess_param"}, nil}, 126 wantResult: nil, 127 wantErr: zbxerr.ErrorTooManyParameters, 128 }, 129 { 130 name: "Must successfully get general stats", 131 p: &p, 132 args: args{keyStats, []string{"tcp://" + ms.GetAddr()}, nil}, 133 wantResult: `{"version":"1.4.15"}`, 134 wantErr: nil, 135 }, 136 { 137 name: "Type parameter must be passed correctly", 138 p: &p, 139 args: args{keyStats, []string{"tcp://" + ms.GetAddr(), "", "", statsTypeItems}, nil}, 140 wantResult: `{"items:1:number":"1"}`, 141 wantErr: nil, 142 }, 143 { 144 name: "Must fail if server is not working", 145 p: &p, 146 args: args{keyStats, []string{"tcp://127.0.0.1:1"}, nil}, 147 wantResult: nil, 148 wantErr: errors.New("Cannot fetch data: dial tcp 127.0.0.1:1: connect: connection refused."), 149 }, 150 { 151 name: "Must not fail if server is not working for " + keyPing, 152 p: &p, 153 args: args{keyPing, []string{"tcp://127.0.0.1:1"}, nil}, 154 wantResult: pingFailed, 155 wantErr: nil, 156 }, 157 { 158 name: "pingHandler must create connection to server and run NoOp command", 159 p: &p, 160 args: args{keyPing, []string{"tcp://" + ms.GetAddr()}, nil}, 161 wantResult: pingOk, 162 wantErr: nil, 163 }, 164 } 165 for _, tt := range tests { 166 t.Run(tt.name, func(t *testing.T) { 167 gotResult, err := tt.p.Export(tt.args.key, tt.args.params, tt.args.ctx) 168 if err != nil && (tt.wantErr == nil || err.Error() != tt.wantErr.Error()) { 169 t.Errorf("Plugin.Export() error = %v, wantErr %v", err, tt.wantErr) 170 return 171 } 172 if !reflect.DeepEqual(gotResult, tt.wantResult) { 173 t.Errorf("Plugin.Export() = %v, want %v", gotResult, tt.wantResult) 174 } 175 }) 176 } 177} 178 179func TestPlugin_Stop(t *testing.T) { 180 p := Plugin{} 181 p.Start() 182 t.Run("Connection manager must be deinitialized", func(t *testing.T) { 183 p.Stop() 184 if p.connMgr != nil { 185 t.Error("Connection manager is not deinitialized") 186 } 187 }) 188} 189