package net.yher2.workstyle.manager;

import java.io.File;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import net.yher2.commons.io.Classpath;
import net.yher2.commons.lang.DateUtils;
import net.yher2.commons.lang.Pager;
import net.yher2.commons.torque.Transaction;
import net.yher2.workstyle.FileData;
import net.yher2.workstyle.TaskQuery;
import net.yher2.workstyle.exception.NotFoundException;
import net.yher2.workstyle.test.DBTestCase;
import net.yher2.workstyle.torque.AttachedFile;
import net.yher2.workstyle.torque.AttachedFilePeer;
import net.yher2.workstyle.torque.FileInfo;
import net.yher2.workstyle.torque.FileInfoPeer;
import net.yher2.workstyle.torque.Status;
import net.yher2.workstyle.torque.StatusPeer;
import net.yher2.workstyle.torque.Tag;
import net.yher2.workstyle.torque.TagPeer;
import net.yher2.workstyle.torque.Task;
import net.yher2.workstyle.torque.TaskCommentPeer;
import net.yher2.workstyle.torque.TaskPeer;
import net.yher2.workstyle.torque.TaskRelation;
import net.yher2.workstyle.torque.TaskRelationPeer;
import net.yher2.workstyle.torque.bean.AttachedFileBean;
import net.yher2.workstyle.torque.bean.TaskBean;
import net.yher2.workstyle.torque.bean.TaskCommentBean;

import org.apache.commons.lang.StringUtils;
import org.apache.torque.NoRowsException;
import org.apache.torque.TooManyRowsException;
import org.apache.torque.TorqueException;
import org.apache.torque.util.Criteria;

public class TaskManagerTest extends DBTestCase {
	TaskManager manager = null;
	Connection con = null;
	Classpath testData = new Classpath("net/yher2/workstyle/manager/TaskManagerTest.xls");
	Date date = null;
	
	List<FileInfo> existFiles = null;

	protected void setUp() throws Exception {
		super.setUp();
		super.prepare(testData);
		
		date = new Date();
		
		con = getConnection();
		con.setAutoCommit(false);
		manager = new TaskManager(con);
		
		existFiles = FileInfoPeer.doSelect(new Criteria(), con);
	}

	protected void tearDown() throws Exception {
		super.tearDown();
		deleteFiles();
		Transaction.rollback(con);
		super.clear(testData);
	}
	
	private void deleteFiles() throws TorqueException {
		List<Integer> idList = new ArrayList<Integer>();
		for (FileInfo file : existFiles) idList.add(file.getFileId());
		
		Criteria criteria = new Criteria();
		criteria.addNotIn(FileInfoPeer.FILE_ID, idList);
		List<FileInfo> fileList = FileInfoPeer.doSelect(criteria, con);
		for (FileInfo dbFile : fileList) {
			File sysFile = new File(dbFile.getPath());
			sysFile.delete();
		}
	}
	
	public void testDelete_TASKテーブルからレコードを削除する() throws Exception {
		manager.delete(1001);
		try {
			TaskPeer.retrieveByPK(1001, con);
		} catch (NoRowsException e) {
			return;
		}
		fail();
	}
	public void testDelete_TASK_COMMENTテーブルから指定のタスクへのコメントを削除する() throws Exception {
		TaskBean task = manager.get(1001);
		manager.delete(1001);
		
		assertTrue(task.getTaskCommentBeans().size() > 0);
		for (TaskCommentBean comment : task.getTaskCommentBeans()) {
			try {
				TaskCommentPeer.retrieveByPK(comment.getCommentId(), con);
			} catch (NoRowsException e) {
				continue;
			}
			fail();
		}
	}
	public void testDelete_FILEテーブルから指定のタスクへの添付ファイルを削除する() throws Exception {
		TaskBean task = manager.get(1001);
		manager.delete(1001);
		
		assertTrue(task.getAttachedFileBeans().size() > 0);
		for (AttachedFileBean file : task.getAttachedFileBeans()) {
			try {
				FileInfoPeer.retrieveByPK(file.getFileId(), con);
			} catch (NoRowsException e) {
				continue;
			}
			fail();
		}
	}
	public void testDelete_対象のタスクが存在しない場合はそのまま終了() throws Exception {
		try {
			manager.delete(0);
		} catch (Exception e) {
			fail();
		}
	}
	
