package org.springframework.batch.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

import org.junit.Test;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.scope.context.JobContext;
import org.springframework.batch.core.scope.context.JobSynchronizationManager;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestContextManager;

/**
 * @author Dave Syer
 * @since 2.1
 */
@ContextConfiguration
public class JobScopeTestExecutionListenerTests {

	private JobScopeTestExecutionListener listener = new JobScopeTestExecutionListener();

	@Test
	public void testDefaultJobContext() throws Exception {
		TestContext testContext = getTestContext(new Object());
		listener.prepareTestInstance(testContext);
		listener.beforeTestMethod(testContext);
		JobContext context = JobSynchronizationManager.getContext();
		assertNotNull(context);
		listener.afterTestMethod(testContext);
		assertNull(JobSynchronizationManager.getContext());
	}

	@Test
	public void testWithJobExecutionFactory() throws Exception {
		testExecutionContext(new WithJobExecutionFactory());
	}

	@Test
	public void testWithParameters() throws Exception {
		testJobParameters(new WithJobExecutionFactory());
	}

	private void testExecutionContext(Object target) throws Exception {
		TestContext testContext = getTestContext(target);
		listener.prepareTestInstance(testContext);
		try {
			listener.beforeTestMethod(testContext);
			JobContext context = JobSynchronizationManager.getContext();
			assertNotNull(context);
			assertEquals("bar", context.getJobExecutionContext().get("foo"));
		}
		finally {
			listener.afterTestMethod(testContext);
		}
		assertNull(JobSynchronizationManager.getContext());
	}

	private void testJobParameters(Object target) throws Exception {
		TestContext testContext = getTestContext(target);
		listener.prepareTestInstance(testContext);
		try {
			listener.beforeTestMethod(testContext);
			JobContext context = JobSynchronizationManager.getContext();
			assertNotNull(context);
			assertEquals("spam", context.getJobParameters().get("foo"));
		}
		finally {
			listener.afterTestMethod(testContext);
		}
		assertNull(JobSynchronizationManager.getContext());
	}

	@SuppressWarnings("unused")
	private static class WithJobExecutionFactory {
		public JobExecution getJobExecution() {
			JobExecution jobExecution = MetaDataInstanceFactory.createJobExecution("job", 11L, 123L,
					new JobParametersBuilder().addString("foo", "spam").toJobParameters());
			jobExecution.getExecutionContext().putString("foo", "bar");
			return jobExecution;
		}
	}

	private TestContext getTestContext(Object target) throws Exception {
		return new MockTestContextManager(target, getClass()).getContext();
	}

	private final class MockTestContextManager extends TestContextManager {

		private MockTestContextManager(Object target, Class<?> testClass) throws Exception {
			super(testClass);
			prepareTestInstance(target);
		}

		public TestContext getContext() {
			return getTestContext();
		}

	}

}
