code.vegaprotocol.io/vega@v0.79.0/wallet/api/admin_import_network_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package api_test 17 18 import ( 19 "context" 20 "fmt" 21 "os" 22 "path/filepath" 23 "testing" 24 25 "code.vegaprotocol.io/vega/libs/jsonrpc" 26 vgrand "code.vegaprotocol.io/vega/libs/rand" 27 "code.vegaprotocol.io/vega/wallet/api" 28 "code.vegaprotocol.io/vega/wallet/api/mocks" 29 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 ) 34 35 func TestAdminImportNetwork(t *testing.T) { 36 t.Run("Documentation matches the code", testAdminImportNetworkSchemaCorrect) 37 t.Run("Importing a network with invalid params fails", testImportingNetworkWithInvalidParamsFails) 38 t.Run("Importing a network that already exists fails", testImportingNetworkThatAlreadyExistsFails) 39 t.Run("Getting internal error during verification does not import the network", testGettingInternalErrorDuringVerificationDoesNotImportNetwork) 40 t.Run("Importing a network from a file that doesn't exist fails", testImportingANetworkFromAFileThatDoesntExistFails) 41 t.Run("Importing a network from a valid file saves", testImportingValidFileSaves) 42 t.Run("Importing a network with no name fails", testImportingWithNoNameFails) 43 t.Run("Importing a network from a valid file with name in config works", testImportingWithNameInConfig) 44 t.Run("Importing a network with a github url suggests better alternative", testImportNetworkWithURL) 45 t.Run("Importing a network with a content that is not TOML fails with a user friendly message", testImportNetworkWithNotTOMLContentFailsWithFriendlyMessage) 46 } 47 48 func testAdminImportNetworkSchemaCorrect(t *testing.T) { 49 assertEqualSchema(t, "admin.import_network", api.AdminImportNetworkParams{}, api.AdminImportNetworkResult{}) 50 } 51 52 func testImportingNetworkWithInvalidParamsFails(t *testing.T) { 53 tcs := []struct { 54 name string 55 params interface{} 56 expectedError error 57 }{ 58 { 59 name: "with nil params", 60 params: nil, 61 expectedError: api.ErrParamsRequired, 62 }, { 63 name: "with wrong type of params", 64 params: "test", 65 expectedError: api.ErrParamsDoNotMatch, 66 }, { 67 name: "with empty sources", 68 params: api.AdminImportNetworkParams{ 69 Name: "fairground", 70 URL: "", 71 }, 72 expectedError: api.ErrNetworkSourceIsRequired, 73 }, 74 } 75 76 for _, tc := range tcs { 77 t.Run(tc.name, func(tt *testing.T) { 78 // given 79 ctx := context.Background() 80 81 // setup 82 handler := newImportNetworkHandler(tt) 83 84 // when 85 result, errorDetails := handler.handle(t, ctx, tc.params) 86 87 // then 88 require.Empty(tt, result) 89 assertInvalidParams(tt, errorDetails, tc.expectedError) 90 }) 91 } 92 } 93 94 func testImportingNetworkThatAlreadyExistsFails(t *testing.T) { 95 // given 96 ctx := context.Background() 97 name := vgrand.RandomStr(5) 98 d := t.TempDir() 99 filePath := filepath.Join(d + "tmp.toml") 100 err := os.WriteFile(filePath, []byte("Name = \"local\""), 0o644) 101 require.NoError(t, err) 102 103 // setup 104 handler := newImportNetworkHandler(t) 105 106 // -- expected calls 107 handler.networkStore.EXPECT().NetworkExists(gomock.Any()).Times(1).Return(true, nil) 108 109 // when 110 result, errorDetails := handler.handle(t, ctx, api.AdminImportNetworkParams{ 111 Name: name, 112 URL: api.FileSchemePrefix + filePath, 113 }) 114 115 // then 116 require.NotNil(t, errorDetails) 117 assert.Empty(t, result) 118 assertInvalidParams(t, errorDetails, api.ErrNetworkAlreadyExists) 119 } 120 121 func testGettingInternalErrorDuringVerificationDoesNotImportNetwork(t *testing.T) { 122 // given 123 ctx := context.Background() 124 name := vgrand.RandomStr(5) 125 d := t.TempDir() 126 filePath := filepath.Join(d + "tmp.toml") 127 err := os.WriteFile(filePath, []byte("Name = \"local\""), 0o644) 128 require.NoError(t, err) 129 130 // setup 131 handler := newImportNetworkHandler(t) 132 // -- expected calls 133 handler.networkStore.EXPECT().NetworkExists(gomock.Any()).Times(1).Return(false, assert.AnError) 134 135 // when 136 result, errorDetails := handler.handle(t, ctx, api.AdminImportNetworkParams{ 137 Name: name, 138 URL: api.FileSchemePrefix + filePath, 139 }) 140 141 // then 142 require.NotNil(t, errorDetails) 143 assert.Empty(t, result) 144 assertInternalError(t, errorDetails, fmt.Errorf("could not verify the network existence: %w", assert.AnError)) 145 } 146 147 func testImportingANetworkFromAFileThatDoesntExistFails(t *testing.T) { 148 // given 149 ctx := context.Background() 150 name := vgrand.RandomStr(5) 151 152 // setup 153 handler := newImportNetworkHandler(t) 154 155 // when 156 result, errorDetails := handler.handle(t, ctx, api.AdminImportNetworkParams{ 157 Name: name, 158 URL: api.FileSchemePrefix + "some-file-path", 159 }) 160 161 // then 162 require.NotNil(t, errorDetails) 163 assert.Empty(t, result) 164 assertInvalidParams(t, errorDetails, fmt.Errorf("the network source file does not exist: %w", api.ErrInvalidNetworkSource)) 165 } 166 167 func testImportingValidFileSaves(t *testing.T) { 168 // given 169 ctx := context.Background() 170 name := vgrand.RandomStr(5) 171 172 d := t.TempDir() 173 filePath := filepath.Join(d + "tmp.toml") 174 err := os.WriteFile(filePath, []byte("Name = \"local\""), 0o644) 175 require.NoError(t, err) 176 177 // setup 178 handler := newImportNetworkHandler(t) 179 // -- expected calls 180 handler.networkStore.EXPECT().NetworkExists(name).Times(1).Return(false, nil) 181 handler.networkStore.EXPECT().SaveNetwork(gomock.Any()).Times(1) 182 183 // when 184 result, errorDetails := handler.handle(t, ctx, api.AdminImportNetworkParams{ 185 Name: name, 186 URL: api.FileSchemePrefix + filePath, 187 }) 188 189 // then 190 require.Nil(t, errorDetails) 191 assert.Equal(t, result.Name, name) 192 } 193 194 func testImportingWithNoNameFails(t *testing.T) { 195 // given 196 ctx := context.Background() 197 198 d := t.TempDir() 199 filePath := filepath.Join(d + "tmp.toml") 200 err := os.WriteFile(filePath, []byte("Address = \"local\""), 0o644) 201 require.NoError(t, err) 202 203 // setup 204 handler := newImportNetworkHandler(t) 205 206 // when the config has no network name, and there is no network name specified in the params 207 _, errorDetails := handler.handle(t, ctx, api.AdminImportNetworkParams{ 208 URL: api.FileSchemePrefix + filePath, 209 }) 210 211 // then 212 assertInvalidParams(t, errorDetails, api.ErrNetworkNameIsRequired) 213 } 214 215 func testImportingWithNameInConfig(t *testing.T) { 216 // given 217 ctx := context.Background() 218 219 d := t.TempDir() 220 filePath := filepath.Join(d + "tmp.toml") 221 err := os.WriteFile(filePath, []byte("Name = \"local\""), 0o644) 222 require.NoError(t, err) 223 224 // setup 225 handler := newImportNetworkHandler(t) 226 // -- expected calls 227 handler.networkStore.EXPECT().NetworkExists("local").Times(1).Return(false, nil) 228 handler.networkStore.EXPECT().SaveNetwork(gomock.Any()).Times(1) 229 230 // when 231 result, errorDetails := handler.handle(t, ctx, api.AdminImportNetworkParams{ 232 URL: api.FileSchemePrefix + filePath, 233 }) 234 235 // then 236 require.Nil(t, errorDetails) 237 assert.Equal(t, result.Name, "local") 238 } 239 240 func testImportNetworkWithURL(t *testing.T) { 241 // given 242 ctx := context.Background() 243 244 d := t.TempDir() 245 filePath := filepath.Join(d + "tmp.toml") 246 err := os.WriteFile(filePath, []byte("Name = \"local\""), 0o644) 247 require.NoError(t, err) 248 249 // setup 250 _ = "network-path/local.toml" 251 handler := newImportNetworkHandler(t) 252 253 testCases := []struct { 254 name string 255 url string 256 suggestion string 257 jsonrpcErr jsonrpc.ErrorCode 258 }{ 259 { 260 name: "real-url", 261 url: "https://github.com/vegaprotocol/networks-internal/blob/main/fairground/vegawallet-fairground.toml", 262 suggestion: "https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml", 263 jsonrpcErr: jsonrpc.ErrorCodeInvalidParams, 264 }, 265 { 266 name: "without s in http", 267 url: "http://github.com/blah/blob/main/fairground/network.toml", 268 suggestion: "http://raw.githubusercontent.com/blah/main/fairground/network.toml", 269 jsonrpcErr: jsonrpc.ErrorCodeInvalidParams, 270 }, 271 { 272 name: "non-github url tries to fetch", 273 url: "https://example.com", 274 jsonrpcErr: jsonrpc.ErrorCodeInternalError, 275 }, 276 { 277 name: "missing .toml tries to fetch", 278 url: "https://github.com/vegaprotocol/vega/issues", 279 jsonrpcErr: jsonrpc.ErrorCodeInternalError, 280 }, 281 } 282 for _, tc := range testCases { 283 t.Run(tc.name, func(t *testing.T) { 284 _, errorDetails := handler.handle(t, ctx, api.AdminImportNetworkParams{ 285 URL: tc.url, 286 }) 287 // then 288 require.NotNil(t, errorDetails) 289 assert.Equal(t, tc.jsonrpcErr, errorDetails.Code) 290 if tc.suggestion != "" { 291 require.Contains(t, errorDetails.Data, tc.suggestion) 292 } 293 }) 294 } 295 } 296 297 func testImportNetworkWithNotTOMLContentFailsWithFriendlyMessage(t *testing.T) { 298 // given 299 ctx := context.Background() 300 d := t.TempDir() 301 302 // setup 303 handler := newImportNetworkHandler(t) 304 305 tcs := []struct { 306 name string 307 content []byte 308 identifiedType string 309 }{ 310 { 311 name: "when HTML", 312 content: []byte("<!DOCTYPE html><html></html>"), 313 identifiedType: "HTML", 314 }, { 315 name: "when JSON", 316 content: []byte("{\"type\":\"JSON\"}"), 317 identifiedType: "JSON", 318 }, { 319 name: "when JSON", 320 content: []byte("{\"type\":\"JSON\"}"), 321 identifiedType: "JSON", 322 }, 323 } 324 325 for _, tc := range tcs { 326 t.Run(tc.name, func(tt *testing.T) { 327 filePath := filepath.Join(d + "tmp.toml") 328 err := os.WriteFile(filePath, tc.content, 0o644) 329 require.NoError(tt, err) 330 331 // when 332 result, errorDetails := handler.handle(tt, ctx, api.AdminImportNetworkParams{ 333 URL: api.FileSchemePrefix + filePath, 334 }) 335 336 // then 337 require.NotNil(tt, errorDetails) 338 assert.Equal(tt, fmt.Sprintf("could not read the network configuration at %q: the content looks like it contains %s, be sure your file has TOML formatting", filePath, tc.identifiedType), errorDetails.Data) 339 assert.Empty(tt, result) 340 }) 341 } 342 } 343 344 type importNetworkHandler struct { 345 *api.AdminImportNetwork 346 ctrl *gomock.Controller 347 networkStore *mocks.MockNetworkStore 348 } 349 350 func (h *importNetworkHandler) handle(t *testing.T, ctx context.Context, params jsonrpc.Params) (api.AdminImportNetworkResult, *jsonrpc.ErrorDetails) { 351 t.Helper() 352 353 rawResult, err := h.Handle(ctx, params) 354 if rawResult != nil { 355 result, ok := rawResult.(api.AdminImportNetworkResult) 356 if !ok { 357 t.Fatal("AdminImportWallet handler result is not a AdminImportWalletResult") 358 } 359 return result, err 360 } 361 return api.AdminImportNetworkResult{}, err 362 } 363 364 func newImportNetworkHandler(t *testing.T) *importNetworkHandler { 365 t.Helper() 366 367 ctrl := gomock.NewController(t) 368 networkStore := mocks.NewMockNetworkStore(ctrl) 369 370 return &importNetworkHandler{ 371 AdminImportNetwork: api.NewAdminImportNetwork(networkStore), 372 ctrl: ctrl, 373 networkStore: networkStore, 374 } 375 }