github.com/soulteary/pocket-bookcase@v0.0.0-20240428065142-0b5a9a0fc98a/internal/http/routes/api/v1/auth_test.go (about) 1 package api_v1 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "net/http" 8 "net/http/httptest" 9 "testing" 10 "time" 11 12 "github.com/sirupsen/logrus" 13 "github.com/soulteary/pocket-bookcase/internal/http/middleware" 14 "github.com/soulteary/pocket-bookcase/internal/model" 15 "github.com/soulteary/pocket-bookcase/internal/testutil" 16 "github.com/stretchr/testify/require" 17 ) 18 19 func noopLegacyLoginHandler(_ model.Account, _ time.Duration) (string, error) { 20 return "", nil 21 } 22 23 func TestAccountsRoute(t *testing.T) { 24 logger := logrus.New() 25 ctx := context.TODO() 26 27 t.Run("login invalid", func(t *testing.T) { 28 g := testutil.NewGin() 29 _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) 30 router := NewAuthAPIRoutes(logger, deps, noopLegacyLoginHandler) 31 router.Setup(g.Group("/")) 32 w := httptest.NewRecorder() 33 body := []byte(`{"username": "gopher"}`) 34 req := httptest.NewRequest("POST", "/login", bytes.NewBuffer(body)) 35 g.ServeHTTP(w, req) 36 37 require.Equal(t, 400, w.Code) 38 }) 39 40 t.Run("login incorrect", func(t *testing.T) { 41 g := testutil.NewGin() 42 _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) 43 router := NewAuthAPIRoutes(logger, deps, noopLegacyLoginHandler) 44 router.Setup(g.Group("/")) 45 w := httptest.NewRecorder() 46 body := []byte(`{"username": "gopher", "password": "shiori"}`) 47 req := httptest.NewRequest("POST", "/login", bytes.NewBuffer(body)) 48 g.ServeHTTP(w, req) 49 50 require.Equal(t, 400, w.Code) 51 }) 52 53 t.Run("login correct", func(t *testing.T) { 54 g := testutil.NewGin() 55 _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) 56 router := NewAuthAPIRoutes(logger, deps, noopLegacyLoginHandler) 57 router.Setup(g.Group("/")) 58 59 // Create an account manually to test 60 account := model.Account{ 61 Username: "shiori", 62 Password: "gopher", 63 Owner: true, 64 } 65 require.NoError(t, deps.Database.SaveAccount(ctx, account)) 66 67 w := httptest.NewRecorder() 68 body := []byte(`{"username": "shiori", "password": "gopher"}`) 69 req := httptest.NewRequest("POST", "/login", bytes.NewBuffer(body)) 70 g.ServeHTTP(w, req) 71 72 require.Equal(t, 200, w.Code) 73 }) 74 75 t.Run("check /me (correct token)", func(t *testing.T) { 76 _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) 77 78 g := testutil.NewGin() 79 g.Use(middleware.AuthMiddleware(deps)) 80 81 router := NewAuthAPIRoutes(logger, deps, noopLegacyLoginHandler) 82 router.Setup(g.Group("/")) 83 84 // Create an account manually to test 85 account := model.Account{ 86 Username: "shiori", 87 Password: "gopher", 88 Owner: true, 89 } 90 require.NoError(t, deps.Database.SaveAccount(ctx, account)) 91 92 token, err := deps.Domains.Auth.CreateTokenForAccount(&account, time.Now().Add(time.Minute)) 93 require.NoError(t, err) 94 95 req := httptest.NewRequest("GET", "/me", nil) 96 req.Header.Add("Authorization", "Bearer "+token) 97 w := httptest.NewRecorder() 98 g.ServeHTTP(w, req) 99 100 require.Equal(t, 200, w.Code) 101 }) 102 103 t.Run("check /me (incorrect token)", func(t *testing.T) { 104 _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) 105 106 g := testutil.NewGin() 107 g.Use(middleware.AuthMiddleware(deps)) 108 109 router := NewAuthAPIRoutes(logger, deps, noopLegacyLoginHandler) 110 router.Setup(g.Group("/")) 111 112 req := httptest.NewRequest("GET", "/me", nil) 113 w := httptest.NewRecorder() 114 g.ServeHTTP(w, req) 115 116 require.Equal(t, 403, w.Code) 117 }) 118 } 119 120 func TestLoginRequestPayload(t *testing.T) { 121 // Test empty payload 122 t.Run("test empty payload", func(t *testing.T) { 123 payload := loginRequestPayload{} 124 err := payload.IsValid() 125 require.Error(t, err) 126 }) 127 128 // Test empty username 129 t.Run("test empty username", func(t *testing.T) { 130 payload := loginRequestPayload{ 131 Password: "gopher", 132 } 133 err := payload.IsValid() 134 require.Error(t, err) 135 }) 136 137 // Test empty password 138 t.Run("test empty password", func(t *testing.T) { 139 payload := loginRequestPayload{ 140 Username: "shiori", 141 } 142 err := payload.IsValid() 143 require.Error(t, err) 144 }) 145 146 // Test valid payload 147 t.Run("test valid payload", func(t *testing.T) { 148 payload := loginRequestPayload{ 149 Username: "shiori", 150 Password: "gopher", 151 } 152 err := payload.IsValid() 153 require.NoError(t, err) 154 }) 155 } 156 157 func TestRefreshHandler(t *testing.T) { 158 logger := logrus.New() 159 ctx := context.TODO() 160 g := testutil.NewGin() 161 162 _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) 163 router := NewAuthAPIRoutes(logger, deps, noopLegacyLoginHandler) 164 g.Use(middleware.AuthMiddleware(deps)) // Requires AuthMiddleware to manipulate context 165 router.Setup(g.Group("/")) 166 167 t.Run("empty headers", func(t *testing.T) { 168 w := testutil.PerformRequest(g, "POST", "/refresh") 169 require.Equal(t, http.StatusForbidden, w.Code) 170 }) 171 172 t.Run("token invalid", func(t *testing.T) { 173 w := testutil.PerformRequest(g, "POST", "/refresh") 174 require.Equal(t, http.StatusForbidden, w.Code) 175 }) 176 177 t.Run("token valid", func(t *testing.T) { 178 token, err := deps.Domains.Auth.CreateTokenForAccount(&model.Account{ 179 Username: "shiori", 180 }, time.Now().Add(time.Minute)) 181 require.NoError(t, err) 182 183 w := testutil.PerformRequest(g, "POST", "/refresh", testutil.WithHeader(model.AuthorizationHeader, model.AuthorizationTokenType+" "+token)) 184 185 require.Equal(t, http.StatusAccepted, w.Code) 186 }) 187 } 188 189 func TestSettingsHandler(t *testing.T) { 190 logger := logrus.New() 191 ctx := context.TODO() 192 g := testutil.NewGin() 193 194 _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) 195 router := NewAuthAPIRoutes(logger, deps, noopLegacyLoginHandler) 196 g.Use(middleware.AuthMiddleware(deps)) 197 router.Setup(g.Group("/")) 198 199 t.Run("token valid", func(t *testing.T) { 200 token, err := deps.Domains.Auth.CreateTokenForAccount(&model.Account{ 201 Username: "shiori", 202 }, time.Now().Add(time.Minute)) 203 require.NoError(t, err) 204 205 type settingRequestPayload struct { 206 Config model.UserConfig `json:"config"` 207 } 208 payload := settingRequestPayload{ 209 Config: model.UserConfig{ 210 // add your configuration data here 211 }, 212 } 213 payloadJSON, err := json.Marshal(payload) 214 if err != nil { 215 logrus.Printf("problem") 216 } 217 218 w := testutil.PerformRequest(g, "PATCH", "/account", testutil.WithBody(string(payloadJSON)), testutil.WithHeader(model.AuthorizationHeader, model.AuthorizationTokenType+" "+token)) 219 220 require.Equal(t, http.StatusOK, w.Code) 221 222 }) 223 224 t.Run("config not valid", func(t *testing.T) { 225 token, err := deps.Domains.Auth.CreateTokenForAccount(&model.Account{ 226 Username: "shiori", 227 }, time.Now().Add(time.Minute)) 228 require.NoError(t, err) 229 230 w := testutil.PerformRequest(g, "PATCH", "/account", testutil.WithBody("notValidConfig"), testutil.WithHeader(model.AuthorizationHeader, model.AuthorizationTokenType+" "+token)) 231 232 require.Equal(t, http.StatusInternalServerError, w.Code) 233 234 }) 235 t.Run("Test configure change in database", func(t *testing.T) { 236 // Create a tmp database 237 g := testutil.NewGin() 238 _, deps := testutil.GetTestConfigurationAndDependencies(t, ctx, logger) 239 router := NewAuthAPIRoutes(logger, deps, noopLegacyLoginHandler) 240 g.Use(middleware.AuthMiddleware(deps)) 241 router.Setup(g.Group("/")) 242 243 // Create an account manually to test 244 account := model.Account{ 245 Username: "shiori", 246 Password: "gopher", 247 Owner: true, 248 Config: model.UserConfig{ 249 ShowId: true, 250 ListMode: true, 251 HideThumbnail: true, 252 HideExcerpt: true, 253 NightMode: true, 254 KeepMetadata: true, 255 UseArchive: true, 256 CreateEbook: true, 257 MakePublic: true, 258 }, 259 } 260 require.NoError(t, deps.Database.SaveAccount(ctx, account)) 261 262 // Get current user config 263 user, _, err := deps.Database.GetAccount(ctx, "shiori") 264 require.NoError(t, err) 265 require.Equal(t, user.Config, account.Config) 266 267 // Send Request to update config for user 268 token, err := deps.Domains.Auth.CreateTokenForAccount(&user, time.Now().Add(time.Minute)) 269 require.NoError(t, err) 270 271 payloadJSON := []byte(`{ 272 "config": { 273 "ShowId": false, 274 "ListMode": false, 275 "HideThumbnail": false, 276 "HideExcerpt": false, 277 "NightMode": false, 278 "KeepMetadata": false, 279 "UseArchive": false, 280 "CreateEbook": false, 281 "MakePublic": false 282 } 283 }`) 284 285 w := httptest.NewRecorder() 286 req := httptest.NewRequest(http.MethodPatch, "/account", bytes.NewBuffer(payloadJSON)) 287 req.Header.Set("Content-Type", "application/json") 288 req.Header.Add("Authorization", "Bearer "+token) 289 g.ServeHTTP(w, req) 290 291 require.Equal(t, 200, w.Code) 292 user, _, err = deps.Database.GetAccount(ctx, "shiori") 293 294 require.NoError(t, err) 295 require.NotEqual(t, user.Config, account.Config) 296 297 }) 298 }