github.com/weaviate/weaviate@v1.24.6/usecases/backup/handler_test.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package backup 13 14 import ( 15 "context" 16 "errors" 17 "fmt" 18 "testing" 19 "time" 20 21 "github.com/stretchr/testify/assert" 22 "github.com/weaviate/weaviate/entities/backup" 23 "github.com/weaviate/weaviate/entities/models" 24 ) 25 26 // helper methods 27 func (m *Handler) Backup(ctx context.Context, pr *models.Principal, req *BackupRequest, 28 ) (*models.BackupCreateResponse, error) { 29 store, err := nodeBackend(m.node, m.backends, req.Backend, req.ID) 30 if err != nil { 31 err = fmt.Errorf("no backup backend %q, did you enable the right module?", req.Backend) 32 return nil, backup.NewErrUnprocessable(err) 33 } 34 35 classes := req.Include 36 if err := store.Initialize(ctx); err != nil { 37 return nil, backup.NewErrUnprocessable(fmt.Errorf("init uploader: %w", err)) 38 } 39 if meta, err := m.backupper.Backup(ctx, store, req.ID, classes); err != nil { 40 return nil, err 41 } else { 42 status := string(meta.Status) 43 return &models.BackupCreateResponse{ 44 Classes: classes, 45 ID: req.ID, 46 Backend: req.Backend, 47 Status: &status, 48 Path: meta.Path, 49 }, nil 50 } 51 } 52 53 func (m *Handler) Restore(ctx context.Context, pr *models.Principal, 54 req *BackupRequest, 55 ) (*models.BackupRestoreResponse, error) { 56 store, err := nodeBackend(m.node, m.backends, req.Backend, req.ID) 57 if err != nil { 58 err = fmt.Errorf("no backup backend %q, did you enable the right module?", req.Backend) 59 return nil, backup.NewErrUnprocessable(err) 60 } 61 meta, err := m.validateRestoreRequest(ctx, store, req) 62 if err != nil { 63 return nil, err 64 } 65 cs := meta.List() 66 // if cls := m.restorer.AnyExists(cs); cls != "" { 67 // err := fmt.Errorf("cannot restore class %q because it already exists", cls) 68 // return nil, backup.NewErrUnprocessable(err) 69 // } 70 rreq := Request{ 71 Method: OpRestore, 72 ID: meta.ID, 73 Backend: req.Backend, 74 Classes: cs, 75 NodeMapping: req.NodeMapping, 76 } 77 data, err := m.restorer.Restore(ctx, &rreq, meta, store) 78 if err != nil { 79 return nil, backup.NewErrUnprocessable(err) 80 } 81 82 return data, nil 83 } 84 85 func (m *Handler) validateRestoreRequest(ctx context.Context, store nodeStore, req *BackupRequest) (*backup.BackupDescriptor, error) { 86 meta, cs, err := m.restorer.validate(ctx, &store, &Request{ID: req.ID, Classes: req.Include}) 87 if err != nil { 88 if errors.Is(err, errMetaNotFound) { 89 return nil, backup.NewErrNotFound(err) 90 } 91 return nil, backup.NewErrUnprocessable(err) 92 } 93 meta.Exclude(req.Exclude) 94 if len(meta.Classes) == 0 { 95 err = fmt.Errorf("empty class list: please choose from : %v", cs) 96 return nil, backup.NewErrUnprocessable(err) 97 } 98 return meta, nil 99 } 100 101 type fakeSchemaManger struct { 102 errRestoreClass error 103 nodeName string 104 } 105 106 func (f *fakeSchemaManger) RestoreClass(context.Context, *backup.ClassDescriptor, map[string]string, 107 ) error { 108 return f.errRestoreClass 109 } 110 111 func (f *fakeSchemaManger) NodeName() string { 112 return f.nodeName 113 } 114 115 type fakeAuthorizer struct{} 116 117 func (f *fakeAuthorizer) Authorize(principal *models.Principal, verb, resource string) error { 118 return nil 119 } 120 121 func TestFilterClasses(t *testing.T) { 122 tests := []struct { 123 in []string 124 xs []string 125 out []string 126 }{ 127 {in: []string{}, xs: []string{}, out: []string{}}, 128 {in: []string{"a"}, xs: []string{}, out: []string{"a"}}, 129 {in: []string{"a"}, xs: []string{"a"}, out: []string{}}, 130 {in: []string{"1", "2", "3", "4"}, xs: []string{"2", "3"}, out: []string{"1", "4"}}, 131 {in: []string{"1", "2", "3"}, xs: []string{"1", "3"}, out: []string{"2"}}, 132 {in: []string{"1", "2", "1", "3", "1", "3"}, xs: []string{"2"}, out: []string{"1", "3"}}, 133 } 134 for _, tc := range tests { 135 got := filterClasses(tc.in, tc.xs) 136 assert.ElementsMatch(t, tc.out, got) 137 } 138 } 139 140 func TestHandlerValidateCoordinationOperation(t *testing.T) { 141 var ( 142 ctx = context.Background() 143 bm = createManager(nil, nil, nil, nil) 144 ) 145 146 { // OnCanCommit 147 req := Request{ 148 Method: "Unknown", 149 ID: "1", 150 Classes: []string{"class1"}, 151 Backend: "s3", 152 Duration: time.Millisecond * 20, 153 } 154 resp := bm.OnCanCommit(ctx, &req) 155 assert.Contains(t, resp.Err, "unknown backup operation") 156 assert.Equal(t, resp.Timeout, time.Duration(0)) 157 } 158 159 { // OnCommit 160 req := StatusRequest{ 161 Method: "Unknown", 162 ID: "1", 163 Backend: "s3", 164 } 165 err := bm.OnCommit(ctx, &req) 166 assert.NotNil(t, err) 167 assert.ErrorIs(t, err, errUnknownOp) 168 } 169 170 { // OnAbort 171 req := AbortRequest{ 172 Method: "Unknown", 173 ID: "1", 174 } 175 err := bm.OnAbort(ctx, &req) 176 assert.NotNil(t, err) 177 assert.ErrorIs(t, err, errUnknownOp) 178 } 179 { // OnStatus 180 req := StatusRequest{ 181 Method: "Unknown", 182 ID: "1", 183 } 184 ret := bm.OnStatus(ctx, &req) 185 assert.Contains(t, ret.Err, errUnknownOp.Error()) 186 } 187 }