package com.atlassian.stash.internal.user;

import com.atlassian.bitbucket.project.Project;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.permission.PermissionService;
import com.atlassian.bitbucket.permission.PermissionPredicateFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.function.Predicate;

import static org.mockito.Mockito.*;

import static org.junit.Assert.*;

@RunWith(MockitoJUnitRunner.class)
public class PermissionPredicateFactoryImplTest {
    
    @Mock
    private PermissionService permissionService;
    
    @Test
    public void testCreateProjectPermissionPredicate() {
        Project green = mock(Project.class);
        Project red = mock(Project.class);

        PermissionPredicateFactory factory = new PermissionPredicateFactoryImpl(permissionService);
        when(permissionService.hasProjectPermission(same(green), same(Permission.PROJECT_READ))).thenReturn(true);
        when(permissionService.hasProjectPermission(same(red), same(Permission.PROJECT_READ))).thenReturn(false);

        Predicate<Project> predicate = factory.createProjectPermissionPredicate(Permission.PROJECT_READ);
        assertTrue(predicate.test(green));
        assertFalse(predicate.test(red));
        try {
            predicate.test(null);

            fail("Applying the project Predicate to a null Project should throw");
        } catch (NullPointerException ignored) {
            //Catching an NPE indicates the predicate correctly rejected the null parameter
        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void testCreateProjectPermissionPredicateWithGlobalPermission() {
        new PermissionPredicateFactoryImpl(permissionService).createProjectPermissionPredicate(Permission.SYS_ADMIN);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testCreateProjectPermissionPredicateWithRepositoryPermission() {
        new PermissionPredicateFactoryImpl(permissionService).createProjectPermissionPredicate(Permission.REPO_ADMIN);
    }

    @Test
    public void testCreateRepositoryPermissionPredicate() {
        Repository green = mock(Repository.class);
        Repository red = mock(Repository.class);

        PermissionPredicateFactory factory = new PermissionPredicateFactoryImpl(permissionService);
        when(permissionService.hasRepositoryPermission(same(green), same(Permission.REPO_WRITE))).thenReturn(true);
        when(permissionService.hasRepositoryPermission(same(red), same(Permission.REPO_WRITE))).thenReturn(false);

        Predicate<Repository> predicate = factory.createRepositoryPermissionPredicate(Permission.REPO_WRITE);
        assertTrue(predicate.test(green));
        assertFalse(predicate.test(red));
        try {
            predicate.test(null);

            fail("Applying the repository Predicate to a null Repository should throw");
        } catch (NullPointerException ignored) {
            //Catching an NPE indicates the predicate correctly rejected the null parameter
        }
    }

    @Test(expected = IllegalArgumentException.class)
    public void testCreateRepositoryPermissionPredicateWithGlobalPermission() {
        new PermissionPredicateFactoryImpl(permissionService).createRepositoryPermissionPredicate(Permission.ADMIN);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testCreateRepositoryPermissionPredicateWithProjectPermission() {
        new PermissionPredicateFactoryImpl(permissionService).createRepositoryPermissionPredicate(Permission.PROJECT_ADMIN);
    }

    @Test
    public void testCreateProjectAccessiblePredicate() {
        Project green = mock(Project.class);
        Project red = mock(Project.class);

        PermissionPredicateFactory factory = new PermissionPredicateFactoryImpl(permissionService);
        when(permissionService.isProjectAccessible(same(green))).thenReturn(true);
        when(permissionService.isProjectAccessible(same(red))).thenReturn(false);

        Predicate<Project> predicate = factory.createProjectAccessiblePredicate();
        assertTrue(predicate.test(green));
        assertFalse(predicate.test(red));
        try {
            predicate.test(null);

            fail("Applying the project Predicate to a null Project should throw");
        } catch (NullPointerException ignored) {
            //Catching an NPE indicates the predicate correctly rejected the null parameter
        }
    }

    @Test
    public void testCreateRepositoryAccessiblePredicate() throws Exception {
        Repository green = mock(Repository.class);
        Repository red = mock(Repository.class);

        PermissionPredicateFactory factory = new PermissionPredicateFactoryImpl(permissionService);
        when(permissionService.isRepositoryAccessible(same(green))).thenReturn(true);
        when(permissionService.isRepositoryAccessible(same(red))).thenReturn(false);

        Predicate<Repository> predicate = factory.createRepositoryAccessiblePredicate();
        assertTrue(predicate.test(green));
        assertFalse(predicate.test(red));
        try {
            predicate.test(null);

            fail("Applying the repository Predicate to a null Repository should throw");
        } catch (NullPointerException ignored) {
            //Catching an NPE indicates the predicate correctly rejected the null parameter
        }
    }
}
