github.com/apptainer/singularity@v3.1.1+incompatible/pkg/client/library/util_test.go (about) 1 // Copyright (c) 2018, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 package client 7 8 import ( 9 "reflect" 10 "strings" 11 "testing" 12 13 "github.com/globalsign/mgo/bson" 14 "github.com/sylabs/singularity/internal/pkg/test" 15 ) 16 17 func Test_isLibraryPullRef(t *testing.T) { 18 tests := []struct { 19 name string 20 libraryRef string 21 want bool 22 }{ 23 {"Good long ref 1", "library://entity/collection/image:tag", true}, 24 {"Good long ref 2", "entity/collection/image:tag", true}, 25 {"Good long ref 3", "entity/collection/image", true}, 26 {"Good short ref 1", "library://image:tag", true}, 27 {"Good short ref 2", "library://image", true}, 28 {"Good short ref 3", "library://collection/image:tag", true}, 29 {"Good short ref 4", "library://image", true}, 30 {"Good long sha ref 1", "library://entity/collection/image:sha256.e50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e88", true}, 31 {"Good long sha ref 2", "entity/collection/image:sha256.e50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e88", true}, 32 {"Good short sha ref 1", "library://image:sha256.e50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e88", true}, 33 {"Good short sha ref 2", "image:sha256.e50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e88", true}, 34 {"Good short sha ref 3", "library://collection/image:sha256.e50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e88", true}, 35 {"Good short sha ref 4", "collection/image:sha256.e50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e88", true}, 36 {"Too many components", "library://entity/collection/extra/image:tag", false}, 37 {"Bad character", "library://entity/collection/im,age:tag", false}, 38 {"Bad initial character", "library://entity/collection/-image:tag", false}, 39 } 40 for _, tt := range tests { 41 t.Run(tt.name, test.WithoutPrivilege(func(t *testing.T) { 42 if got := IsLibraryPullRef(tt.libraryRef); got != tt.want { 43 t.Errorf("isLibraryPullRef() = %v, want %v", got, tt.want) 44 } 45 })) 46 } 47 } 48 49 func Test_isLibraryPushRef(t *testing.T) { 50 tests := []struct { 51 name string 52 libraryRef string 53 want bool 54 }{ 55 {"Good long ref 1", "library://entity/collection/image:tag", true}, 56 {"Good long ref 2", "entity/collection/image:tag", true}, 57 {"Good long ref 3", "entity/collection/image", true}, 58 {"Short ref not allowed 1", "library://image:tag", false}, 59 {"Short ref not allowed 2", "library://image", false}, 60 {"Short ref not allowed 3", "library://collection/image:tag", false}, 61 {"Short ref not allowed 4", "library://image", false}, 62 {"Good long sha ref 1", "library://entity/collection/image:sha256.e50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e88", true}, 63 {"Good long sha ref 2", "entity/collection/image:sha256.e50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e88", true}, 64 {"Too many components", "library://entity/collection/extra/image:tag", false}, 65 {"Bad character", "library://entity/collection/im,age:tag", false}, 66 {"Bad initial character", "library://entity/collection/-image:tag", false}, 67 {"No capitals", "library://Entity/collection/image:tag", false}, 68 } 69 for _, tt := range tests { 70 t.Run(tt.name, test.WithoutPrivilege(func(t *testing.T) { 71 if got := IsLibraryPushRef(tt.libraryRef); got != tt.want { 72 t.Errorf("isLibraryPushRef() = %v, want %v", got, tt.want) 73 } 74 })) 75 } 76 } 77 78 func Test_IsRefPart(t *testing.T) { 79 tests := []struct { 80 name string 81 libraryRef string 82 want bool 83 }{ 84 {"Good ref 1", "abc123", true}, 85 {"Good ref 2", "abc-123", true}, 86 {"Good ref 3", "abc_123", true}, 87 {"Good ref 4", "abc.123", true}, 88 {"Bad character", "abc,123", false}, 89 {"Bad initial character", "-abc123", false}, 90 {"No capitals", "Abc123", false}, 91 } 92 for _, tt := range tests { 93 t.Run(tt.name, test.WithoutPrivilege(func(t *testing.T) { 94 if got := IsRefPart(tt.libraryRef); got != tt.want { 95 t.Errorf("IsRefPart() = %v, want %v", got, tt.want) 96 } 97 })) 98 } 99 } 100 101 func Test_IsImageHash(t *testing.T) { 102 tests := []struct { 103 name string 104 libraryRef string 105 want bool 106 }{ 107 {"Good sha256", "sha256.e50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e88", true}, 108 {"Good sif", "sif.5574b72c-7705-49cc-874e-424fc3b78116", true}, 109 {"sha256 too long", "sha256.e50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e88a", false}, 110 {"sha256 too short", "sha256.e50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e8", false}, 111 {"sha256 bad character", "sha256.g50a30881ace3d5944f5661d222db7bee5296be9e4dc7c1fcb7604bcae926e88", false}, 112 {"sif too long", "sif.5574b72c-7705-49cc-874e-424fc3b78116a", false}, 113 {"sif too short", "sif.5574b72c-7705-49cc-874e-424fc3b7811", false}, 114 {"sif bad character", "sif.g574b72c-7705-49cc-874e-424fc3b78116", false}, 115 } 116 for _, tt := range tests { 117 t.Run(tt.name, test.WithoutPrivilege(func(t *testing.T) { 118 if got := IsImageHash(tt.libraryRef); got != tt.want { 119 t.Errorf("IsImageHash() = %v, want %v", got, tt.want) 120 } 121 })) 122 } 123 } 124 125 func Test_parseLibraryRef(t *testing.T) { 126 tests := []struct { 127 name string 128 libraryRef string 129 wantEnt string 130 wantCol string 131 wantCon string 132 wantTags []string 133 }{ 134 {"Good long ref 1", "library://entity/collection/image:tag", "entity", "collection", "image", []string{"tag"}}, 135 {"Good long ref 2", "entity/collection/image:tag", "entity", "collection", "image", []string{"tag"}}, 136 {"Good long ref latest", "library://entity/collection/image", "entity", "collection", "image", []string{"latest"}}, 137 {"Good long ref multi tag", "library://entity/collection/image:tag1,tag2,tag3", "entity", "collection", "image", []string{"tag1", "tag2", "tag3"}}, 138 {"Good short ref 1", "library://image:tag", "", "", "image", []string{"tag"}}, 139 {"Good short ref 2", "image:tag", "", "", "image", []string{"tag"}}, 140 {"Good short ref 3", "library://collection/image:tag", "", "collection", "image", []string{"tag"}}, 141 {"Good short ref 4", "collection/image:tag", "", "collection", "image", []string{"tag"}}, 142 {"Good short ref latest", "library://image", "", "", "image", []string{"latest"}}, 143 {"Good short ref multi tag", "library://image:tag1,tag2,tag3", "", "", "image", []string{"tag1", "tag2", "tag3"}}, 144 } 145 for _, tt := range tests { 146 t.Run(tt.name, test.WithoutPrivilege(func(t *testing.T) { 147 ent, col, con, tags := parseLibraryRef(tt.libraryRef) 148 if ent != tt.wantEnt { 149 t.Errorf("parseLibraryRef() = entity %v, want %v", ent, tt.wantEnt) 150 } 151 if col != tt.wantCol { 152 t.Errorf("parseLibraryRef() = collection %v, want %v", col, tt.wantCol) 153 } 154 if con != tt.wantCon { 155 t.Errorf("parseLibraryRef() = container %v, want %v", con, tt.wantCon) 156 } 157 if !reflect.DeepEqual(tags, tt.wantTags) { 158 t.Errorf("parseLibraryRef() = entity %v, want %v", tags, tt.wantTags) 159 } 160 })) 161 } 162 } 163 164 func Test_ParseErrorBody(t *testing.T) { 165 166 test.DropPrivilege(t) 167 defer test.ResetPrivilege(t) 168 169 eb := JSONError{ 170 Code: 500, 171 Status: "Internal Server Error", 172 Message: "The server had a problem", 173 } 174 ebJSON := "{ \"error\": {\"code\": 500, \"status\": \"Internal Server Error\", \"message\": \"The server had a problem\"}}" 175 r := strings.NewReader(ebJSON) 176 177 jRes, err := ParseErrorBody(r) 178 179 if err != nil { 180 t.Errorf("Decoding good error response did not succeed: %v", err) 181 } 182 183 if !reflect.DeepEqual(jRes.Error, eb) { 184 t.Errorf("Decoding error body expected %v, got %v", eb, jRes) 185 } 186 187 ebJSON = "{ \"error {\"code\": 500, \"status\": \"Internal Server Error\", \"message\": \"The server had a problem\"}}" 188 jRes, err = ParseErrorBody(r) 189 190 if err == nil { 191 t.Errorf("Decoding bad error response succeeded, but should return an error: %v", ebJSON) 192 } 193 194 } 195 196 func TestIdInSlice(t *testing.T) { 197 198 test.DropPrivilege(t) 199 defer test.ResetPrivilege(t) 200 201 trueID := bson.NewObjectId() 202 203 slice := []bson.ObjectId{trueID, bson.NewObjectId(), bson.NewObjectId(), bson.NewObjectId()} 204 if !IDInSlice(trueID, slice) { 205 t.Errorf("should find %v in %v", trueID, slice) 206 } 207 208 slice = []bson.ObjectId{bson.NewObjectId(), bson.NewObjectId(), trueID, bson.NewObjectId()} 209 if !IDInSlice(trueID, slice) { 210 t.Errorf("should find %v in %v", trueID, slice) 211 } 212 213 slice = []bson.ObjectId{bson.NewObjectId(), bson.NewObjectId(), bson.NewObjectId(), trueID} 214 if !IDInSlice(trueID, slice) { 215 t.Errorf("should find %v in %v", trueID, slice) 216 } 217 218 falseID := bson.NewObjectId() 219 if IDInSlice(falseID, slice) { 220 t.Errorf("should not find %v in %v", trueID, slice) 221 } 222 223 } 224 225 func TestSliceWithoutID(t *testing.T) { 226 227 test.DropPrivilege(t) 228 defer test.ResetPrivilege(t) 229 230 a := bson.NewObjectId() 231 b := bson.NewObjectId() 232 c := bson.NewObjectId() 233 d := bson.NewObjectId() 234 z := bson.NewObjectId() 235 slice := []bson.ObjectId{a, b, c, d} 236 237 result := SliceWithoutID(slice, a) 238 if !reflect.DeepEqual([]bson.ObjectId{b, c, d}, result) { 239 t.Errorf("error removing a from {a, b, c, d}, got: %v", result) 240 } 241 result = SliceWithoutID(slice, b) 242 if !reflect.DeepEqual([]bson.ObjectId{a, c, d}, result) { 243 t.Errorf("error removing b from {a, b, c, d}, got: %v", result) 244 } 245 result = SliceWithoutID(slice, d) 246 if !reflect.DeepEqual([]bson.ObjectId{a, b, c}, result) { 247 t.Errorf("error removing c from {a, b, c, d}, got: %v", result) 248 } 249 result = SliceWithoutID(slice, z) 250 if !reflect.DeepEqual([]bson.ObjectId{a, b, c, d}, result) { 251 t.Errorf("error removing non-existent z from {a, b, c, d}, got: %v", result) 252 } 253 } 254 255 func TestStringInSlice(t *testing.T) { 256 257 test.DropPrivilege(t) 258 defer test.ResetPrivilege(t) 259 260 trueID := bson.NewObjectId().Hex() 261 262 slice := []string{trueID, bson.NewObjectId().Hex(), bson.NewObjectId().Hex(), bson.NewObjectId().Hex()} 263 if !StringInSlice(trueID, slice) { 264 t.Errorf("should find %v in %v", trueID, slice) 265 } 266 267 slice = []string{bson.NewObjectId().Hex(), bson.NewObjectId().Hex(), trueID, bson.NewObjectId().Hex()} 268 if !StringInSlice(trueID, slice) { 269 t.Errorf("should find %v in %v", trueID, slice) 270 } 271 272 slice = []string{bson.NewObjectId().Hex(), bson.NewObjectId().Hex(), bson.NewObjectId().Hex(), trueID} 273 if !StringInSlice(trueID, slice) { 274 t.Errorf("should find %v in %v", trueID, slice) 275 } 276 277 falseID := bson.NewObjectId().Hex() 278 if StringInSlice(falseID, slice) { 279 t.Errorf("should not find %v in %v", trueID, slice) 280 } 281 282 } 283 284 func Test_imageHash(t *testing.T) { 285 286 test.DropPrivilege(t) 287 defer test.ResetPrivilege(t) 288 289 expectedSha256 := "sha256.d7d356079af905c04e5ae10711ecf3f5b34385e9b143c5d9ddbf740665ce2fb7" 290 291 shasum, err := ImageHash("no_such_file.txt") 292 if err == nil { 293 t.Error("Invalid file must return an error") 294 } 295 296 shasum, err = ImageHash("test_data/test_sha256") 297 if err != nil { 298 t.Errorf("ImageHash on valid file should not raise error: %v", err) 299 } 300 if shasum != expectedSha256 { 301 t.Errorf("ImageHash returned %v - expected %v", shasum, expectedSha256) 302 } 303 } 304 305 func Test_sha256sum(t *testing.T) { 306 307 test.DropPrivilege(t) 308 defer test.ResetPrivilege(t) 309 310 expectedSha256 := "sha256.d7d356079af905c04e5ae10711ecf3f5b34385e9b143c5d9ddbf740665ce2fb7" 311 312 shasum, err := sha256sum("no_such_file.txt") 313 if err == nil { 314 t.Error("Invalid file must return an error") 315 } 316 317 shasum, err = sha256sum("test_data/test_sha256") 318 if err != nil { 319 t.Errorf("sha256sum on valid file should not raise error: %v", err) 320 } 321 if shasum != expectedSha256 { 322 t.Errorf("sha256sum returned %v - expected %v", shasum, expectedSha256) 323 } 324 }