From 35c930b7dccbdd3aee311fe38c8386d19cf44a07 Mon Sep 17 00:00:00 2001 From: Steven Date: Sat, 25 Oct 2025 09:55:18 -0500 Subject: [PATCH 1/3] Enhance authentication middleware by adding AuthFetchAuthSessionServer function This commit updates the authentication middleware to include the AuthFetchAuthSessionServer function, improving session management. The logic now checks for both the current session and user, ensuring better handling of user authentication states and cache management. --- middlewares/auth/auth.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/middlewares/auth/auth.ts b/middlewares/auth/auth.ts index 7a1979c6..c65be36e 100644 --- a/middlewares/auth/auth.ts +++ b/middlewares/auth/auth.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { AuthGetCurrentUserServer, AuthFetchUserAttributesServer } from '@/utils/client/AmplifyUtils'; +import { + AuthGetCurrentUserServer, + AuthFetchUserAttributesServer, + AuthFetchAuthSessionServer, +} from '@/utils/client/AmplifyUtils'; import { getLastVisitedStore } from '@/lib/cookies/last-store'; import { NextRequest, NextResponse } from 'next/server'; import NodeCache from 'node-cache'; @@ -131,10 +135,11 @@ export async function getSession(request: NextRequest, _response: NextResponse) } try { + const currentSession = await AuthFetchAuthSessionServer(); const currentUser = await AuthGetCurrentUserServer(); // Si no hay usuario, limpiar caché - if (!currentUser) { + if (!currentSession || !currentUser) { sessionCache.del(cacheKey); userAttributesCache.del(cacheKey); return null; From 7d246c9defff3a14602652380371ff6d8219d468 Mon Sep 17 00:00:00 2001 From: Steven Date: Sat, 25 Oct 2025 10:07:14 -0500 Subject: [PATCH 2/3] Enhance authentication tests by integrating AuthFetchAuthSessionServer This commit updates the authentication integration and unit tests to include the AuthFetchAuthSessionServer function. The tests now verify that the new session management logic correctly handles user authentication states and ensures that the AuthFetchAuthSessionServer is called as expected, improving overall test coverage and reliability. --- test/unit/integration/auth-flow.test.ts | 24 ++++++++++++++++++-- test/unit/middlewares/auth-cache.test.ts | 23 ++++++++++++++++++- test/unit/middlewares/auth.test.ts | 29 +++++++++++++++++++++++- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/test/unit/integration/auth-flow.test.ts b/test/unit/integration/auth-flow.test.ts index a91d8dda..14bf0b36 100644 --- a/test/unit/integration/auth-flow.test.ts +++ b/test/unit/integration/auth-flow.test.ts @@ -1,10 +1,15 @@ import { NextRequest, NextResponse } from 'next/server'; import { getSession, type AuthSession, clearAllSessionCache } from '@/middlewares/auth/auth'; -import { AuthGetCurrentUserServer, AuthFetchUserAttributesServer } from '@/utils/client/AmplifyUtils'; +import { + AuthGetCurrentUserServer, + AuthFetchUserAttributesServer, + AuthFetchAuthSessionServer, +} from '@/utils/client/AmplifyUtils'; jest.mock('@/utils/client/AmplifyUtils', () => ({ AuthGetCurrentUserServer: jest.fn(), AuthFetchUserAttributesServer: jest.fn(), + AuthFetchAuthSessionServer: jest.fn(), })); jest.mock('next/server', () => ({ @@ -71,7 +76,19 @@ describe('Auth Flow Integration Tests', () => { const mockAuthGetCurrentUserServer = AuthGetCurrentUserServer as jest.Mock; const mockAuthFetchUserAttributesServer = AuthFetchUserAttributesServer as jest.Mock; + const mockAuthFetchAuthSessionServer = AuthFetchAuthSessionServer as jest.Mock; + const mockAuthSession = { + tokens: { + idToken: { + payload: { + 'cognito:username': 'test-user-123', + }, + }, + }, + }; + + mockAuthFetchAuthSessionServer.mockResolvedValue(mockAuthSession); mockAuthGetCurrentUserServer.mockResolvedValue(mockUser); mockAuthFetchUserAttributesServer.mockResolvedValue(mockUserAttributes); }); @@ -86,6 +103,7 @@ describe('Auth Flow Integration Tests', () => { expect(storeAccessSession).toEqual(expectedSession); expect(storeSession).toEqual(expectedSession); + expect(AuthFetchAuthSessionServer).toHaveBeenCalled(); expect(AuthGetCurrentUserServer).toHaveBeenCalled(); expect(AuthFetchUserAttributesServer).toHaveBeenCalled(); }); @@ -103,7 +121,8 @@ describe('Auth Flow Integration Tests', () => { expect(result).toEqual(expectedSession); }); - // Verificar que AuthGetCurrentUserServer fue llamado + // Verificar que las funciones fueron llamadas + expect(AuthFetchAuthSessionServer).toHaveBeenCalled(); expect(AuthGetCurrentUserServer).toHaveBeenCalled(); expect(AuthFetchUserAttributesServer).toHaveBeenCalled(); }); @@ -137,6 +156,7 @@ describe('Auth Flow Integration Tests', () => { const session = await getSession(regionRequest, mockResponse); expect(session).toEqual(expectedSession); + expect(AuthFetchAuthSessionServer).toHaveBeenCalled(); expect(AuthGetCurrentUserServer).toHaveBeenCalled(); expect(AuthFetchUserAttributesServer).toHaveBeenCalled(); } diff --git a/test/unit/middlewares/auth-cache.test.ts b/test/unit/middlewares/auth-cache.test.ts index db8b74d2..38137b50 100644 --- a/test/unit/middlewares/auth-cache.test.ts +++ b/test/unit/middlewares/auth-cache.test.ts @@ -1,5 +1,9 @@ import { getSession, clearAllSessionCache } from '@/middlewares/auth/auth'; -import { AuthGetCurrentUserServer, AuthFetchUserAttributesServer } from '@/utils/client/AmplifyUtils'; +import { + AuthGetCurrentUserServer, + AuthFetchUserAttributesServer, + AuthFetchAuthSessionServer, +} from '@/utils/client/AmplifyUtils'; import { NextRequest, NextResponse } from 'next/server'; // Mock de NodeCache @@ -15,6 +19,7 @@ jest.mock('node-cache', () => { jest.mock('@/utils/client/AmplifyUtils', () => ({ AuthGetCurrentUserServer: jest.fn(), AuthFetchUserAttributesServer: jest.fn(), + AuthFetchAuthSessionServer: jest.fn(), })); jest.mock('next/server', () => ({ @@ -74,12 +79,25 @@ describe('getSession with Caching', () => { it('debe fetchear una nueva sesión correctamente', async () => { const mockAuthGetCurrentUserServer = AuthGetCurrentUserServer as jest.Mock; const mockAuthFetchUserAttributesServer = AuthFetchUserAttributesServer as jest.Mock; + const mockAuthFetchAuthSessionServer = AuthFetchAuthSessionServer as jest.Mock; + + const mockAuthSession = { + tokens: { + idToken: { + payload: { + 'cognito:username': 'testuser', + }, + }, + }, + }; + mockAuthFetchAuthSessionServer.mockResolvedValueOnce(mockAuthSession); mockAuthGetCurrentUserServer.mockResolvedValueOnce(mockUser); mockAuthFetchUserAttributesServer.mockResolvedValueOnce(mockUserAttributes); const result = await getSession(mockRequest, mockResponse); + expect(mockAuthFetchAuthSessionServer).toHaveBeenCalled(); expect(mockAuthGetCurrentUserServer).toHaveBeenCalled(); expect(mockAuthFetchUserAttributesServer).toHaveBeenCalled(); expect(result).toEqual(expectedSession); @@ -88,13 +106,16 @@ describe('getSession with Caching', () => { it('debe retornar null cuando no hay usuario autenticado', async () => { const mockAuthGetCurrentUserServer = AuthGetCurrentUserServer as jest.Mock; const mockAuthFetchUserAttributesServer = AuthFetchUserAttributesServer as jest.Mock; + const mockAuthFetchAuthSessionServer = AuthFetchAuthSessionServer as jest.Mock; + mockAuthFetchAuthSessionServer.mockResolvedValueOnce(null); mockAuthGetCurrentUserServer.mockResolvedValueOnce(null); // No se llama a AuthFetchUserAttributesServer cuando no hay usuario mockAuthFetchUserAttributesServer.mockResolvedValueOnce(null); const result = await getSession(mockRequest, mockResponse); + expect(mockAuthFetchAuthSessionServer).toHaveBeenCalled(); expect(mockAuthGetCurrentUserServer).toHaveBeenCalled(); // AuthFetchUserAttributesServer no se llama cuando no hay usuario expect(mockAuthFetchUserAttributesServer).not.toHaveBeenCalled(); diff --git a/test/unit/middlewares/auth.test.ts b/test/unit/middlewares/auth.test.ts index 8a5c7092..374fdce0 100644 --- a/test/unit/middlewares/auth.test.ts +++ b/test/unit/middlewares/auth.test.ts @@ -4,13 +4,18 @@ import { handleAuthenticationMiddleware, clearAllSessionCache, } from '@/middlewares/auth/auth'; -import { AuthGetCurrentUserServer, AuthFetchUserAttributesServer } from '@/utils/client/AmplifyUtils'; +import { + AuthGetCurrentUserServer, + AuthFetchUserAttributesServer, + AuthFetchAuthSessionServer, +} from '@/utils/client/AmplifyUtils'; import { NextRequest, NextResponse } from 'next/server'; // Mock de los módulos externos jest.mock('@/utils/client/AmplifyUtils', () => ({ AuthGetCurrentUserServer: jest.fn(), AuthFetchUserAttributesServer: jest.fn(), + AuthFetchAuthSessionServer: jest.fn(), })); jest.mock('next/server', () => ({ @@ -57,12 +62,16 @@ describe('Auth Middleware', () => { const mockAuthGetCurrentUserServer = AuthGetCurrentUserServer as jest.Mock; const mockAuthFetchUserAttributesServer = AuthFetchUserAttributesServer as jest.Mock; + const mockAuthFetchAuthSessionServer = AuthFetchAuthSessionServer as jest.Mock; + // Mock AuthFetchAuthSessionServer para que retorne una sesión válida + mockAuthFetchAuthSessionServer.mockResolvedValueOnce({ tokens: { idToken: { payload: {} } } }); mockAuthGetCurrentUserServer.mockResolvedValueOnce(mockUser); mockAuthFetchUserAttributesServer.mockResolvedValueOnce(mockUserAttributes); const result = await getSession(mockRequest, mockResponse); + expect(mockAuthFetchAuthSessionServer).toHaveBeenCalled(); expect(mockAuthGetCurrentUserServer).toHaveBeenCalled(); expect(mockAuthFetchUserAttributesServer).toHaveBeenCalled(); expect(result).toEqual({ @@ -133,7 +142,13 @@ describe('Auth Middleware', () => { }; const mockAuthGetCurrentUserServer = AuthGetCurrentUserServer as jest.Mock; + const mockAuthFetchAuthSessionServer = AuthFetchAuthSessionServer as jest.Mock; + const mockAuthFetchUserAttributesServer = AuthFetchUserAttributesServer as jest.Mock; + + // Mock AuthFetchAuthSessionServer para que retorne una sesión válida + mockAuthFetchAuthSessionServer.mockResolvedValueOnce({ tokens: { idToken: { payload: {} } } }); mockAuthGetCurrentUserServer.mockResolvedValueOnce(mockUser); + mockAuthFetchUserAttributesServer.mockResolvedValueOnce({}); const result = await handleAuthenticationMiddleware(mockRequest, mockResponse); @@ -196,7 +211,13 @@ describe('Auth Middleware', () => { } as unknown as NextRequest; const mockAuthGetCurrentUserServer = AuthGetCurrentUserServer as jest.Mock; + const mockAuthFetchAuthSessionServer = AuthFetchAuthSessionServer as jest.Mock; + const mockAuthFetchUserAttributesServer = AuthFetchUserAttributesServer as jest.Mock; + + // Mock AuthFetchAuthSessionServer para que retorne una sesión válida + mockAuthFetchAuthSessionServer.mockResolvedValueOnce({ tokens: { idToken: { payload: {} } } }); mockAuthGetCurrentUserServer.mockResolvedValueOnce(mockUser); + mockAuthFetchUserAttributesServer.mockResolvedValueOnce({}); const mockNextResponseRedirect = NextResponse.redirect as jest.Mock; mockNextResponseRedirect.mockReturnValueOnce(mockRedirectResponse); @@ -253,7 +274,13 @@ describe('Auth Middleware', () => { }; const mockAuthGetCurrentUserServer = AuthGetCurrentUserServer as jest.Mock; + const mockAuthFetchAuthSessionServer = AuthFetchAuthSessionServer as jest.Mock; + const mockAuthFetchUserAttributesServer = AuthFetchUserAttributesServer as jest.Mock; + + // Mock AuthFetchAuthSessionServer para que retorne una sesión válida + mockAuthFetchAuthSessionServer.mockResolvedValueOnce({ tokens: { idToken: { payload: {} } } }); mockAuthGetCurrentUserServer.mockResolvedValueOnce(mockUser); + mockAuthFetchUserAttributesServer.mockResolvedValueOnce({}); await handleAuthenticatedRedirectMiddleware(customRequest, mockResponse); From 69e16d37e44eec5565ddee94914b908eafe3f59d Mon Sep 17 00:00:00 2001 From: Steven Date: Sat, 25 Oct 2025 10:29:23 -0500 Subject: [PATCH 3/3] Refactor form components to improve structure and readability This commit refactors the form components in the UI library by converting several forwardRef components to standard function components. It also enhances the use of the useFormState hook for better form state management and updates the rendering logic for FormLabel, FormControl, FormDescription, and FormMessage to improve clarity and maintainability. These changes streamline the code and enhance the overall structure of the form handling logic. --- components/ui/form.tsx | 139 +++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 68 deletions(-) diff --git a/components/ui/form.tsx b/components/ui/form.tsx index b0d5561f..c93555c2 100644 --- a/components/ui/form.tsx +++ b/components/ui/form.tsx @@ -1,7 +1,17 @@ +'use client'; + import * as React from 'react'; import * as LabelPrimitive from '@radix-ui/react-label'; import { Slot } from '@radix-ui/react-slot'; -import { Controller, ControllerProps, FieldPath, FieldValues, FormProvider, useFormContext } from 'react-hook-form'; +import { + Controller, + FormProvider, + useFormContext, + useFormState, + type ControllerProps, + type FieldPath, + type FieldValues, +} from 'react-hook-form'; import { cn } from '@/lib/utils'; import { Label } from '@/components/ui/label'; @@ -33,8 +43,8 @@ const FormField = < const useFormField = () => { const fieldContext = React.useContext(FormFieldContext); const itemContext = React.useContext(FormItemContext); - const { getFieldState, formState } = useFormContext(); - + const { getFieldState } = useFormContext(); + const formState = useFormState({ name: fieldContext.name }); const fieldState = getFieldState(fieldContext.name, formState); if (!fieldContext) { @@ -59,77 +69,70 @@ type FormItemContextValue = { const FormItemContext = React.createContext({} as FormItemContextValue); -const FormItem = React.forwardRef>( - ({ className, ...props }, ref) => { - const id = React.useId(); +function FormItem({ className, ...props }: React.ComponentProps<'div'>) { + const id = React.useId(); - return ( - -
- - ); - } -); -FormItem.displayName = 'FormItem'; + return ( + +
+ + ); +} -const FormLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { +function FormLabel({ className, ...props }: React.ComponentProps) { const { error, formItemId } = useFormField(); - return