package com.atlassian.stash.internal.auth;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.WebAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class DefaultAuthenticationHelperTest {

    @InjectMocks
    private DefaultAuthenticationHelper authenticationHelper;
    @Mock
    private HttpServletRequest request;

    @Test
    public void testGetAuthenticationExceptionWithSession() {
        AuthenticationException exception = mock(AuthenticationException.class);

        HttpSession session = mock(HttpSession.class);
        when(session.getAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION))).thenReturn(exception);

        when(request.getSession(eq(false))).thenReturn(session);

        assertSame(exception, authenticationHelper.getAuthenticationException(request));

        InOrder ordered = inOrder(request, session);
        ordered.verify(request).getSession(eq(false));
        ordered.verify(session).getAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION));
        ordered.verifyNoMoreInteractions();
    }

    @Test
    public void testGetAuthenticationExceptionWithoutSessionUsesRequest() {
        AuthenticationException exception = mock(AuthenticationException.class);

        when(request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).thenReturn(exception);

        assertSame(exception, authenticationHelper.getAuthenticationException(request));

        InOrder ordered = inOrder(request);
        ordered.verify(request).getSession(eq(false)); //Session should have precedence
        ordered.verify(request).getAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION));
        ordered.verifyNoMoreInteractions();
    }

    @Test
    public void testGetCachedUsernameWithSession() {
        HttpSession session = mock(HttpSession.class);
        when(session.getAttribute(eq(DefaultAuthenticationHelper.ATTR_CACHED_USERNAME))).thenReturn("sideshowbob");

        when(request.getSession(eq(false))).thenReturn(session);

        assertEquals("sideshowbob", authenticationHelper.getCachedUsername(request));

        InOrder ordered = inOrder(request, session);
        ordered.verify(request).getSession(eq(false));
        ordered.verify(session).getAttribute(eq(DefaultAuthenticationHelper.ATTR_CACHED_USERNAME));
        ordered.verifyNoMoreInteractions();
    }

    @Test
    public void testGetCachedUsernameWithoutSessionUsesRequest() {
        when(request.getAttribute(DefaultAuthenticationHelper.ATTR_CACHED_USERNAME)).thenReturn("sideshowbob");

        assertEquals("sideshowbob", authenticationHelper.getCachedUsername(request));

        InOrder ordered = inOrder(request);
        ordered.verify(request).getSession(eq(false)); //Session should have precedence
        ordered.verify(request).getAttribute(eq(DefaultAuthenticationHelper.ATTR_CACHED_USERNAME));
    }

    @Test
    public void testIsAuthenticationFailedWithSession() {
        HttpSession session = mock(HttpSession.class);
        when(session.getAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION)))
                .thenReturn(mock(AuthenticationException.class));

        when(request.getSession(eq(false))).thenReturn(session);

        assertTrue(authenticationHelper.isAuthenticationFailed(request));

        InOrder ordered = inOrder(request, session);
        ordered.verify(request).getSession(eq(false));
        ordered.verify(session).getAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION));
        ordered.verifyNoMoreInteractions();
    }

    @Test
    public void testIsAuthenticationFailedWithoutSessionUsesRequest() {
        AuthenticationException exception = mock(AuthenticationException.class);

        when(request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).thenReturn(exception);

        assertTrue(authenticationHelper.isAuthenticationFailed(request));

        InOrder ordered = inOrder(request);
        ordered.verify(request).getSession(eq(false)); //Session should have precedence
        ordered.verify(request).getAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION));
        ordered.verifyNoMoreInteractions();
    }

    @Test
    public void testSetCachedUsernameWithSession() {
        HttpSession session = mock(HttpSession.class);

        when(request.getSession(eq(false))).thenReturn(session);

        authenticationHelper.setCachedUsername(request, "sideshowbob");

        //When a session is present, the attribute should be set on both the request and the session
        verify(request).setAttribute(eq(DefaultAuthenticationHelper.ATTR_CACHED_USERNAME), eq("sideshowbob"));
        verify(session).setAttribute(eq(DefaultAuthenticationHelper.ATTR_CACHED_USERNAME), eq("sideshowbob"));
    }

    @Test
    public void testSetAuthenticationExceptionToNullWithSession() {
        HttpSession session = mock(HttpSession.class);

        when(request.getSession(eq(false))).thenReturn(session);

        authenticationHelper.setAuthenticationException(request, null);

        //When a session is present, the attribute should be removed from both the request and the session
        verify(request).removeAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION));
        verify(session).removeAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION));
    }

    @Test
    public void testSetAuthenticationExceptionToNullWithSessionUsesRequest() {
        authenticationHelper.setAuthenticationException(request, null);

        InOrder ordered = inOrder(request);
        ordered.verify(request).getSession(eq(false));
        ordered.verify(request).removeAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION));
    }

    @Test
    public void testSetAuthenticationExceptionWithSession() {
        AuthenticationException exception = mock(AuthenticationException.class);
        HttpSession session = mock(HttpSession.class);

        when(request.getSession(eq(false))).thenReturn(session);

        authenticationHelper.setAuthenticationException(request, exception);

        //When a session is present, the attribute should be set on both the request and the session
        verify(request).setAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION), same(exception));
        verify(session).setAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION), same(exception));
    }

    @Test
    public void testSetAuthenticationExceptionWithoutSessionUsesRequest() {
        AuthenticationException exception = mock(AuthenticationException.class);

        authenticationHelper.setAuthenticationException(request, exception);

        InOrder ordered = inOrder(request);
        ordered.verify(request).getSession(eq(false));
        ordered.verify(request).setAttribute(eq(WebAttributes.AUTHENTICATION_EXCEPTION), same(exception));
    }
}