	public void testGet() throws Exception {
		TaskBean result = manager.get(1001);
		Task dbTask = TaskPeer.retrieveByPK(result.getTaskId(), con);
		
		assertEquals(dbTask.getContents(), result.getContents());
		assertEquals(dbTask.getEstimatedManHour(), result.getEstimatedManHour());
		assertEquals(dbTask.getStatusId(), result.getStatusId());
		assertEquals(dbTask.getTagList(), result.getTagList());
		assertEquals(dbTask.getUpdateDate(), result.getUpdateDate());
		
		assertNotNull(result.getStatusBean());
		assertNotNull(result.getTaskCommentBeans());
		assertNotNull(result.getAttachedFileBeans());
	}
	public void testGet_関連タスクも取得すること() throws Exception {
		TaskBean result = manager.get(1001);
		
		assertTrue(result.getLinkedTaskList().size() > 0);
		
		List<Integer> linkedTaskIdList = getLinkedTaskIdList(result.getTaskId());
		for (TaskBean linkedTask : result.getLinkedTaskList()) {
			assertTrue(linkedTaskIdList.contains(linkedTask.getTaskId()));
			assertNotNull(linkedTask.getStatusBean());
		}
	}
	private List<Integer> getLinkedTaskIdList(int taskId) throws TorqueException {
		Criteria criteria = new Criteria();
		criteria.add(TaskRelationPeer.SRC_TASK_ID, taskId);
		
		List<TaskRelation> relationList = TaskRelationPeer.doSelect(criteria, con);
		List<Integer> result = new ArrayList<Integer>();
		for (TaskRelation relation : relationList) {
			result.add(relation.getDestTaskId());
		}
		
		criteria.clear();
		criteria.add(TaskRelationPeer.DEST_TASK_ID, taskId);
		
		relationList = TaskRelationPeer.doSelect(criteria, con);
		for (TaskRelation relation : relationList) {
			result.add(relation.getSrcTaskId());
		}
		return result;
	}
	
	public void testGet_存在しない場合はNotFoundException() throws Exception {
		try {
			manager.get(0);
		} catch (NotFoundException e) {
			return;
		}
		fail();
	}

	public void testAddLinkedTask_TASKテーブルにレコードを追加する() throws Exception {
		int parentId = 1001;
		TaskBean task = prepareValidTask();
		TaskBean result = manager.addLinkedTask(parentId, task);
		
		assertTrue(result.getTaskId() != 0);
		assertTrue(result.getTaskId() != parentId);
		
		assertEquals(task.getContents(), result.getContents());
		assertEquals(task.getEstimatedManHour(), result.getEstimatedManHour());
		assertEquals(task.getStatusId(), result.getStatusId());
		assertEquals(task.getTagList(), result.getTagList());
		assertNotNull(result.getUpdateDate());

		assertEqualsDb(result);
	}
	public void testAddLinkedTask_TASK_RELATIONテーブルにレコードを追加する() throws Exception {
		int parentId = 1001;
		TaskBean task = prepareValidTask();
		TaskBean result = manager.addLinkedTask(parentId, task);
		
		Criteria criteria = new Criteria();
		criteria.add(TaskRelationPeer.SRC_TASK_ID, parentId);
		criteria.add(TaskRelationPeer.DEST_TASK_ID, result.getTaskId());
		
		assertTrue(TaskRelationPeer.doSelect(criteria, con).size() > 0);
	}
	public void testAddLinkedTask_存在しない場合はTAGテーブルにもレコードを追加する() throws Exception {
		TaskBean task = prepareValidTask();
		
		assertEquals(0, TagPeer.doSelectByName(task.getTagAsList(), con).size());

		 manager.addLinkedTask(1001, task);
		
		assertTrue(task.getTagAsList().size() > 0);
		assertEquals(task.getTagAsList().size(), TagPeer.doSelectByName(task.getTagAsList(), con).size());
	}
	public void testAddLinkedTask_statusIdが不正な場合はUnknownに() throws Exception {
		TaskBean task = prepareValidTask();
		task.setStatusId(-1);
		
		TaskBean result = manager.addLinkedTask(1001, task);
		assertEquals(Status.UNKNOWN, result.getStatusId());
	}
	public void testAddLinkedTask_リンク元タスクが存在しない場合はタスク作成だけ行う() throws Exception {
		TaskBean task = prepareValidTask();
		task.setStatusId(0);
		
		TaskBean result = manager.addLinkedTask(0, task);
		
		assertTrue(result.getTaskId() != 0);
		assertEquals(task.getContents(), result.getContents());
		assertEquals(task.getEstimatedManHour(), result.getEstimatedManHour());
		assertEquals(task.getStatusId(), result.getStatusId());
		assertEquals(task.getTagList(), result.getTagList());
		assertNotNull(result.getUpdateDate());

		assertEqualsDb(result);
	}
	public void testAdd_TASKテーブルにレコードを追加する() throws Exception {
		TaskBean task = prepareValidTask();
		TaskBean result = manager.add(task);
		
		assertTrue(result.getTaskId() != 0);
		
		assertEquals(task.getContents(), result.getContents());
		assertEquals(task.getEstimatedManHour(), result.getEstimatedManHour());
		assertEquals(task.getStatusId(), result.getStatusId());
		assertEquals(task.getTagList(), result.getTagList());
		assertNotNull(result.getUpdateDate());
		
		assertEqualsDb(result);
	}
	public void testAdd_存在しない場合はTAGテーブルにもレコードを追加する() throws Exception {
		TaskBean task = prepareValidTask();
		
		assertEquals(0, TagPeer.doSelectByName(task.getTagAsList(), con).size());

		 manager.add(task);
		
		assertTrue(task.getTagAsList().size() > 0);
		assertEquals(task.getTagAsList().size(), TagPeer.doSelectByName(task.getTagAsList(), con).size());
	}
	public void testAdd_statusIdが不正な場合はUnknownに() throws Exception {
		TaskBean task = prepareValidTask();
		task.setStatusId(-1);
		
		TaskBean result = manager.add(task);
		assertEquals(Status.UNKNOWN, result.getStatusId());
	}
	
