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  }