package com.atlassian.stash.internal.scm.git;

import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.repository.Branch;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.Command;
import com.atlassian.bitbucket.scm.UpdateDefaultBranchCommandParameters;
import com.atlassian.bitbucket.scm.git.GitAgent;
import com.atlassian.bitbucket.scm.git.command.GitCommand;
import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory;
import com.atlassian.bitbucket.scm.git.command.GitScmCommandBuilder;
import com.atlassian.bitbucket.scm.git.command.symbolicref.GitSymbolicRef;
import com.atlassian.bitbucket.scm.git.command.symbolicref.GitSymbolicRefSetBuilder;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
 * Contains {@link DefaultGitExtendedCommandFactory} tests which use mock objects to verify behaviour
 * without actually invoking {@code git}.
 * <p>
 * Some tests will be able to use mock objects and effectively verify behaviour. Others need to run real
 * commands and verify their output to perform useful testing. Tests which run real commands should be
 * added to {@link DefaultGitExtendedCommandFactoryTest} instead.
 */
@RunWith(MockitoJUnitRunner.class)
@SuppressWarnings("unchecked")
public class DefaultGitExtendedCommandFactoryMockTest {

    @Mock
    private InternalGitAgent agent;
    @Mock
    private GitScmCommandBuilder builder;
    @Mock
    private GitCommandBuilderFactory builderFactory;
    @InjectMocks
    private DefaultGitExtendedCommandFactory commandFactory;
    @Mock
    private InternalGitScmConfig config;
    @Mock
    private I18nService i18nService;
    @Mock
    private Repository repository;

    @Before
    public void setup() {
        when(builderFactory.builder(same(repository))).thenReturn(builder);

        when(config.getBinary()).thenReturn("git");

        when(repository.getName()).thenReturn("Test");
    }

    @Test
    public void testUpdateDefaultBranchWithResolvableBranch() {
        GitCommand<Void> expected = mock(GitCommand.class);

        GitSymbolicRefSetBuilder refSetBuilder = mock(GitSymbolicRefSetBuilder.class);
        when(refSetBuilder.build()).thenReturn(expected);

        GitSymbolicRef symbolicRef = mock(GitSymbolicRef.class);
        when(symbolicRef.set(eq(GitAgent.HEAD), eq("refs/heads/master"))).thenReturn(refSetBuilder);

        Branch branch = mock(Branch.class);
        when(branch.getId()).thenReturn("refs/heads/master");

        when(agent.resolveBranch(same(repository), eq("master"))).thenReturn(branch);
        when(builder.symbolicRef()).thenReturn(symbolicRef);

        Command<Void> actual = commandFactory.updateDefaultBranch(repository,
                new UpdateDefaultBranchCommandParameters.Builder("master").build());
        assertNotNull(actual);
        assertSame(expected, actual);
    }

    @Test
    public void testUpdateDefaultBranchWithUnresolvableBranch() {
        GitCommand<Void> expected = mock(GitCommand.class);

        GitSymbolicRefSetBuilder refSetBuilder = mock(GitSymbolicRefSetBuilder.class);
        when(refSetBuilder.build()).thenReturn(expected);

        GitSymbolicRef symbolicRef = mock(GitSymbolicRef.class);
        when(symbolicRef.set(eq(GitAgent.HEAD), eq("refs/heads/something"))).thenReturn(refSetBuilder);

        when(agent.qualifyBranch(eq("heads/something"))).thenReturn("refs/heads/something");
        when(builder.symbolicRef()).thenReturn(symbolicRef);

        Command<Void> actual = commandFactory.updateDefaultBranch(repository,
                new UpdateDefaultBranchCommandParameters.Builder("heads/something").build());
        assertNotNull(actual);
        assertSame(expected, actual);
    }
}
