github.com/moov-io/imagecashletter@v0.10.1/internal/files/files_test.go (about)

     1  // Copyright 2020 The Moov Authors
     2  // Use of this source code is governed by an Apache License
     3  // license that can be found in the LICENSE file.
     4  
     5  package files
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	"net/http"
    13  	"net/http/httptest"
    14  	"os"
    15  	"path/filepath"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/stretchr/testify/assert"
    20  
    21  	"github.com/stretchr/testify/require"
    22  
    23  	"github.com/moov-io/base"
    24  	"github.com/moov-io/imagecashletter"
    25  
    26  	"github.com/gorilla/mux"
    27  	"github.com/moov-io/base/log"
    28  )
    29  
    30  func TestFileId(t *testing.T) {
    31  	w := httptest.NewRecorder()
    32  	req := httptest.NewRequest("GET", "/foo", nil)
    33  
    34  	fileID := getFileId(w, req)
    35  
    36  	assert.Empty(t, fileID)
    37  	require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
    38  }
    39  
    40  func TestCashLetterId(t *testing.T) {
    41  	w := httptest.NewRecorder()
    42  	req := httptest.NewRequest("GET", "/foo", nil)
    43  
    44  	cashLetterID := getCashLetterId(w, req)
    45  
    46  	assert.Empty(t, cashLetterID)
    47  	require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
    48  }
    49  
    50  func TestFiles_getFiles(t *testing.T) {
    51  	req := httptest.NewRequest("GET", "/files", nil)
    52  	repo := &testICLFileRepository{
    53  		file: &imagecashletter.File{
    54  			ID: base.ID(),
    55  		},
    56  	}
    57  	router := mux.NewRouter()
    58  	AppendRoutes(log.NewNopLogger(), router, repo)
    59  
    60  	t.Run("returns one file", func(t *testing.T) {
    61  		w := httptest.NewRecorder()
    62  		router.ServeHTTP(w, req)
    63  		w.Flush()
    64  
    65  		require.Equal(t, http.StatusOK, w.Code, w.Body)
    66  		var files []*imagecashletter.File
    67  		require.NoError(t, json.NewDecoder(w.Body).Decode(&files))
    68  		require.Len(t, files, 1)
    69  	})
    70  
    71  	t.Run("repo error", func(t *testing.T) {
    72  		w := httptest.NewRecorder()
    73  		repo.err = errors.New("bad error")
    74  		router.ServeHTTP(w, req)
    75  		w.Flush()
    76  
    77  		require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
    78  	})
    79  }
    80  
    81  func TestFiles_determineBufferSize(t *testing.T) {
    82  	env := t.Name()
    83  
    84  	size := determineBufferSize(env, 10001)
    85  	require.Equal(t, 10001, size)
    86  
    87  	t.Setenv(env, "452181")
    88  
    89  	size = determineBufferSize(env, 10001)
    90  	require.Equal(t, 452181, size)
    91  }
    92  
    93  func TestFiles_createFile(t *testing.T) {
    94  	w := httptest.NewRecorder()
    95  	fd, _ := os.Open(filepath.Join("..", "..", "test", "testdata", "valid-ebcdic.x937"))
    96  	req := httptest.NewRequest("POST", "/files/create", fd)
    97  	repo := &testICLFileRepository{}
    98  	router := mux.NewRouter()
    99  	AppendRoutes(log.NewNopLogger(), router, repo)
   100  	router.ServeHTTP(w, req)
   101  	w.Flush()
   102  
   103  	require.Equal(t, http.StatusCreated, w.Code, w.Body.String())
   104  
   105  	var resp imagecashletter.File
   106  	require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
   107  
   108  	require.Equal(t, "Wave Money", resp.Header.ImmediateDestinationName)
   109  
   110  	// error case
   111  	repo.err = errors.New("bad error")
   112  
   113  	w = httptest.NewRecorder()
   114  	router.ServeHTTP(w, req)
   115  	w.Flush()
   116  
   117  	require.Equal(t, http.StatusBadRequest, w.Code, w.Body.String())
   118  }
   119  
   120  func TestFiles_createFileJSON(t *testing.T) {
   121  	w := httptest.NewRecorder()
   122  	fd, _ := os.Open(filepath.Join("..", "..", "test", "testdata", "icl-valid.json"))
   123  	req := httptest.NewRequest("POST", "/files/create", fd)
   124  	req.Header.Set("Content-Type", "application/json")
   125  	repo := &testICLFileRepository{}
   126  	router := mux.NewRouter()
   127  	AppendRoutes(log.NewNopLogger(), router, repo)
   128  	router.ServeHTTP(w, req)
   129  	w.Flush()
   130  
   131  	require.Equal(t, http.StatusCreated, w.Code, w.Body)
   132  	var resp imagecashletter.File
   133  	require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
   134  	assert.Equal(t, "US", resp.Header.CountryCode)
   135  
   136  	// error case
   137  	w = httptest.NewRecorder()
   138  	req = httptest.NewRequest("POST", "/files/create", strings.NewReader("{invalid-json"))
   139  	req.Header.Set("content-type", "application/json")
   140  
   141  	router.ServeHTTP(w, req)
   142  	w.Flush()
   143  
   144  	require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
   145  }
   146  
   147  func TestFiles_getFile(t *testing.T) {
   148  	repo := &testICLFileRepository{}
   149  	router := mux.NewRouter()
   150  	AppendRoutes(log.NewNopLogger(), router, repo)
   151  	req := httptest.NewRequest("GET", "/files/foo", nil)
   152  
   153  	t.Run("file not found", func(t *testing.T) {
   154  		w := httptest.NewRecorder()
   155  		router.ServeHTTP(w, req)
   156  		w.Flush()
   157  
   158  		require.Equal(t, http.StatusNotFound, w.Code, w.Body)
   159  	})
   160  
   161  	t.Run("successful request", func(t *testing.T) {
   162  		w := httptest.NewRecorder()
   163  		repo.file = &imagecashletter.File{
   164  			ID: base.ID(),
   165  		}
   166  		router.ServeHTTP(w, req)
   167  		w.Flush()
   168  
   169  		require.Equal(t, http.StatusOK, w.Code, w.Body)
   170  		var file imagecashletter.File
   171  		require.NoError(t, json.NewDecoder(w.Body).Decode(&file))
   172  		assert.NotEmpty(t, file.ID)
   173  	})
   174  
   175  	t.Run("repo error", func(t *testing.T) {
   176  		w := httptest.NewRecorder()
   177  		repo.err = errors.New("bad error")
   178  		router.ServeHTTP(w, req)
   179  		w.Flush()
   180  
   181  		require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
   182  	})
   183  }
   184  
   185  func TestFiles_updateFileHeader(t *testing.T) {
   186  	repo := &testICLFileRepository{}
   187  	router := mux.NewRouter()
   188  	AppendRoutes(log.NewNopLogger(), router, repo)
   189  	f := readFile(t, "BNK20180905121042882-A.icl")
   190  	f.ID = base.ID()
   191  
   192  	t.Run("file not found", func(t *testing.T) {
   193  		var buf bytes.Buffer
   194  		require.NoError(t, json.NewEncoder(&buf).Encode(f.Header))
   195  		w := httptest.NewRecorder()
   196  		req := httptest.NewRequest("POST", fmt.Sprintf("/files/%s", f.ID), &buf)
   197  		router.ServeHTTP(w, req)
   198  		w.Flush()
   199  
   200  		require.Equal(t, http.StatusNotFound, w.Code, w.Body)
   201  	})
   202  
   203  	t.Run("successful request", func(t *testing.T) {
   204  		var buf bytes.Buffer
   205  		require.NoError(t, json.NewEncoder(&buf).Encode(f.Header))
   206  		w := httptest.NewRecorder()
   207  		req := httptest.NewRequest("POST", fmt.Sprintf("/files/%s", f.ID), &buf)
   208  		repo.file = &imagecashletter.File{
   209  			ID: f.ID, // create a file without FileHeader so it's updated
   210  		}
   211  		router.ServeHTTP(w, req)
   212  		w.Flush()
   213  
   214  		require.Equal(t, http.StatusCreated, w.Code, w.Body)
   215  		assert.Equal(t, repo.file.Header.CountryCode, f.Header.CountryCode)
   216  	})
   217  }
   218  
   219  func TestFiles_deleteFile(t *testing.T) {
   220  	req := httptest.NewRequest("DELETE", "/files/foo", nil)
   221  	repo := &testICLFileRepository{}
   222  	router := mux.NewRouter()
   223  	AppendRoutes(log.NewNopLogger(), router, repo)
   224  
   225  	t.Run("file not found", func(t *testing.T) {
   226  		w := httptest.NewRecorder()
   227  		router.ServeHTTP(w, req)
   228  		w.Flush()
   229  
   230  		require.Equal(t, http.StatusNotFound, w.Code, w.Body)
   231  	})
   232  
   233  	t.Run("successful request", func(t *testing.T) {
   234  		repo.file = &imagecashletter.File{}
   235  		w := httptest.NewRecorder()
   236  		router.ServeHTTP(w, req)
   237  		w.Flush()
   238  
   239  		require.Equal(t, http.StatusOK, w.Code, w.Body)
   240  	})
   241  
   242  	t.Run("repo error", func(t *testing.T) {
   243  		repo.err = errors.New("bad error")
   244  		w := httptest.NewRecorder()
   245  		router.ServeHTTP(w, req)
   246  		w.Flush()
   247  
   248  		require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
   249  	})
   250  
   251  }
   252  
   253  func TestFiles_getFileContents(t *testing.T) {
   254  	req := httptest.NewRequest("GET", "/files/foo/contents", nil)
   255  	router := mux.NewRouter()
   256  	repo := &testICLFileRepository{}
   257  	AppendRoutes(log.NewNopLogger(), router, repo)
   258  
   259  	t.Run("file not found", func(t *testing.T) {
   260  		w := httptest.NewRecorder()
   261  		router.ServeHTTP(w, req)
   262  		w.Flush()
   263  
   264  		require.Equal(t, http.StatusNotFound, w.Code, w.Body)
   265  	})
   266  
   267  	t.Run("successful request", func(t *testing.T) {
   268  		w := httptest.NewRecorder()
   269  		f := readFile(t, "BNK20180905121042882-A.icl")
   270  		repo.file = f
   271  		router.ServeHTTP(w, req)
   272  		w.Flush()
   273  
   274  		require.Equal(t, http.StatusOK, w.Code, w.Body)
   275  		assert.Equal(t, "text/plain", w.Header().Get("Content-Type"), "unexpected content type")
   276  	})
   277  
   278  	t.Run("repo error", func(t *testing.T) {
   279  		repo.err = errors.New("bad error")
   280  
   281  		w := httptest.NewRecorder()
   282  		router.ServeHTTP(w, req)
   283  		w.Flush()
   284  
   285  		require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
   286  	})
   287  
   288  }
   289  
   290  func TestFiles_validateFile(t *testing.T) {
   291  	req := httptest.NewRequest("GET", "/files/foo/validate", nil)
   292  	repo := &testICLFileRepository{}
   293  	router := mux.NewRouter()
   294  	AppendRoutes(log.NewNopLogger(), router, repo)
   295  	f := readFile(t, "BNK20180905121042882-A.icl")
   296  
   297  	t.Run("file not found", func(t *testing.T) {
   298  		w := httptest.NewRecorder()
   299  		router.ServeHTTP(w, req)
   300  		w.Flush()
   301  
   302  		require.Equal(t, http.StatusNotFound, w.Code, w.Body)
   303  	})
   304  
   305  	t.Run("valid file", func(t *testing.T) {
   306  		w := httptest.NewRecorder()
   307  		repo.file = f
   308  		router.ServeHTTP(w, req)
   309  		w.Flush()
   310  
   311  		require.Equal(t, http.StatusOK, w.Code, w.Body)
   312  		assert.Contains(t, w.Body.String(), `"{\"error\": null}"`)
   313  	})
   314  
   315  	t.Run("invalid file", func(t *testing.T) {
   316  		w := httptest.NewRecorder()
   317  		// make the file invalid
   318  		repo.file.Header = imagecashletter.NewFileHeader()
   319  		router.ServeHTTP(w, req)
   320  		w.Flush()
   321  
   322  		require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
   323  	})
   324  
   325  	t.Run("repo error", func(t *testing.T) {
   326  		w := httptest.NewRecorder()
   327  		repo.err = errors.New("bad error")
   328  		router.ServeHTTP(w, req)
   329  		w.Flush()
   330  
   331  		require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
   332  	})
   333  }
   334  
   335  func TestFiles_addCashLetterToFile(t *testing.T) {
   336  	repo := &testICLFileRepository{}
   337  	router := mux.NewRouter()
   338  	AppendRoutes(log.NewNopLogger(), router, repo)
   339  	f := readFile(t, "BNK20180905121042882-A.icl")
   340  	cashLetter := f.CashLetters[0]
   341  	f.CashLetters = nil
   342  
   343  	t.Run("file not found", func(t *testing.T) {
   344  		var buf bytes.Buffer
   345  		require.NoError(t, json.NewEncoder(&buf).Encode(cashLetter))
   346  		req := httptest.NewRequest("POST", "/files/foo/cashLetters", &buf)
   347  		w := httptest.NewRecorder()
   348  		router.ServeHTTP(w, req)
   349  		w.Flush()
   350  
   351  		require.Equal(t, http.StatusNotFound, w.Code, w.Body)
   352  	})
   353  
   354  	t.Run("successful request", func(t *testing.T) {
   355  		var buf bytes.Buffer
   356  		require.NoError(t, json.NewEncoder(&buf).Encode(cashLetter))
   357  		req := httptest.NewRequest("POST", "/files/foo/cashLetters", &buf)
   358  		w := httptest.NewRecorder()
   359  		repo.file = f
   360  		router.ServeHTTP(w, req)
   361  		w.Flush()
   362  
   363  		require.Equal(t, http.StatusOK, w.Code, w.Body)
   364  		var out imagecashletter.File
   365  		require.NoError(t, json.NewDecoder(w.Body).Decode(&out))
   366  		assert.Len(t, out.CashLetters, 1, "expected one cashLetter")
   367  	})
   368  
   369  	t.Run("repo error", func(t *testing.T) {
   370  		var buf bytes.Buffer
   371  		require.NoError(t, json.NewEncoder(&buf).Encode(cashLetter))
   372  		req := httptest.NewRequest("POST", "/files/foo/cashLetters", &buf)
   373  		w := httptest.NewRecorder()
   374  		repo.file = nil
   375  		repo.err = errors.New("bad error")
   376  		router.ServeHTTP(w, req)
   377  		w.Flush()
   378  
   379  		require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
   380  	})
   381  }
   382  
   383  func TestFiles_removeCashLetterFromFile(t *testing.T) {
   384  	repo := &testICLFileRepository{}
   385  	router := mux.NewRouter()
   386  	AppendRoutes(log.NewNopLogger(), router, repo)
   387  	f := readFile(t, "BNK20180905121042882-A.icl")
   388  	cashLetterId := base.ID()
   389  	f.CashLetters[0].ID = cashLetterId
   390  	req := httptest.NewRequest("DELETE", fmt.Sprintf("/files/foo/cashLetters/%s", cashLetterId), nil)
   391  
   392  	t.Run("file not found", func(t *testing.T) {
   393  		w := httptest.NewRecorder()
   394  		router.ServeHTTP(w, req)
   395  		w.Flush()
   396  
   397  		require.Equal(t, http.StatusNotFound, w.Code, w.Body)
   398  	})
   399  
   400  	t.Run("successful request", func(t *testing.T) {
   401  		w := httptest.NewRecorder()
   402  		repo.file = f
   403  		router.ServeHTTP(w, req)
   404  		w.Flush()
   405  
   406  		require.Equal(t, http.StatusOK, w.Code, w.Body)
   407  	})
   408  
   409  	t.Run("repo error", func(t *testing.T) {
   410  		w := httptest.NewRecorder()
   411  		repo.file = nil
   412  		repo.err = errors.New("bad error")
   413  		router.ServeHTTP(w, req)
   414  		w.Flush()
   415  
   416  		require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
   417  	})
   418  }
   419  
   420  func TestFiles_createFile_Issue228(t *testing.T) {
   421  	repo := &testICLFileRepository{}
   422  	router := mux.NewRouter()
   423  	AppendRoutes(log.NewNopLogger(), router, repo)
   424  
   425  	w := httptest.NewRecorder()
   426  	fd, _ := os.Open(filepath.Join("..", "..", "test", "testdata", "issue228.json"))
   427  	req := httptest.NewRequest("POST", "/files/create", fd)
   428  	req.Header.Set("Content-Type", "application/json")
   429  
   430  	router.ServeHTTP(w, req)
   431  
   432  	require.Equal(t, http.StatusBadRequest, w.Code, w.Body)
   433  	type apiError struct {
   434  		Error string `json:"error"`
   435  	}
   436  	var wantErr apiError
   437  	require.NoError(t, json.Unmarshal(w.Body.Bytes(), &wantErr))
   438  	require.Contains(t, wantErr.Error, "CashLetterControl record is mandatory")
   439  }
   440  
   441  func readFile(t *testing.T, filename string) *imagecashletter.File {
   442  	t.Helper()
   443  
   444  	fd, err := os.Open(filepath.Join("..", "..", "test", "testdata", filename))
   445  	require.NoError(t, err)
   446  	f, err := imagecashletter.NewReader(fd, imagecashletter.ReadVariableLineLengthOption()).Read()
   447  	require.NoError(t, err)
   448  	return &f
   449  }
   450  
   451  type testICLFileRepository struct {
   452  	err error
   453  
   454  	file *imagecashletter.File
   455  }
   456  
   457  func (r *testICLFileRepository) GetFiles() ([]*imagecashletter.File, error) {
   458  	if r.err != nil {
   459  		return nil, r.err
   460  	}
   461  	return []*imagecashletter.File{r.file}, nil
   462  }
   463  
   464  func (r *testICLFileRepository) GetFile(fileId string) (*imagecashletter.File, error) {
   465  	if r.err != nil {
   466  		return nil, r.err
   467  	}
   468  	return r.file, nil
   469  }
   470  
   471  func (r *testICLFileRepository) SaveFile(file *imagecashletter.File) error {
   472  	if r.err == nil { // only persist if we're not error'ing
   473  		r.file = file
   474  	}
   475  	return r.err
   476  }
   477  
   478  func (r *testICLFileRepository) DeleteFile(fileId string) error {
   479  	return r.err
   480  }