mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
Moar Login Fixes (#4948)
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import AuthCallback from '@app/routes/AuthCallback';
|
||||
import { springAuth } from '@app/auth/springAuthClient';
|
||||
|
||||
// Mock springAuth
|
||||
vi.mock('@app/auth/springAuthClient', () => ({
|
||||
springAuth: {
|
||||
getSession: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock useNavigate
|
||||
const mockNavigate = vi.fn();
|
||||
vi.mock('react-router-dom', async () => {
|
||||
const actual = await vi.importActual('react-router-dom');
|
||||
return {
|
||||
...actual,
|
||||
useNavigate: () => mockNavigate,
|
||||
};
|
||||
});
|
||||
|
||||
describe('AuthCallback', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
vi.clearAllMocks();
|
||||
// Reset window.location.hash
|
||||
window.location.hash = '';
|
||||
});
|
||||
|
||||
it('should extract JWT from URL hash and validate it', async () => {
|
||||
const mockToken = 'oauth-jwt-token';
|
||||
const mockUser = {
|
||||
id: '123',
|
||||
email: '[email protected]',
|
||||
username: 'oauthuser',
|
||||
role: 'USER',
|
||||
};
|
||||
|
||||
// Set URL hash with access token
|
||||
window.location.hash = `#access_token=${mockToken}`;
|
||||
|
||||
// Mock successful session validation
|
||||
vi.mocked(springAuth.getSession).mockResolvedValueOnce({
|
||||
data: {
|
||||
session: {
|
||||
user: mockUser,
|
||||
access_token: mockToken,
|
||||
expires_in: 3600,
|
||||
expires_at: Date.now() + 3600000,
|
||||
},
|
||||
},
|
||||
error: null,
|
||||
});
|
||||
|
||||
const dispatchEventSpy = vi.spyOn(window, 'dispatchEvent');
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<AuthCallback />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
// Verify JWT was stored
|
||||
expect(localStorage.getItem('stirling_jwt')).toBe(mockToken);
|
||||
|
||||
// Verify jwt-available event was dispatched
|
||||
expect(dispatchEventSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ type: 'jwt-available' })
|
||||
);
|
||||
|
||||
// Verify getSession was called to validate token
|
||||
expect(springAuth.getSession).toHaveBeenCalled();
|
||||
|
||||
// Verify navigation to home
|
||||
expect(mockNavigate).toHaveBeenCalledWith('/', { replace: true });
|
||||
});
|
||||
});
|
||||
|
||||
it('should redirect to login when no access token in hash', async () => {
|
||||
// No hash or empty hash
|
||||
window.location.hash = '';
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<AuthCallback />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockNavigate).toHaveBeenCalledWith('/login', {
|
||||
replace: true,
|
||||
state: { error: 'OAuth login failed - no token received.' },
|
||||
});
|
||||
expect(localStorage.getItem('stirling_jwt')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('should redirect to login when token validation fails', async () => {
|
||||
const invalidToken = 'invalid-oauth-token';
|
||||
window.location.hash = `#access_token=${invalidToken}`;
|
||||
|
||||
// Mock failed session validation
|
||||
vi.mocked(springAuth.getSession).mockResolvedValueOnce({
|
||||
data: { session: null },
|
||||
error: { message: 'Invalid token' },
|
||||
});
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<AuthCallback />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
// JWT should be stored initially
|
||||
expect(localStorage.getItem('stirling_jwt')).toBeNull(); // Cleared after validation failure
|
||||
|
||||
// Verify redirect to login
|
||||
expect(mockNavigate).toHaveBeenCalledWith('/login', {
|
||||
replace: true,
|
||||
state: { error: 'OAuth login failed - invalid token.' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle errors gracefully', async () => {
|
||||
const mockToken = 'error-token';
|
||||
window.location.hash = `#access_token=${mockToken}`;
|
||||
|
||||
// Mock getSession throwing error
|
||||
vi.mocked(springAuth.getSession).mockRejectedValueOnce(
|
||||
new Error('Network error')
|
||||
);
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<AuthCallback />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockNavigate).toHaveBeenCalledWith('/login', {
|
||||
replace: true,
|
||||
state: { error: 'OAuth login failed. Please try again.' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should display loading state while processing', () => {
|
||||
window.location.hash = '#access_token=processing-token';
|
||||
|
||||
vi.mocked(springAuth.getSession).mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((resolve) =>
|
||||
setTimeout(
|
||||
() =>
|
||||
resolve({
|
||||
data: { session: null },
|
||||
error: { message: 'Token expired' },
|
||||
}),
|
||||
100
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const { getByText } = render(
|
||||
<BrowserRouter>
|
||||
<AuthCallback />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
expect(getByText('Completing authentication...')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user