package com.atlassian.stash.internal.maintenance.backup;

import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.i18n.SimpleI18nService;
import com.atlassian.stash.internal.ApplicationSettings;
import com.atlassian.stash.internal.maintenance.MaintenanceTask;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.bitbucket.auth.AuthenticationContext;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.io.File;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class BackupPhaseTest {

    @Mock
    private AuthenticationContext authenticationContext;
    @Mock
    private ApplicationSettings applicationSettings;
    @SuppressWarnings("unused")
    private I18nService i18nService = new SimpleI18nService(SimpleI18nService.Mode.RETURN_KEYS);
    @Mock
    private MaintenanceTask nested;
    private BackupPhase phase;
    @InjectMocks
    private BackupPhase.Builder phaseBuilder;
    @Mock
    private BackupState state;
    @Captor
    private ArgumentCaptor<File> fileCaptor;
    @Rule
    public TemporaryFolder temporaryFolder = new TemporaryFolder();

    @Before
    public void setUp() {
        phase = phaseBuilder.add(nested, 100).build();

        when(applicationSettings.getBackupDir()).thenReturn(temporaryFolder.getRoot());
    }

    @Test
    public void testBackupDeletedWhenNestedFailsWithException() {
        doThrow(new NullPointerException("gotcha")).when(nested).run();

        try {
            phase.run();
        } catch (NullPointerException e) {
            // expected. Now verify that the partially written file has been deleted
            verify(state).setBackupFile(fileCaptor.capture());
            assertFalse("partial zip file should have been deleted!", fileCaptor.getValue().exists());
        }
    }

    @Test
    public void testBackupSurvivesWhenNestedSucceeds() {
        phase.run();

        verify(state).setBackupFile(fileCaptor.capture());
        verify(state).setBackupZipStream(any(BackupPhase.CloseShieldZipOutputStream.class));

        assertTrue("partial zip file shouldn't have been deleted!", fileCaptor.getValue().exists());
    }

    @Test
    public void testBackupFilenameIncludesAnonymousWhenNotAuthenticated() {
        phase.run();

        verify(state).setBackupFile(fileCaptor.capture());
        assertTrue(fileCaptor.getValue().getName().contains("anonymous"));
    }

    @Test
    public void testBackupFilenameIncludesUsernameWhenAuthenticated() {
        ApplicationUser user = mock(ApplicationUser.class);
        when(user.getName()).thenReturn("mr_retention");
        when(authenticationContext.getCurrentUser()).thenReturn(user);

        phase.run();

        verify(state).setBackupFile(fileCaptor.capture());
        assertTrue(fileCaptor.getValue().getName().contains("mr_retention"));
    }

    @Test
    public void testBackupFilenameRemovesDots() {
        //the backup service ignores anything with '.', '/' and '\' to protected against directory escaping
        //plus forward and back slashes will cause trouble when creating the file
        ApplicationUser user = mock(ApplicationUser.class);
        when(user.getName()).thenReturn("mr..foo/bar\\baz");
        when(authenticationContext.getCurrentUser()).thenReturn(user);

        phase.run();

        verify(state).setBackupFile(fileCaptor.capture());
        assertTrue(fileCaptor.getValue().getName().contains("mr__foo_bar_baz"));
    }

}