	public void testAddComment_COMMENTテーブルにレコードが追加されていること() throws Exception {
		Thread.sleep(500);
		TaskCommentBean comment = prepareValidComment();
		
		TaskBean result = manager.addComment(1001, comment);
		
		assertEqualsDb(result);
		assertTrue(result.getUpdateDate().after(date));
		
		Criteria criteria = new Criteria();
		criteria.add(TaskCommentPeer.TASK_ID, result.getTaskId());
		assertTrue(TaskCommentPeer.doSelect(criteria, con).size() > 0);
	}
	public void testAddFile_FILEテーブルにレコードが追加されていること() throws Exception {
		FileData file = new FileData("test.file1", new byte[]{1}, ".");
		
		TaskBean result = manager.addFile(1002, file);
		
		assertEqualsDb(result);
		assertTrue(result.getUpdateDate().after(date));
		
		Criteria criteria = new Criteria();
		criteria.add(AttachedFilePeer.TASK_ID, result.getTaskId());
		assertTrue(AttachedFilePeer.doSelect(criteria, con).size() > 0);
	}
	public void testAddFile_実ファイルが作成されていること() throws Exception {
		FileData file = new FileData("test.file1", new byte[]{1}, ".");
		
		TaskBean result = manager.addFile(1002, file);
		
		Criteria criteria = new Criteria();
		criteria.add(AttachedFilePeer.TASK_ID, result.getTaskId());
		List<AttachedFile> fileList = AttachedFilePeer.doSelect(criteria, con);
		for (AttachedFile dbFile : fileList) {
			File sysFile = new File(dbFile.getPath());
			assertTrue(sysFile.exists());
		}
	}
	public void testAddFile_ファイルがnullの場合は何もしない() throws Exception {
		TaskBean result = manager.addFile(1002, null);
		
		Criteria criteria = new Criteria();
		criteria.add(AttachedFilePeer.TASK_ID, result.getTaskId());
		List<AttachedFile> fileList = AttachedFilePeer.doSelect(criteria, con);
		assertEquals(0, fileList.size());
	}
	public void testAddFile_ファイルが空の場合は通常通り() throws Exception {
		FileData file = new FileData("test.file1", new byte[]{}, ".");
		
		TaskBean result = manager.addFile(1002, file);
		
		Criteria criteria = new Criteria();
		criteria.add(AttachedFilePeer.TASK_ID, result.getTaskId());
		List<AttachedFile> fileList = AttachedFilePeer.doSelect(criteria, con);
		assertTrue(AttachedFilePeer.doSelect(criteria, con).size() > 0);
		
		for (AttachedFile dbFile : fileList) {
			File sysFile = new File(dbFile.getPath());
			assertTrue(sysFile.exists());
		}
	}
	public void testAddLink_TASK_RELATIONにレコードが追加されていること() throws Exception {
		manager.link(1001, 1002);
		
		Criteria criteria = new Criteria();
		criteria.add(TaskRelationPeer.SRC_TASK_ID, 1001);
		criteria.add(TaskRelationPeer.DEST_TASK_ID, 1002);
		
		assertTrue(TaskRelationPeer.doSelect(criteria, con).size() > 0);
	}
	public void testAddLink_既にリンクが存在する場合は何もしない() throws Exception {
		manager.link(1001, 1002);
		
		Criteria criteria = new Criteria();
		criteria.add(TaskRelationPeer.SRC_TASK_ID, 1001);
		criteria.add(TaskRelationPeer.DEST_TASK_ID, 1002);
		int count = TaskRelationPeer.doSelect(criteria, con).size();
		
		manager.link(1001, 1002);
		assertEquals(count, TaskRelationPeer.doSelect(criteria, con).size());
	}
	public void testAddLink_リンク元タスクが存在しない場合はNotFound() throws Exception {
		try {
			manager.link(0, 1002);
		} catch (NotFoundException e) {
			return;
		}
		fail();
	}
	public void testAddLink_リンク先タスクが存在しない場合はNotFound() throws Exception {
		try {
			manager.link(1001, 0);
		} catch (NotFoundException e) {
			return;
		}
		fail();
	}
	public void testAddLink_リンク先とリンク元が同じ場合は何もしない() throws Exception {
		manager.link(1001, 1001);
		Criteria criteria = new Criteria();
		criteria.add(TaskRelationPeer.SRC_TASK_ID, 1001);
		criteria.add(TaskRelationPeer.DEST_TASK_ID, 1001);
		assertEquals(0, TaskRelationPeer.doSelect(criteria, con).size());
	}
	
