github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/malwarescan/malwarescan_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package malwarescan 5 6 import ( 7 "fmt" 8 piperhttp "github.com/SAP/jenkins-library/pkg/http" 9 "github.com/stretchr/testify/assert" 10 "io" 11 "net/http" 12 "testing" 13 ) 14 15 func TestMalwareServiceScan(t *testing.T) { 16 t.Run("Scan without finding", func(t *testing.T) { 17 httpClient := &httpMock{StatusCode: 200, ResponseBody: "{\"malwareDetected\":false,\"encryptedContentDetected\":false,\"scanSize\":298782,\"mimeType\":\"application/octet-stream\",\"SHA256\":\"96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85\"}"} 18 19 malwareService := ClientImpl{ 20 HTTPClient: httpClient, 21 Host: "https://example.org/malwarescanner", 22 } 23 24 candidate := readCloserMock{Content: "HELLO"} 25 scanResult, err := malwareService.Scan(candidate) 26 27 if assert.NoError(t, err) { 28 assert.True(t, httpClient.Body.Closed) 29 30 assert.Equal(t, "https://example.org/malwarescanner/scan", httpClient.URL) 31 assert.Equal(t, "POST", httpClient.Method) 32 33 if assert.NotNil(t, httpClient.Header) { 34 assert.Equal(t, "application/octet-stream", httpClient.Header.Get("Content-Type")) 35 } 36 37 assert.Equal(t, "application/octet-stream", scanResult.MimeType) 38 assert.Equal(t, 298782, scanResult.ScanSize) 39 assert.Equal(t, "96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85", scanResult.SHA256) 40 assert.Equal(t, "", scanResult.Finding) 41 assert.False(t, scanResult.MalwareDetected) 42 assert.False(t, scanResult.EncryptedContentDetected) 43 } 44 }) 45 46 t.Run("Scan without finding", func(t *testing.T) { 47 httpClient := &httpMock{StatusCode: 200, ResponseBody: "{\"malwareDetected\":true,\"encryptedContentDetected\":true,\"scanSize\":298782,\"mimeType\":\"application/octet-stream\",\"SHA256\":\"96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85\", \"finding\": \"Description of the finding\"}"} 48 49 malwareService := ClientImpl{ 50 HTTPClient: httpClient, 51 Host: "https://example.org/malwarescanner", 52 } 53 54 candidate := readCloserMock{Content: "HELLO"} 55 scanResult, err := malwareService.Scan(candidate) 56 57 if assert.NoError(t, err) { 58 assert.True(t, httpClient.Body.Closed) 59 60 assert.Equal(t, "https://example.org/malwarescanner/scan", httpClient.URL) 61 assert.Equal(t, "POST", httpClient.Method) 62 63 if assert.NotNil(t, httpClient.Header) { 64 assert.Equal(t, "application/octet-stream", httpClient.Header.Get("Content-Type")) 65 } 66 67 assert.Equal(t, "application/octet-stream", scanResult.MimeType) 68 assert.Equal(t, 298782, scanResult.ScanSize) 69 assert.Equal(t, "96ca802fbd54d31903f1115a1d95590c685160637d9262bd340ab30d0f817e85", scanResult.SHA256) 70 assert.Equal(t, "Description of the finding", scanResult.Finding) 71 assert.True(t, scanResult.MalwareDetected) 72 assert.True(t, scanResult.EncryptedContentDetected) 73 } 74 }) 75 76 t.Run("Scan results in error - file to large", func(t *testing.T) { 77 httpClient := &httpMock{StatusCode: 413, ResponseBody: "{\"message\":\"Payload too large - The file is too large and cannot be scanned or the archive structure is too complex.\"}"} 78 79 malwareService := ClientImpl{ 80 HTTPClient: httpClient, 81 Host: "https://example.org/malwarescanner", 82 } 83 84 candidate := readCloserMock{Content: "HELLO"} 85 scanResult, err := malwareService.Scan(candidate) 86 87 assert.Nil(t, scanResult) 88 assert.EqualError(t, err, "MalwareService returned with status code 413: Payload too large - The file is too large and cannot be scanned or the archive structure is too complex.") 89 }) 90 91 t.Run("Scan results in error - unexpected error", func(t *testing.T) { 92 httpClient := &httpMock{StatusCode: 500, ResponseBody: ""} 93 94 malwareService := ClientImpl{ 95 HTTPClient: httpClient, 96 Host: "https://example.org/malwarescanner", 97 } 98 99 candidate := readCloserMock{Content: "HELLO"} 100 scanResult, err := malwareService.Scan(candidate) 101 102 assert.Nil(t, scanResult) 103 assert.EqualError(t, err, "MalwareService returned with status code 500, no further information available") 104 }) 105 } 106 107 func TestMalwareServiceInfo(t *testing.T) { 108 t.Run("Receives engine info", func(t *testing.T) { 109 httpClient := &httpMock{StatusCode: 200, ResponseBody: "{\"engineVersion\": \"Malware Service Mock\", \"signatureTimestamp\": \"2022-01-12T09:26:28.000Z\", \"maxScanSize\": 666}"} 110 111 malwareService := ClientImpl{ 112 HTTPClient: httpClient, 113 Host: "https://example.org/malwarescanner", 114 } 115 116 info, err := malwareService.Info() 117 118 if assert.NoError(t, err) { 119 assert.True(t, httpClient.Body.Closed) 120 121 assert.Equal(t, "https://example.org/malwarescanner/info", httpClient.URL) 122 assert.Equal(t, "GET", httpClient.Method) 123 assert.Equal(t, "Malware Service Mock", info.EngineVersion) 124 assert.Equal(t, "2022-01-12T09:26:28.000Z", info.SignatureTimestamp) 125 assert.Equal(t, 666, info.MaxScanSize) 126 } 127 }) 128 } 129 130 type httpMock struct { 131 Method string // is set during test execution 132 URL string // is set before test execution 133 ResponseBody string // is set before test execution 134 Options piperhttp.ClientOptions // is set during test 135 StatusCode int // is set during test 136 Body readCloserMock // is set during test 137 Header http.Header // is set during test 138 } 139 140 func (c *httpMock) SetOptions(options piperhttp.ClientOptions) { 141 c.Options = options 142 } 143 144 func (c *httpMock) SendRequest(method string, url string, r io.Reader, header http.Header, cookies []*http.Cookie) (*http.Response, error) { 145 c.Method = method 146 c.URL = url 147 c.Header = header 148 149 if r != nil { 150 _, err := io.ReadAll(r) 151 152 if err != nil { 153 return nil, err 154 } 155 } 156 157 c.Body = readCloserMock{Content: c.ResponseBody} 158 res := http.Response{StatusCode: c.StatusCode, Body: &c.Body} 159 160 return &res, nil 161 } 162 163 type readCloserMock struct { 164 Content string 165 Closed bool 166 } 167 168 func (rc readCloserMock) Read(b []byte) (n int, err error) { 169 170 if len(b) < len(rc.Content) { 171 // in real life we would fill the buffer according to buffer size ... 172 return 0, fmt.Errorf("Buffer size (%d) not sufficient, need: %d", len(b), len(rc.Content)) 173 } 174 copy(b, rc.Content) 175 return len(rc.Content), io.EOF 176 } 177 178 func (rc *readCloserMock) Close() error { 179 rc.Closed = true 180 return nil 181 }