1package namespace 2 3import ( 4 "os" 5 "path/filepath" 6 "testing" 7 8 "github.com/stretchr/testify/require" 9 "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/config" 10 "gitlab.com/gitlab-org/gitaly/v14/internal/helper" 11 "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" 12 "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper/testserver" 13 "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" 14 "google.golang.org/grpc/codes" 15) 16 17func TestMain(m *testing.M) { 18 os.Exit(testMain(m)) 19} 20 21func testMain(m *testing.M) int { 22 defer testhelper.MustHaveNoChildProcess() 23 24 cleanup := testhelper.Configure() 25 defer cleanup() 26 27 return m.Run() 28} 29 30func TestNamespaceExists(t *testing.T) { 31 cfg, client := setupNamespaceService(t, testserver.WithDisablePraefect()) 32 existingStorage := cfg.Storages[0] 33 34 ctx, cancel := testhelper.Context() 35 defer cancel() 36 37 const existingNamespace = "existing" 38 require.NoError(t, os.MkdirAll(filepath.Join(existingStorage.Path, existingNamespace), 0755)) 39 40 queries := []struct { 41 desc string 42 request *gitalypb.NamespaceExistsRequest 43 errorCode codes.Code 44 exists bool 45 }{ 46 { 47 desc: "empty name", 48 request: &gitalypb.NamespaceExistsRequest{ 49 StorageName: existingStorage.Name, 50 Name: "", 51 }, 52 errorCode: codes.InvalidArgument, 53 }, 54 { 55 desc: "Namespace doesn't exists", 56 request: &gitalypb.NamespaceExistsRequest{ 57 StorageName: existingStorage.Name, 58 Name: "not-existing", 59 }, 60 errorCode: codes.OK, 61 exists: false, 62 }, 63 { 64 desc: "Wrong storage path", 65 request: &gitalypb.NamespaceExistsRequest{ 66 StorageName: "other", 67 Name: existingNamespace, 68 }, 69 errorCode: codes.OK, 70 exists: false, 71 }, 72 { 73 desc: "Namespace exists", 74 request: &gitalypb.NamespaceExistsRequest{ 75 StorageName: existingStorage.Name, 76 Name: existingNamespace, 77 }, 78 errorCode: codes.OK, 79 exists: true, 80 }, 81 } 82 83 for _, tc := range queries { 84 t.Run(tc.desc, func(t *testing.T) { 85 response, err := client.NamespaceExists(ctx, tc.request) 86 87 require.Equal(t, tc.errorCode, helper.GrpcCode(err)) 88 89 if tc.errorCode == codes.OK { 90 require.Equal(t, tc.exists, response.Exists) 91 } 92 }) 93 } 94} 95 96func getStorageDir(t *testing.T, cfg config.Cfg, storageName string) string { 97 t.Helper() 98 s, found := cfg.Storage(storageName) 99 require.True(t, found) 100 return s.Path 101} 102 103func TestAddNamespace(t *testing.T) { 104 cfg, client := setupNamespaceService(t) 105 existingStorage := cfg.Storages[0] 106 107 queries := []struct { 108 desc string 109 request *gitalypb.AddNamespaceRequest 110 errorCode codes.Code 111 }{ 112 { 113 desc: "No name", 114 request: &gitalypb.AddNamespaceRequest{ 115 StorageName: existingStorage.Name, 116 Name: "", 117 }, 118 errorCode: codes.InvalidArgument, 119 }, 120 { 121 desc: "Namespace is successfully created", 122 request: &gitalypb.AddNamespaceRequest{ 123 StorageName: existingStorage.Name, 124 Name: "create-me", 125 }, 126 errorCode: codes.OK, 127 }, 128 { 129 desc: "Idempotent on creation", 130 request: &gitalypb.AddNamespaceRequest{ 131 StorageName: existingStorage.Name, 132 Name: "create-me", 133 }, 134 errorCode: codes.OK, 135 }, 136 { 137 desc: "no storage", 138 request: &gitalypb.AddNamespaceRequest{ 139 StorageName: "", 140 Name: "mepmep", 141 }, 142 errorCode: codes.InvalidArgument, 143 }, 144 } 145 146 for _, tc := range queries { 147 t.Run(tc.desc, func(t *testing.T) { 148 ctx, cancel := testhelper.Context() 149 defer cancel() 150 151 _, err := client.AddNamespace(ctx, tc.request) 152 153 require.Equal(t, tc.errorCode, helper.GrpcCode(err)) 154 155 // Clean up 156 if tc.errorCode == codes.OK { 157 require.Equal(t, existingStorage.Name, tc.request.StorageName, "sanity check") 158 159 require.DirExists(t, filepath.Join(existingStorage.Path, tc.request.Name)) 160 } 161 }) 162 } 163} 164 165func TestRemoveNamespace(t *testing.T) { 166 cfg, client := setupNamespaceService(t) 167 existingStorage := cfg.Storages[0] 168 169 ctx, cancel := testhelper.Context() 170 defer cancel() 171 172 const existingNamespace = "created" 173 require.NoError(t, os.MkdirAll(filepath.Join(existingStorage.Path, existingNamespace), 0755), "test setup") 174 175 queries := []struct { 176 desc string 177 request *gitalypb.RemoveNamespaceRequest 178 errorCode codes.Code 179 }{ 180 { 181 desc: "Namespace is successfully removed", 182 request: &gitalypb.RemoveNamespaceRequest{ 183 StorageName: existingStorage.Name, 184 Name: existingNamespace, 185 }, 186 errorCode: codes.OK, 187 }, 188 { 189 desc: "Idempotent on deletion", 190 request: &gitalypb.RemoveNamespaceRequest{ 191 StorageName: existingStorage.Name, 192 Name: "not-there", 193 }, 194 errorCode: codes.OK, 195 }, 196 { 197 desc: "no storage", 198 request: &gitalypb.RemoveNamespaceRequest{ 199 StorageName: "", 200 Name: "mepmep", 201 }, 202 errorCode: codes.InvalidArgument, 203 }, 204 } 205 206 for _, tc := range queries { 207 t.Run(tc.desc, func(t *testing.T) { 208 _, err := client.RemoveNamespace(ctx, tc.request) 209 require.Equal(t, tc.errorCode, helper.GrpcCode(err)) 210 211 if tc.errorCode == codes.OK { 212 require.Equal(t, existingStorage.Name, tc.request.StorageName, "sanity check") 213 require.NoFileExists(t, filepath.Join(existingStorage.Path, tc.request.Name)) 214 } 215 }) 216 } 217} 218 219func TestRenameNamespace(t *testing.T) { 220 cfg, client := setupNamespaceService(t) 221 existingStorage := cfg.Storages[0] 222 223 ctx, cancel := testhelper.Context() 224 defer cancel() 225 226 const existingNamespace = "existing" 227 require.NoError(t, os.MkdirAll(filepath.Join(existingStorage.Path, existingNamespace), 0755)) 228 229 queries := []struct { 230 desc string 231 request *gitalypb.RenameNamespaceRequest 232 errorCode codes.Code 233 }{ 234 { 235 desc: "Renaming an existing namespace", 236 request: &gitalypb.RenameNamespaceRequest{ 237 From: existingNamespace, 238 To: "new-path", 239 StorageName: existingStorage.Name, 240 }, 241 errorCode: codes.OK, 242 }, 243 { 244 desc: "No from given", 245 request: &gitalypb.RenameNamespaceRequest{ 246 From: "", 247 To: "new-path", 248 StorageName: existingStorage.Name, 249 }, 250 errorCode: codes.InvalidArgument, 251 }, 252 { 253 desc: "non-existing namespace", 254 request: &gitalypb.RenameNamespaceRequest{ 255 From: "non-existing", 256 To: "new-path", 257 StorageName: existingStorage.Name, 258 }, 259 errorCode: codes.InvalidArgument, 260 }, 261 { 262 desc: "existing destination namespace", 263 request: &gitalypb.RenameNamespaceRequest{ 264 From: existingNamespace, 265 To: existingNamespace, 266 StorageName: existingStorage.Name, 267 }, 268 errorCode: codes.InvalidArgument, 269 }, 270 } 271 272 for _, tc := range queries { 273 t.Run(tc.desc, func(t *testing.T) { 274 _, err := client.RenameNamespace(ctx, tc.request) 275 276 require.Equal(t, tc.errorCode, helper.GrpcCode(err)) 277 278 if tc.errorCode == codes.OK { 279 toDir := filepath.Join(existingStorage.Path, tc.request.To) 280 require.DirExists(t, toDir) 281 require.NoError(t, os.RemoveAll(toDir)) 282 } 283 }) 284 } 285} 286 287func TestRenameNamespaceWithNonexistentParentDir(t *testing.T) { 288 cfg, client := setupNamespaceService(t) 289 existingStorage := cfg.Storages[0] 290 291 ctx, cancel := testhelper.Context() 292 defer cancel() 293 294 _, err := client.AddNamespace(ctx, &gitalypb.AddNamespaceRequest{ 295 StorageName: existingStorage.Name, 296 Name: "existing", 297 }) 298 require.NoError(t, err) 299 300 testCases := []struct { 301 desc string 302 request *gitalypb.RenameNamespaceRequest 303 errorCode codes.Code 304 }{ 305 { 306 desc: "existing source, non existing target directory", 307 request: &gitalypb.RenameNamespaceRequest{ 308 From: "existing", 309 To: "some/other/new-path", 310 StorageName: existingStorage.Name, 311 }, 312 errorCode: codes.OK, 313 }, 314 } 315 316 for _, tc := range testCases { 317 t.Run(tc.desc, func(t *testing.T) { 318 _, err = client.RenameNamespace(ctx, &gitalypb.RenameNamespaceRequest{ 319 From: "existing", 320 To: "some/other/new-path", 321 StorageName: existingStorage.Name, 322 }) 323 require.Equal(t, tc.errorCode, helper.GrpcCode(err)) 324 325 if tc.errorCode == codes.OK { 326 storagePath := getStorageDir(t, cfg, tc.request.StorageName) 327 require.NoError(t, err) 328 329 toDir := namespacePath(storagePath, tc.request.GetTo()) 330 331 require.DirExists(t, toDir) 332 require.NoError(t, os.RemoveAll(toDir)) 333 } 334 }) 335 } 336} 337