github.com/isavita/tictactoe_api@v0.0.0-20230610152111-9a1d2166c39d/cmd/server/main_test.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"os"
     9  	"reflect"
    10  	"strings"
    11  	"sync"
    12  	"testing"
    13  
    14  	"github.com/isavita/tictactoe_api/internal/api"
    15  	"github.com/isavita/tictactoe_api/internal/game"
    16  	"github.com/isavita/tictactoe_api/internal/model"
    17  )
    18  
    19  func TestMain(m *testing.M) {
    20  	// path setup to the root directory
    21  	// we need this because the main file is in `/cmd/server`
    22  	prevPath, _ := os.Getwd()
    23  	os.Chdir("../..")
    24  	defer os.Chdir(prevPath)
    25  
    26  	m.Run()
    27  }
    28  
    29  func TestTicTacToeHandler(t *testing.T) {
    30  	t.Run("first move with empty payload", func(t *testing.T) {
    31  		// setup the tic-tac-toe api
    32  		ticTacToeGame := game.NewTicTacToeGame()
    33  		ticTacToeAPI := api.NewTicTacToeAPI(ticTacToeGame)
    34  
    35  		s := httptest.NewServer(http.HandlerFunc(ticTacToeAPI.TicTacToeHandler))
    36  		url := s.URL + "/v1/tictactoe"
    37  		payload := strings.NewReader(`{}`)
    38  		req, err := http.NewRequest(http.MethodPost, url, payload)
    39  		assertNoError(t, err)
    40  
    41  		resp, err := http.DefaultClient.Do(req)
    42  		assertNoError(t, err)
    43  
    44  		defer resp.Body.Close()
    45  		body, err := io.ReadAll(resp.Body)
    46  		assertNoError(t, err)
    47  		assertStatusCode(t, resp, http.StatusOK)
    48  
    49  		got := model.MoveResponse{}
    50  		err = json.Unmarshal(body, &got)
    51  		assertNoError(t, err)
    52  
    53  		want := model.MoveResponse{
    54  			Success:      true,
    55  			Message:      "Player 1 has placed 'X' in position 1. Please note: If the user is playing with 'X', disregard this move. Instead, ask the user where they would like to place their first move, then present the game board reflecting their choice.",
    56  			Board:        []int{1, 0, 0, 0, 0, 0, 0, 0, 0},
    57  			BoardSize:    3,
    58  			BoardDisplay: " X | 2 | 3 \n --------- \n 4 | 5 | 6 \n --------- \n 7 | 8 | 9 ",
    59  			GameStatus:   "ongoing",
    60  			NextPlayer:   game.OPlayer,
    61  		}
    62  
    63  		if !reflect.DeepEqual(got, want) {
    64  			t.Errorf("got %v want %v", got, want)
    65  		}
    66  	})
    67  
    68  	t.Run("handles concurrent requests", func(t *testing.T) {
    69  		s := newTestServer()
    70  		url := s.URL + "/v1/tictactoe"
    71  		wg := &sync.WaitGroup{}
    72  
    73  		for i := 0; i < 10; i++ {
    74  			wg.Add(1)
    75  			go func(w *sync.WaitGroup) {
    76  				defer w.Done()
    77  				payload := strings.NewReader(`{}`)
    78  				req, _ := http.NewRequest(http.MethodPost, url, payload)
    79  				_, err := http.DefaultClient.Do(req)
    80  				assertNoError(t, err)
    81  			}(wg)
    82  		}
    83  		wg.Wait()
    84  	})
    85  
    86  }
    87  
    88  func TestOpenAIPluginHandler(t *testing.T) {
    89  	t.Parallel()
    90  
    91  	req := httptest.NewRequest(http.MethodGet, "/.well-known/ai-plugin.json", nil)
    92  	recorder := httptest.NewRecorder()
    93  	openAIPluginHandler(recorder, req)
    94  
    95  	res := recorder.Result()
    96  	defer res.Body.Close()
    97  
    98  	assertStatusCode(t, res, http.StatusOK)
    99  }
   100  
   101  func TestOpenapiHandler(t *testing.T) {
   102  	t.Parallel()
   103  
   104  	req := httptest.NewRequest(http.MethodGet, "/openapi.yaml", nil)
   105  	recorder := httptest.NewRecorder()
   106  	openapiHandler(recorder, req)
   107  
   108  	res := recorder.Result()
   109  	defer res.Body.Close()
   110  
   111  	assertStatusCode(t, res, http.StatusOK)
   112  }
   113  
   114  func TestLogoHandler(t *testing.T) {
   115  	t.Parallel()
   116  
   117  	req := httptest.NewRequest(http.MethodGet, "/logo.png", nil)
   118  	recorder := httptest.NewRecorder()
   119  	openapiHandler(recorder, req)
   120  	res := recorder.Result()
   121  	defer res.Body.Close()
   122  
   123  	assertStatusCode(t, res, http.StatusOK)
   124  }
   125  
   126  func BenchmarkTicTacToeHandler(b *testing.B) {
   127  	s := newTestServer()
   128  	defer s.Close()
   129  
   130  	url := s.URL + "/v1/tictactoe"
   131  	for i := 0; i < b.N; i++ {
   132  		payload := strings.NewReader(`{}`)
   133  		req, _ := http.NewRequest(http.MethodPost, url, payload)
   134  		_, err := http.DefaultClient.Do(req)
   135  		assertNoError(b, err)
   136  	}
   137  }
   138  
   139  func BenchmarkOpenAIPluginHandler(b *testing.B) {
   140  	s := newTestServer()
   141  	defer s.Close()
   142  
   143  	url := s.URL + "/.well-known/ai-plugin.json"
   144  	for i := 0; i < b.N; i++ {
   145  		req, _ := http.NewRequest(http.MethodGet, url, nil)
   146  		_, err := http.DefaultClient.Do(req)
   147  		assertNoError(b, err)
   148  	}
   149  }
   150  
   151  func BenchmarkOpenapiHandler(b *testing.B) {
   152  	s := newTestServer()
   153  	defer s.Close()
   154  
   155  	url := s.URL + "/openapi.yaml"
   156  	for i := 0; i < b.N; i++ {
   157  		req, _ := http.NewRequest(http.MethodGet, url, nil)
   158  		_, err := http.DefaultClient.Do(req)
   159  		assertNoError(b, err)
   160  	}
   161  }
   162  
   163  func BenchmarkLogoHandler(b *testing.B) {
   164  	s := newTestServer()
   165  	defer s.Close()
   166  
   167  	url := s.URL + "/logo.png"
   168  	for i := 0; i < b.N; i++ {
   169  		req, _ := http.NewRequest(http.MethodGet, url, nil)
   170  		_, err := http.DefaultClient.Do(req)
   171  		assertNoError(b, err)
   172  	}
   173  }
   174  
   175  func assertStatusCode(t testing.TB, res *http.Response, want int) {
   176  	if res.StatusCode != want {
   177  		t.Errorf("got %v want %v", res.StatusCode, want)
   178  	}
   179  }
   180  
   181  func assertNoError(t testing.TB, err error) {
   182  	t.Helper()
   183  	if err != nil {
   184  		t.Errorf("got error %v when no error was expected", err)
   185  	}
   186  }
   187  
   188  func newTestServer() *httptest.Server {
   189  	ticTacToeGame := game.NewTicTacToeGame()
   190  	ticTacToeAPI := api.NewTicTacToeAPI(ticTacToeGame)
   191  	return httptest.NewServer(http.HandlerFunc(ticTacToeAPI.TicTacToeHandler))
   192  }