github.com/jayanpraveen/tildly@v0.0.0-20221205145005-1db0c7d7e885/handler/url_handler_test.go (about)

     1  package handler
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"os"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  	m "github.com/jayanpraveen/tildly/entity"
    13  )
    14  
    15  type HandleTester func(method string, url string, body string) *httptest.ResponseRecorder
    16  
    17  func generateHandleTester(t *testing.T, handleFunc http.Handler, expSC int) HandleTester {
    18  	return func(method string, url string, body string) *httptest.ResponseRecorder {
    19  
    20  		if url == "" {
    21  			url = "/"
    22  		}
    23  
    24  		if method == "" {
    25  			method = http.MethodGet
    26  		}
    27  
    28  		res := httptest.NewRecorder()
    29  		req := httptest.NewRequest(method, url, strings.NewReader(body))
    30  
    31  		// Calling the given handler
    32  		handleFunc.ServeHTTP(res, req)
    33  
    34  		// Check http status
    35  		assertEquals(t, expSC, res.Code)
    36  
    37  		return res
    38  	}
    39  }
    40  
    41  /*
    42   * This functions follows the convention similar to JUnit's `assertEquals` method.
    43   * The exp or expected is the value the user expects from the program (the known one).
    44   * The act or actual is the value the program produces (the unknown one).
    45   */
    46  func assertEquals(t *testing.T, exp interface{}, act interface{}) {
    47  	if !cmp.Equal(exp, act) {
    48  		t.Errorf(" Expected: %v, Actual: %v string", exp, act)
    49  	}
    50  }
    51  
    52  type MockService struct {
    53  	SaveUrlFunc      func(longUrl string, expireAt int64) (string, error)
    54  	GetUrlByHashFunc func(hash string) (*m.Url, error)
    55  }
    56  
    57  func (m *MockService) SaveUrl(longUrl string, exipreAt int64) (string, error) {
    58  	return m.SaveUrlFunc(longUrl, 1257894000)
    59  }
    60  
    61  func (m *MockService) GetUrlByHash(hash string) (*m.Url, error) {
    62  	return m.GetUrlByHashFunc(hash)
    63  }
    64  
    65  func Test_handleIndex(t *testing.T) {
    66  	res := httptest.NewRecorder()
    67  	req := httptest.NewRequest(http.MethodGet, "/", nil)
    68  	uh := UrlHandler{}
    69  	h := uh.handleIndex()
    70  	h(res, req)
    71  
    72  	// Check http status
    73  	assertEquals(t, http.StatusOK, res.Code)
    74  
    75  	// Check response output
    76  	assertEquals(t, []byte("tildly !\n"), res.Body.Bytes())
    77  }
    78  
    79  func Test_handleLongUrl(t *testing.T) {
    80  
    81  	t.Run("post valid url", func(t *testing.T) {
    82  		srv := &MockService{
    83  			SaveUrlFunc: func(longUrl string, exipreAt int64) (string, error) {
    84  				return "UIOP", nil
    85  			},
    86  		}
    87  		uh := NewUrlHandler(srv)
    88  		h := uh.handleLongUrl()
    89  
    90  		expSC := http.StatusCreated
    91  		expBody := "short-url: UIOP"
    92  
    93  		method := http.MethodPost
    94  		url := "/api/longUrl"
    95  		body := `{"longUrl": "http://go.dev/docs"}`
    96  
    97  		gh := generateHandleTester(t, h, expSC)
    98  		res := gh(method, url, body)
    99  
   100  		// Check response output
   101  		assertEquals(t, expBody, res.Body.String())
   102  
   103  	})
   104  
   105  	t.Run("post invalid url", func(t *testing.T) {
   106  		srv := &MockService{
   107  			SaveUrlFunc: func(longUrl string, exipreAt int64) (string, error) {
   108  				return "", fmt.Errorf("")
   109  			},
   110  		}
   111  		uh := NewUrlHandler(srv)
   112  		h := uh.handleLongUrl()
   113  
   114  		expSC := http.StatusBadRequest
   115  		expBody := fmt.Sprintln(`{"status":400,"msg":"Not a valid URL"}`) // Using `ln` becuase Json.NewDecoder adds \n at eof while marshlling
   116  
   117  		method := http.MethodPost
   118  		url := "/api/longUrl"
   119  		body := `{"longUrl": "htt/go.dev/docs"}`
   120  
   121  		gh := generateHandleTester(t, h, expSC)
   122  		res := gh(method, url, body)
   123  
   124  		// Check response output
   125  		assertEquals(t, expBody, res.Body.String())
   126  
   127  		// when given empty url
   128  		body = `{"longUrl": ""}`
   129  		res = gh(method, url, body)
   130  		assertEquals(t, expBody, res.Body.String())
   131  
   132  	})
   133  
   134  	t.Run("post invalid JSON", func(t *testing.T) {
   135  		srv := &MockService{
   136  			SaveUrlFunc: func(longUrl string, exipreAt int64) (string, error) {
   137  				return "", nil
   138  			},
   139  		}
   140  		uh := NewUrlHandler(srv)
   141  		h := uh.handleLongUrl()
   142  
   143  		expSC := http.StatusBadRequest
   144  		expBody := fmt.Sprintln(`{"status":400,"msg":"Not a proper JSON format"}`)
   145  
   146  		method := http.MethodPost
   147  		url := "/api/longUrl"
   148  		body := `{"longUrl" "htt/go.dev/docs"}` // Invalid JSON: missing ':' in longUrl
   149  
   150  		gh := generateHandleTester(t, h, expSC)
   151  		res := gh(method, url, body)
   152  
   153  		// Check response output
   154  		assertEquals(t, expBody, res.Body.String())
   155  	})
   156  
   157  	t.Run("returns interal server error", func(t *testing.T) {
   158  		srv := &MockService{
   159  			SaveUrlFunc: func(longUrl string, exipreAt int64) (string, error) {
   160  				return "", fmt.Errorf("Failed to save url")
   161  			},
   162  		}
   163  
   164  		uh := NewUrlHandler(srv)
   165  		h := uh.handleLongUrl()
   166  
   167  		expSC := http.StatusInternalServerError
   168  		expBody := fmt.Sprintln(`{"status":500,"msg":"Internal server error"}`)
   169  
   170  		method := http.MethodPost
   171  		url := "/api/longUrl"
   172  		body := `{"longUrl": "http://go.dev/docs"}`
   173  
   174  		gh := generateHandleTester(t, h, expSC)
   175  		res := gh(method, url, body)
   176  
   177  		// Check response output
   178  		assertEquals(t, expBody, res.Body.String())
   179  	})
   180  
   181  }
   182  
   183  func Test_handleShortUrl(t *testing.T) {
   184  
   185  	t.Run("short url exist", func(t *testing.T) {
   186  		srv := &MockService{
   187  			GetUrlByHashFunc: func(hash string) (*m.Url, error) {
   188  				return &m.Url{
   189  					Hash:      "XikHsqW",
   190  					LongUrl:   "https://go.dev",
   191  					CreatedAt: 1257894000,
   192  				}, nil
   193  			},
   194  		}
   195  
   196  		uh := NewUrlHandler(srv)
   197  		h := uh.handleShortUrl()
   198  
   199  		expSC := http.StatusPermanentRedirect
   200  
   201  		method := http.MethodGet
   202  		url := "/XikHsqW"
   203  		body := ""
   204  
   205  		gh := generateHandleTester(t, h, expSC)
   206  		gh(method, url, body)
   207  
   208  	})
   209  
   210  	t.Run("short url doesn't exist", func(t *testing.T) {
   211  		srv := &MockService{
   212  			GetUrlByHashFunc: func(hash string) (*m.Url, error) {
   213  				return nil, fmt.Errorf("short url not found")
   214  			},
   215  		}
   216  
   217  		uh := NewUrlHandler(srv)
   218  		h := uh.handleShortUrl()
   219  
   220  		expSC := http.StatusNotFound
   221  
   222  		method := http.MethodGet
   223  		url := "/XikHsqW"
   224  		body := ""
   225  
   226  		gh := generateHandleTester(t, h, expSC)
   227  		gh(method, url, body)
   228  	})
   229  }
   230  
   231  // test fixture
   232  func init() {
   233  	if err := os.Chdir("./testdata"); err != nil {
   234  		panic(err)
   235  	}
   236  }