	public void testDeleteLink_TASK_RELATIONからレコードを削除する() throws Exception {
		manager.link(1001, 1002);
		List<Integer> linkedIdList = getLinkedTaskIdList(1001);
		assertTrue(linkedIdList.contains(1002));
		
		manager.unlink(1001, 1002);
		linkedIdList = getLinkedTaskIdList(1001);
		assertFalse(linkedIdList.contains(1002));
	}
	public void testDeleteLink_存在しないリンクの場合は何もしない() throws Exception {
		List<Integer> linkedIdList = getLinkedTaskIdList(1001);
		assertFalse(linkedIdList.contains(1002));
		
		manager.unlink(1001, 1002);
	}
	public void testDeleteLink_TASK_SRCとDESTの順はどちらでもよい() throws Exception {
		manager.link(1001, 1002);
		manager.unlink(1002, 1001);
		List<Integer> linkedIdList = getLinkedTaskIdList(1001);
		assertFalse(linkedIdList.contains(1002));
	}
	public void testAdd_まとめて追加_TASKテーブルにレコードが追加されていること2() throws Exception {
		TaskBean task = prepareValidTask();
		FileData file = new FileData("test.file1", new byte[]{1}, ".");
		
		TaskBean result = manager.add(task, file);
		
		TaskPeer.retrieveByPK(result.getTaskId(), con);
	}
	public void testAdd_まとめて追加_FILEテーブルにレコードが追加されていること() throws Exception {
		TaskBean task = prepareValidTask();
		FileData file = new FileData("test.file1", new byte[]{1}, ".");
		
		TaskBean result = manager.add(task, file);
		
		Criteria criteria = new Criteria();
		criteria.add(AttachedFilePeer.TASK_ID, result.getTaskId());
		assertTrue(AttachedFilePeer.doSelect(criteria, con).size() > 0);
	}
	public void testAdd_まとめて追加_実ファイルが作成されていること() throws Exception {
		TaskBean task = prepareValidTask();
		FileData file = new FileData("test.file1", new byte[]{1}, ".");
		
		TaskBean result = manager.add(task, file);
		
		Criteria criteria = new Criteria();
		criteria.add(AttachedFilePeer.TASK_ID, result.getTaskId());
		List<AttachedFile> fileList = AttachedFilePeer.doSelect(criteria, con);
		for (AttachedFile dbFile : fileList) {
			File sysFile = new File(dbFile.getPath());
			assertTrue(sysFile.exists());
		}
	}
	public void testAdd_まとめて追加_fileがnullでもちゃんと動くこと() throws Exception {
		TaskBean task = prepareValidTask();
		
		TaskBean result = manager.add(task, null);
		
		Task dbTask = TaskPeer.retrieveByPK(result.getTaskId(), con);
		assertEquals(0, dbTask.getAttachedFiles(con).size());
	}
	public void testAdd_まとめて追加_存在しない場合はTAGテーブルにもレコードを追加する() throws Exception {
		TaskBean task = prepareValidTask();
		
		assertEquals(0, TagPeer.doSelectByName(task.getTagAsList(), con).size());

		manager.add(task, null);
		
		assertTrue(task.getTagAsList().size() > 0);
		assertEquals(task.getTagAsList().size(), TagPeer.doSelectByName(task.getTagAsList(), con).size());
	}
	public void testAdd_まとめて追加_statusIdが不正な場合はUnknownに() throws Exception {
		TaskBean task = prepareValidTask();
		task.setStatusId(-1);
		
		TaskBean result = manager.add(task, null);
		assertEquals(Status.UNKNOWN, result.getStatusId());
	}
	public void testAddTag_指定のタグがTaskに追加されていること() throws Exception {
		Thread.sleep(1000);
		TaskBean task = manager.get(1001);
		manager.addTag(task.getTaskId(), "テストのタグ");
		
		TaskBean result = manager.get(task.getTaskId()); 
		assertEquals(task.getTagList()+Tag.format("テストのタグ"), 
				result.getTagList());
		
		assertTrue(result.getUpdateDate().after(date));
	}
	public void testAddTag_指定のタグが空やnullの場合何もしない() throws Exception {
		TaskBean task = manager.get(1001);
		manager.addTag(task.getTaskId(), "");
		assertEquals(task.getTagList(), manager.get(task.getTaskId()).getTagList());

		manager.addTag(task.getTaskId(), null);
		assertEquals(task.getTagList(), manager.get(task.getTaskId()).getTagList());
	}
	public void testAddTag_タスクが存在しない場合はNotFoundException() throws Exception {
		try {
			manager.addTag(0, "タグ");
		} catch (NotFoundException e) {
			return;
		}
		fail();
	}
	public void testDeleteTag_指定のタグがタスクから削除されていること() throws Exception {
		TaskBean task = manager.get(1001);
		String testTag = task.getTagAsList().get(0);
		
		manager.deleteTag(task.getTaskId(), testTag);
		
		TaskBean result = manager.get(task.getTaskId());
		for (String tag : result.getTagAsList()) {
			assertFalse(StringUtils.equals(testTag, tag));
		}
	}
	public void testDeleteTag_存在しないタグが指定された場合は何もしない() throws Exception {
		TaskBean task = manager.get(1001);
		manager.deleteTag(task.getTaskId(), "存在しないタグ");
		
		TaskBean result = manager.get(task.getTaskId());
		assertEquals(task.getTagList(), result.getTagList());
	}
	public void testDeleteTag_指定のタグがnullや空文字の場合は何もしない() throws Exception {
		TaskBean task = manager.get(1001);
		
		manager.deleteTag(task.getTaskId(), "");
		TaskBean result = manager.get(task.getTaskId());
		assertEquals(task.getTagList(), result.getTagList());

		manager.deleteTag(task.getTaskId(), null);
		result = manager.get(task.getTaskId());
		assertEquals(task.getTagList(), result.getTagList());
	}
	public void testDeleteTag_タスクが存在しない場合はNotFoundException() throws Exception {
		try {
			manager.deleteTag(0, "タグ");
		} catch (NotFoundException e) {
			return;
		}
		fail();
	}
	
