Implement auth domain with outside-in TDD (22 tests)
Controller (7 tests): signup, login, logout, GET/PATCH me. Standalone MockMvc setup (Boot 4 removed @WebMvcTest). Service (11 tests): signup with conflict check, login with password/active validation, getCurrentUser with household info, updateProfile with password change flow. Repository (4 tests): save/find, case-insensitive email via IgnoreCase queries (citext + Hibernate needs explicit IgnoreCase), existsByEmail. Also includes: - SecurityConfig: session auth, CSRF, role-based authorization - CustomUserDetailsService: loads UserAccount for Spring Security - UserAccount, Household, HouseholdMember JPA entities - spring-boot-flyway dependency (Boot 4 requires explicit module) - ddl-auto=none (Flyway owns schema, validate fails on citext) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
65
backend/src/main/java/com/recipeapp/auth/AuthController.java
Normal file
65
backend/src/main/java/com/recipeapp/auth/AuthController.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package com.recipeapp.auth;
|
||||
|
||||
import com.recipeapp.auth.dto.*;
|
||||
import com.recipeapp.common.ApiResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/v1/auth")
|
||||
public class AuthController {
|
||||
|
||||
private final AuthService authService;
|
||||
|
||||
public AuthController(AuthService authService) {
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@PostMapping("/signup")
|
||||
public ResponseEntity<ApiResponse<UserResponse>> signup(
|
||||
@Valid @RequestBody SignupRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
UserResponse user = authService.signup(request);
|
||||
httpRequest.getSession(true);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(ApiResponse.success(user));
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<ApiResponse<UserResponse>> login(
|
||||
@Valid @RequestBody LoginRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
UserResponse user = authService.login(request);
|
||||
HttpSession session = httpRequest.getSession(true);
|
||||
session.setAttribute("user_email", user.email());
|
||||
return ResponseEntity.ok(ApiResponse.success(user));
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<Void> logout(HttpServletRequest httpRequest) {
|
||||
HttpSession session = httpRequest.getSession(false);
|
||||
if (session != null) {
|
||||
session.invalidate();
|
||||
}
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@GetMapping("/me")
|
||||
public ResponseEntity<ApiResponse<UserResponse>> me(Principal principal) {
|
||||
UserResponse user = authService.getCurrentUser(principal.getName());
|
||||
return ResponseEntity.ok(ApiResponse.success(user));
|
||||
}
|
||||
|
||||
@PatchMapping("/me")
|
||||
public ResponseEntity<ApiResponse<UserResponse>> updateProfile(
|
||||
Principal principal,
|
||||
@Valid @RequestBody UpdateProfileRequest request) {
|
||||
UserResponse user = authService.updateProfile(principal.getName(), request);
|
||||
return ResponseEntity.ok(ApiResponse.success(user));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user