	public void testUpdateStatus_指定のタスクが指定の状態になること() throws Exception {
		Thread.sleep(1000);
		manager.updateStatus(1001, Status.FINISHED);
		TaskBean result = manager.get(1001);
		assertEquals(Status.FINISHED, result.getStatusId());
		assertTrue(result.getUpdateDate().after(date));

		assertEqualsDb(result);
	}
	public void testUpdateStatus_指定のタスクが存在しない場合はNotFoundException() throws Exception {
		try {
			manager.updateStatus(0, Status.FINISHED);
		} catch (NotFoundException e) {
			return;
		}
		fail();
	}
	public void testUpdateStatus_指定の状態が存在しない場合は何もしない() throws Exception {
		TaskBean task = manager.get(1001);
		int original = task.getStatusId();
		task.setStatusId(-1);
		manager.updateStatus(task.getTaskId(), -1);
		assertEquals(original, manager.get(task.getTaskId()).getStatusId());
	}
	
	public void testUpdate_まとめて更新_TASKテーブルのレコードを更新する() throws Exception {
		TaskBean task = manager.add(prepareValidTask());
		task.setContents("変更後");
		task.setTagList("[変更後のテスト]");
		TaskCommentBean comment = prepareValidComment();
		FileData file = new FileData("test.file1", new byte[]{1}, ".");
		
		TaskBean result = manager.update(task, comment, file);
		assertEquals(task.getContents(), result.getContents());
		assertEquals(task.getTagList(), result.getTagList());
	}

	public void testUpdate_まとめて更新_新しいタグの場合はTAGテーブルにレコードを追加する() throws Exception {
		TaskBean task = manager.add(prepareValidTask());
		task.setContents("変更後");
		task.setTagList("[変更後のテスト]");
		TaskCommentBean comment = prepareValidComment();
		FileData file = new FileData("test.file1", new byte[]{1}, ".");
		
		manager.update(task, comment, file);
		
		Criteria criteria = new Criteria();
		criteria.add(TagPeer.NAME, task.getTagAsList().get(0));
		assertEquals(1, TagPeer.doSelect(criteria, con).size());
	}
	public void testUpdate_まとめて更新_TASK_COMMENTテーブルにレコードが追加されていること() throws Exception {
		TaskBean task = manager.add(prepareValidTask());
		task.setContents("変更後");
		task.setTagList("[変更後のテスト]");
		TaskCommentBean comment = prepareValidComment();
		FileData file = new FileData("test.file1", new byte[]{1}, ".");
		
		TaskBean result = manager.update(task, comment, file);
		assertEquals(1, manager.get(result.getTaskId()).getTaskCommentBeans().size());
	}
	public void testUpdate_まとめて更新_FILEテーブルとTASK_FILE_RELATIONにレコードが追加されていること() throws Exception {
		TaskBean task = manager.add(prepareValidTask());
		task.setContents("変更後");
		task.setTagList("[変更後のテスト]");
		TaskCommentBean comment = prepareValidComment();
		FileData file = new FileData("test.file1", new byte[]{1}, ".");
		
		TaskBean result = manager.update(task, comment, file);
		assertEquals(1, manager.get(result.getTaskId()).getAttachedFileBeans().size());
	}
	public void testUpdate_まとめて更新_実ファイルが作成されていること() throws Exception {
		TaskBean task = manager.add(prepareValidTask());
		task.setContents("変更後");
		task.setTagList("[変更後のテスト]");
		TaskCommentBean comment = prepareValidComment();
		FileData file = new FileData("test.file1", new byte[]{1}, ".");
		
		manager.update(task, comment, file);
		File sysFile = new File(
				manager.get(task.getTaskId()).getAttachedFileBeans().get(0).getPath());
		assertTrue(sysFile.exists());
	}
	public void testUpdate_まとめて更新_コメントがnullの場合もOK() throws Exception {
		TaskBean task = manager.add(prepareValidTask());
		FileData file = new FileData("test.file1", new byte[]{1}, ".");
		
		TaskBean result = manager.update(task, null, file);
		assertEquals(0, manager.get(result.getTaskId()).getTaskCommentBeans().size());
	}
	public void testUpdate_まとめて更新_ファイルがnullの場合もOK() throws Exception {
		TaskBean task = manager.add(prepareValidTask());
		TaskCommentBean comment = prepareValidComment();
		
		TaskBean result = manager.update(task, comment, null);
		assertEquals(0, manager.get(result.getTaskId()).getAttachedFileBeans().size());
	}
	public void testUpdate_まとめて更新_statusIdが不正な場合はstatusは元のまま() throws Exception {
		TaskBean task = manager.add(prepareValidTask());
		int expected = task.getStatusId();
		task.setStatusId(-1);
		
		TaskBean result = manager.update(task, null, null);
		assertEquals(expected, result.getStatusId());
	}
	public void testUpdate_まとめて更新_対象のタスクが存在しない場合はNotFoundException() throws Exception {
		TaskBean task = prepareValidTask();
		task.setTaskId(0);
		
		try {
			manager.update(task, null, null);
		} catch (NotFoundException e) {
			return;
		}
		fail();
	}

	public void testUpdate_TASKテーブルのレコードを更新する() throws Exception {
		Thread.sleep(1000);
		TaskBean task = prepareValidTask();
		task.setTaskId(1001);
		TaskBean result = null;

		result = manager.update(task);
		
		assertTrue(result.getTaskId() != 0);
		Task dbTask = TaskPeer.retrieveByPK(result.getTaskId(), con);
		
		assertEquals(task.getContents(), result.getContents());
		assertEquals(task.getEstimatedManHour(), result.getEstimatedManHour());
		assertEquals(task.getStatusId(), result.getStatusId());
		assertEquals(task.getTagList(), result.getTagList());
		assertNotNull(result.getUpdateDate());

		assertEquals(result.getContents(), dbTask.getContents());
		assertEquals(result.getEstimatedManHour(), dbTask.getEstimatedManHour());
		assertEquals(result.getStatusId(), dbTask.getStatusId());
		assertEquals(result.getTagList(), dbTask.getTagList());
		assertEquals(result.getUpdateDate(), dbTask.getUpdateDate());
		
		assertTrue(result.getUpdateDate().after(date));
	}
	public void testUpdate_存在しない場合はTAGテーブルにもレコードを追加する() throws Exception {
		TaskBean task = prepareValidTask();
		task.setTaskId(1001);
		
		assertEquals(0, TagPeer.doSelectByName(task.getTagAsList(), con).size());

		manager.update(task);
			
		assertTrue(task.getTagAsList().size() > 0);
		assertEquals(task.getTagAsList().size(), TagPeer.doSelectByName(task.getTagAsList(), con).size());
	}
	public void testUpdate_statusIdが不正な場合はstatusを元のままに() throws Exception {
		TaskBean task = prepareValidTask();
		int expected = task.getStatusId();
		task.setTaskId(1001);
		task.setStatusId(-1);
		
		TaskBean result = manager.update(task);
		assertEquals(expected, result.getStatusId());
	}
	public void testUpdate_対象のタスクが存在しない場合はNotFoundException() throws Exception {
		TaskBean task = prepareValidTask();
		task.setTaskId(0);
		
		try {
			manager.update(task);
		} catch (NotFoundException e) {
			return;
		}
		fail();
	}
	private TaskBean prepareValidTask() {
		TaskBean task = new TaskBean();
		task.setContents("タスクの内容");
		task.setEstimatedManHour(10);
		task.setStatusId(Status.FINISHED);
		task.setTagList("[タスク追加テスト１][タスク追加テスト2]");
		return task;
	}
	
	private TaskCommentBean prepareValidComment() {
		TaskCommentBean comment = new TaskCommentBean();
		comment.setCommentator("人");
		comment.setContents("コメント");
		return comment;
	}

	public void testUpdateTagList_TASKテーブルのレコードを更新する() throws Exception {
		Thread.sleep(1000);
		TaskBean result = manager.updateTagList(1001, "[タグ更新テスト１][タグ更新テスト２]");
		
		assertTrue(result.getTaskId() != 0);
		assertTrue(result.getUpdateDate().after(date));
		assertEquals("[タグ更新テスト１][タグ更新テスト２]", result.getTagList());
		
		assertEqualsDb(result);
	}

	private void assertEqualsDb(TaskBean result) throws TorqueException, NoRowsException, TooManyRowsException {
		Task dbTask = TaskPeer.retrieveByPK(result.getTaskId(), con);
		assertEquals(result.getContents(), dbTask.getContents());
		assertEquals(result.getEstimatedManHour(), dbTask.getEstimatedManHour());
		assertEquals(result.getStatusId(), dbTask.getStatusId());
		assertEquals(result.getTagList(), dbTask.getTagList());
		assertEquals(result.getUpdateDate(), dbTask.getUpdateDate());
	}
	public void testUpdateTagList_存在しない場合はTAGテーブルにもレコードを追加する() throws Exception {
		String tag = "[タグ更新テスト１][タグ更新テスト２]";
		List<String> tagList = TaskBean.parseTagList(tag);
		assertEquals(0, TagPeer.doSelectByName(tagList, con).size());

		manager.updateTagList(1001, tag);
			
		assertTrue(tagList.size() > 0);
		assertEquals(tagList.size(), TagPeer.doSelectByName(tagList, con).size());
	}
	public void testUpdateTagList_対象のタスクが存在しない場合はNotFoundException() throws Exception {
		try {
			manager.updateTagList(0, "");
		} catch (NotFoundException e) {
			return;
		}
		fail();
	}
	public void testUpdateEstimatedManHour_TASKテーブルのレコードを更新する() throws Exception {
		Thread.sleep(1000);
		int taskId = 1001;
		double manHour = 99.99;
		TaskBean result = manager.updateEstimatedManHour(taskId, manHour);
		
		assertTrue(result.getTaskId() != 0);
		assertEquals(manHour, result.getEstimatedManHour());
		assertTrue(result.getUpdateDate().after(date));
		
		assertEqualsDb(result);
	}
	public void testUpdateEstimatedManHour_対象のタスクが存在しない場合はNotFoundException() throws Exception {
		try {
			manager.updateEstimatedManHour(0, 99.99);
		} catch (NotFoundException e) {
			return;
		}
		fail();
	}
	public void testUpdateContents_TASKテーブルのレコードを更新する() throws Exception {
		Thread.sleep(1000);
		int taskId = 1001;
		String contents = "コンテンツ更新テスト";
		
		TaskBean result = manager.updateContents(taskId, contents);
		
		assertTrue(result.getTaskId() != 0);
		assertEquals(contents, result.getContents());
		assertTrue(result.getUpdateDate().after(date));

		assertEqualsDb(result);
	}
	public void testUpdateContents_対象のタスクが存在しない場合はNotFoundException() throws Exception {
		try {
			manager.updateContents(0, "コンテンツ更新テスト");
		} catch (NotFoundException e) {
			return;
		}
		fail();
	}

	public void testSearchDetail_詳細検索はコメントも同時に返す() throws Exception {
		TaskQuery query = new TaskQuery();
		Pager pager = new Pager();
		pager.setLimit(2);
		pager.setOffset(0);
		
		List<TaskBean> result = manager.searchDetail(query, pager);

		assertEquals(2, result.size());
		for (TaskBean task : result) {
			assertNotNull(task.getTaskCommentBeans());
		}
	}
	public void testSearchDetail_検索結果が0件の場合はサイズ0のリストを返す() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setStatus(new int[]{-1});
		
		List<TaskBean> result = manager.searchDetail(query, null);

		assertEquals(0, result.size());
	}

	public void testSearch_Pagerがある場合は指定のリミット数の検索結果を返す() throws Exception {
		TaskQuery query = new TaskQuery();
		Pager pager = new Pager();
		pager.setLimit(2);
		pager.setOffset(0);
		
		List<TaskBean> result = manager.search(query, pager);

		assertEquals(2, result.size());
	}
	public void testSearch_Pagerがある場合は指定のオフセット以降の検索結果を返す() throws Exception {
		TaskQuery query = new TaskQuery();
		Pager pager = new Pager();
		pager.setLimit(2);
		pager.setOffset(0);
		List<TaskBean> expected = manager.search(query, pager);

		pager = new Pager();
		pager.setLimit(2);
		pager.setOffset(1);
		List<TaskBean> result = manager.search(query, pager);

		assertEquals(expected.get(1).getTaskId(), result.get(0).getTaskId());
	}
	public void testSearch_Pagerがある場合は全検索結果数をrowCountとして返す() throws Exception {
		TaskQuery query = new TaskQuery();
		Pager pager = new Pager();
		pager.setLimit(2);
		pager.setOffset(0);
		
		manager.search(query, pager);
		
		assertEquals(getAllRowCount(), pager.getRowCount());
	}
	private int getAllRowCount() throws Exception {
		Criteria criteria = new Criteria();
		criteria.addJoin(TaskPeer.STATUS_ID, StatusPeer.STATUS_ID);
		return TaskPeer.doSelectCount(criteria, con);
	}
	public void testSearch_検索結果をSTATUS順にソートできる() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setOrder(TaskQuery.ORDER_BY_STATUS_ASC);
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		int sortOrder = 0;
		for (TaskBean task : result) {
			assertTrue(sortOrder <= task.getStatusBean().getSortOrder());
			sortOrder = task.getStatusBean().getSortOrder();
		}
	}
	public void testSearch_検索結果をID順にソートできる() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setOrder(TaskQuery.ORDER_BY_ID_ASC);
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		int sortOrder = 0;
		for (TaskBean task : result) {
			assertTrue(sortOrder <= task.getTaskId());
			sortOrder = task.getTaskId();
		}
	}
	public void testSearch_検索結果を見積時間順にソートできる() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setOrder(TaskQuery.ORDER_BY_MAN_HOUR_ASC);
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		double sortOrder = 0;
		for (TaskBean task : result) {
			assertTrue(sortOrder <= task.getEstimatedManHour());
			sortOrder = task.getEstimatedManHour();
		}
	}
	public void testSearch_検索結果を更新日時順にソートできる() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setOrder(TaskQuery.ORDER_BY_DATE_ASC);
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		Date sortOrder = DateUtils.parse(1974, 1, 1);
		for (TaskBean task : result) {
			assertTrue(sortOrder.getTime() <= task.getUpdateDate().getTime());
			sortOrder = task.getUpdateDate();
		}
	}
	public void testSearch_結果はStatusBeanも含む() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setStatus(new int[]{Status.ON_GOING});
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		for (TaskBean task : result) {
			assertNotNull(task.getStatusBean());
		}
	}
	public void testSearch_状態を条件に検索ができる() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setStatus(new int[]{Status.ON_GOING});
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		for (TaskBean task : result) {
			assertEquals(Status.ON_GOING, task.getStatusId());
		}
	}
	public void testSearch_複数の状態条件はOR検索() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setStatus(new int[]{Status.ON_GOING, Status.RAPID});
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		boolean rapidFlag = false;
		boolean onGoingFlag = false;
		for (TaskBean task : result) {
			assertTrue(
					task.getStatusId() == Status.ON_GOING ||
					task.getStatusId() == Status.RAPID);
			switch (task.getStatusId()) {
			case Status.ON_GOING:
				onGoingFlag = true;
				break;
			case Status.RAPID:
				rapidFlag = true;
				break;
			default:
				fail();
			}
		}
		assertTrue(rapidFlag);
		assertTrue(onGoingFlag);
	}
	public void testSearch_タグ条件は完全一致検索() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setTag(new String[]{"タグ条件"});
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		for (TaskBean task : result) {
			boolean tagFlag = false;
			for (String tag : task.getTagAsList()) {
				if (tag.toUpperCase().equals("タグ条件")) {
					tagFlag = true;
				}
			}
			assertTrue(tagFlag);
		}
	}
	public void testSearch_複数タグ条件はAND検索() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setTag(new String[]{"タグ条件", "タグ条件2"});
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		for (TaskBean task : result) {
			boolean tagFlag1 = false;
			boolean tagFlag2 = false;
			for (String tag : task.getTagAsList()) {
				if (tag.toUpperCase().equals("タグ条件")) {
					tagFlag1 = true;
				}
				if (tag.toUpperCase().equals("タグ条件2")) {
					tagFlag2 = true;
				}
			}
			assertTrue(tagFlag1 && tagFlag2);
		}
	}
	public void testSearch_除外タグ条件は完全一致検索() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setExcludeTag(new String[]{"タグ条件"});
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		for (TaskBean task : result) {
			for (String tag : task.getTagAsList()) {
				assertFalse(StringUtils.equals(StringUtils.upperCase(tag), "タグ条件"));
			}
		}
	}
	public void testSearch_除外複数タグ条件はOR検索() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setExcludeTag(new String[]{"タグ条件", "タグ条件2"});
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		for (TaskBean task : result) {
			for (String tag : task.getTagAsList()) {
				assertFalse(StringUtils.equals(StringUtils.upperCase(tag), "タグ条件"));
				assertFalse(StringUtils.equals(StringUtils.upperCase(tag), "タグ条件2"));
			}
		}
	}
	public void testSearch_キーワード条件はタスクIDとタスク内容とタグが対象の部分一致検索() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setKeyword("検索条件");
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		for (TaskBean task : result) {
			assertTrue(
					StringUtils.indexOf(task.getContents(), "検索条件") >= 0 ||
					StringUtils.indexOf(task.getTagList(), "検索条件") >= 0 || 
					StringUtils.indexOf(Integer.toString(task.getTaskId()), "検索条件") >= 0);
		}
	}
	public void testSearch_キーワード条件はタスクIDも検索対象() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setKeyword("1003");
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		boolean taskIdSearched = false;
		for (TaskBean task : result) {
			taskIdSearched = taskIdSearched | (StringUtils.indexOf(Integer.toString(task.getTaskId()), "3") >= 0);
		}
	}
	public void testSearch_空白区切りのキーワード条件は複数キーワードのAND条件() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setKeyword("検索条件 やつ");
		
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		for (TaskBean task : result) {
			assertTrue(
					StringUtils.indexOf(task.getContents(), "検索条件") >= 0 ||
					StringUtils.indexOf(task.getTagList(), "検索条件") >= 0);
			assertTrue(
					StringUtils.indexOf(task.getContents(), "やつ") >= 0 ||
					StringUtils.indexOf(task.getTagList(), "やつ") >= 0);
		}
	}
	public void testSearch_最古更新日条件で検索できること() throws Exception {
		TaskQuery query = new TaskQuery();
		query.setDateFrom(new Date());
		
		assertEquals(0, manager.search(query, null).size());
		
		TaskBean task = prepareValidTask();
		manager.add(task);
		
		List<TaskBean> result = manager.search(query, null);
		assertTrue(result.size() > 0);
		for (TaskBean bean : result) {
			assertTrue(bean.getUpdateDate().getTime() > query.getDateFrom().getTime());
		}
	}
	public void testSearchUnlinkedTask_関連タスクと自分自身は検索対象外() throws Exception {
		int taskId = 1001;
		TaskQuery query = new TaskQuery();
		query.setUnlinkedTaskId(taskId);
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
		List<Integer> idList = getLinkedTaskIdList(taskId);
		for (TaskBean task : result) {
			assertTrue(!idList.contains(task.getTaskId()));
			assertTrue(task.getTaskId() != taskId);
		}
	}
	public void testSearchUnlinkedTask_関連タスクがない場合も検索可能() throws Exception {
		int taskId = 1002;
		TaskQuery query = new TaskQuery();
		query.setUnlinkedTaskId(taskId);
		List<TaskBean> result = manager.search(query, null);

		assertTrue(result.size() > 0);
	}
}
