OC.L10N.register( "core", { "Please select a file." : "파일을 선택하십시오.", "File is too big" : "파일이 너무 큼", "The selected file is not an image." : "선택한 파일은 사진이 아닙니다.", "The selected file cannot be read." : "선택한 파일을 읽을 수 없습니다.", "The file was uploaded" : "파일을 업로드함", "The uploaded file exceeds the upload_max_filesize directive in php.ini" : "업로드한 파일의 크기가 php.ini의 upload_max_filesize를 초과함", "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "업로드한 파일의 크기가 HTML 폼에 지정한 MAX_FILE_SIZE를 초과함", "The file was only partially uploaded" : "파일이 일부분만 업로드됨", "No file was uploaded" : "업로드한 파일 없음", "Missing a temporary folder" : "임시 폴더 없음", "Could not write file to disk" : "디스크에 파일을 쓸 수 없음", "A PHP extension stopped the file upload" : "PHP 확장 기능에서 파일 업로드를 차단함", "Invalid file provided" : "잘못된 파일 지정됨", "No image or file provided" : "사진이나 파일이 없음", "Unknown filetype" : "알려지지 않은 파일 형식", "Invalid image" : "잘못된 사진", "An error occurred. Please contact your admin." : "오류가 발생했습니다. 시스템 관리자에게 연락하십시오.", "No temporary profile picture available, try again" : "사용 가능한 프로필 사진이 없습니다. 다시 시도하십시오.", "No crop data provided" : "선택된 데이터가 없습니다.", "No valid crop data provided" : "올바른 잘라내기 데이터가 지정되지 않음", "Crop is not square" : "잘라내는 영역이 사각형이 아님", "State token does not match" : "상태 토큰이 일치하지 않음", "Invalid app password" : "무효한 앱 비밀번호", "Could not complete login" : "로그인을 할 수 없음", "Your login token is invalid or has expired" : "로그인 토큰이 잘못되었거나 만료되었습니다", "Password reset is disabled" : "암호 재설정이 비활성화됨", "%s password reset" : "%s 암호 재설정", "Password reset" : "암호 재설정", "Click the following button to reset your password. If you have not requested the password reset, then ignore this email." : "아래 단추를 눌러서 암호를 재설정할 수 있습니다. 암호 초기화를 요청하지 않으셨다면 이 이메일을 무시하십시오.", "Click the following link to reset your password. If you have not requested the password reset, then ignore this email." : "아래 링크를 눌러서 암호를 재설정할 수 있습니다. 암호 초기화를 요청하지 않으셨다면 이 이메일을 무시하십시오.", "Reset your password" : "내 암호 재설정", "Nextcloud Server" : "Nextcloud 서버", "Some of your link shares have been removed" : "일부 링크 공유가 삭제됨", "Due to a security bug we had to remove some of your link shares. Please see the link for more information." : "보안 버그로 인하여 일부 링크 공유를 삭제했습니다. 링크를 눌러서 더 많은 정보를 볼 수 있습니다.", "The user limit of this instance is reached." : "이 인스턴스의 사용자수 한계에 도달함.", "Enter your subscription key to increase the user limit. For more information about Nextcloud Enterprise see our website." : "사용자수 제한을 해제하기 위해 구독키를 입력하세요. 기업용 Nextcloud에 대해 자세히 알고 싶다면, 저희 웹사이트를 방문하세요.", "Preparing update" : "업데이트 준비 중", "[%d / %d]: %s" : "[%d / %d]: %s", "Repair step:" : "수리 단계:", "Repair info:" : "수리 정보:", "Repair warning:" : "수리 경고:", "Repair error:" : "수리 오류:", "Please use the command line updater because automatic updating is disabled in the config.php." : "config.php에서 자동 업데이트가 비활성화되어 있기 때문에 명령줄 업데이트를 사용하십시오.", "[%d / %d]: Checking table %s" : "[%d / %d]: 테이블 %s 확인 중", "Turned on maintenance mode" : "유지 보수 모드 켜짐", "Turned off maintenance mode" : "유지 보수 모드 꺼짐", "Maintenance mode is kept active" : "유지 보수 모드가 켜져 있음", "Updating database schema" : "데이터베이스 스키마 업데이트 중", "Updated database" : "데이터베이스 업데이트됨", "Checking for update of app \"%s\" in App Store" : "앱 스토어에서 \"%s\" 앱의 업데이트를 확인하는 중", "Update app \"%s\" from App Store" : "앱 스토어에서 \"%s\" 앱 업데이트", "Checked for update of app \"%s\" in App Store" : "앱 스토어에서 \"%s\" 앱 업데이트 확인함", "Checking whether the database schema for %s can be updated (this can take a long time depending on the database size)" : "%s의 데이터베이스 스키마 업데이트 가능 여부 확인 중(데이터베이스 크기에 따라서 오래 걸릴 수도 있습니다)", "Updated \"%1$s\" to %2$s" : "\"%1$s\"을(를) %2$s(으)로 업데이트함", "Set log level to debug" : "로그 단계를 디버그로 설정", "Reset log level" : "로그 단계 초기화", "Starting code integrity check" : "코드 무결성 검사 시작 중", "Finished code integrity check" : "코드 무결성 검사 완료됨", "%s (incompatible)" : "%s(호환 불가)", "The following apps have been disabled: %s" : "다음 앱이 비활성화되었습니다: %s", "Already up to date" : "최신 상태임", "Your web server is not yet properly set up to allow file synchronization, because the WebDAV interface seems to be broken." : "WebDAV 인터페이스를 사용할 수 없어서 웹 서버에서 파일 동기화를 사용할 수 있도록 설정할 수 없습니다.", "Your web server is not properly set up to resolve \"{url}\". Further information can be found in the {linkstart}documentation ↗{linkend}." : "웹 서버에서 \"{url}\"을(를) 올바르게 처리할 수 없습니다. 더 많은 정보를 보려면 {linkstart}문서 ↗{linkend}를 참고하십시오.", "PHP does not seem to be setup properly to query system environment variables. The test with getenv(\"PATH\") only returns an empty response." : "PHP에서 시스템 환경 변수를 올바르게 조회할 수 없는 것 같습니다. getenv(\"PATH\") 시험 결과 빈 값이 반환되었습니다.", "The read-only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update." : "읽기 전용 설정이 활성화되었습니다. 이 상태에서는 웹 인터페이스를 통하여 일부 설정을 변경할 수 없습니다. 또한 매 업데이트마다 파일을 쓸 수 있는 상태로 변경해야 합니다.", "Your database does not run with \"READ COMMITTED\" transaction isolation level. This can cause problems when multiple actions are executed in parallel." : "데이터베이스가 \"READ COMMITTED\" 트랜잭션 격리 수준에서 실행되고 있지 않습니다. 여러 작업이 동시에 실행될 때 문제가 발생할 수 있습니다.", "The PHP module \"fileinfo\" is missing. It is strongly recommended to enable this module to get the best results with MIME type detection." : "PHP의 \"fileinfo\" 모듈이 없습니다. 올바른 MIME 형식 감지를 위해서 이 모듈을 활성화하는 것을 추천합니다.", "If your installation is not installed at the root of the domain and uses system cron, there can be issues with the URL generation. To avoid these problems, please set the \"overwrite.cli.url\" option in your config.php file to the webroot path of your installation (suggestion: \"{suggestedOverwriteCliURL}\")" : "도메인의 루트 경로에 설치되어 있지 않고 시스템 Cron을 사용한다면 URL 생성에 문제가 발생할 수도 있습니다. 이 문제를 예방하려면 config.php의 \"overwrite.cli.url\" 옵션을 설치본의 웹 루트 경로로 설정하십시오(제안: \"{suggestedOverwriteCliURL}\")", "Your installation has no default phone region set. This is required to validate phone numbers in the profile settings without a country code. To allow numbers without a country code, please add \"default_phone_region\" with the respective {linkstart}ISO 3166-1 code ↗{linkend} of the region to your config file." : "당신의 설치에서 기본 국가 번호가 설정되지 않았습니다. 프로필 설정에서 국가 번호 없이 전화번호를 사용하기 위해서 이 설정이 필요합니다. 국가 번호 없이 전화번호를 사용하게 하려면, 지역의 {linkstart}ISO 3166-1 코드↗{linkend}를 참조하여 설정 파일에 \"default_phone_region\"을 추가하십시오.", "It was not possible to execute the cron job via CLI. The following technical errors have appeared:" : "CLI로 cron 작업을 실행시킬 수 없었습니다. 다음 오류가 발생했습니다:", "This server has no working Internet connection: Multiple endpoints could not be reached. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. Establish a connection from this server to the Internet to enjoy all features." : "웹 서버에서 인터넷에 연결할 수 없습니다. 여러 종단점에 접근할 수 없습니다. 외부 저장소 마운트, 업데이트 알림, 추가 앱 설치 등 기능이 작동하지 않을 것입니다. 원격으로 파일에 접근하거나 알림 이메일을 보내지 못할 수도 있습니다. 모든 기능을 사용하려면 이 서버를 인터넷에 연결하십시오.", "No memory cache has been configured. To enhance performance, please configure a memcache, if available. Further information can be found in the {linkstart}documentation ↗{linkend}." : "매모리 캐시가 설정되지 않았습니다. 성능 향상을 위해 가능하면 memcache를 설정하십시오. 더 많은 정보는 {linkstart}문서 ↗{linkend}를 참조하십시오.", "You are currently running PHP {version}. Upgrade your PHP version to take advantage of {linkstart}performance and security updates provided by the PHP Group ↗{linkend} as soon as your distribution supports it." : "현재 PHP {version}(으)로 실행중입니다. PHP 버전을 업그레이드 하여 지원중인 {linkstart} PHP 그룹의 성능 및 보안 업데이트 ↗{linkend} 혜택을 누리십시오.", "Nextcloud 20 is the last release supporting PHP 7.2. Nextcloud 21 requires at least PHP 7.3." : "Nextcloud 20이 PHP 7.2를 지원하는 마지막 릴리즈입니다. Nextcloud 21은 PHP 7.3 이상이 필요합니다.", "The PHP OPcache module is not loaded. {linkstart}For better performance it is recommended ↗{linkend} to load it into your PHP installation." : "PHP OPcache 모듈이 로드되지 않았습니다. {linkstart}더 나은 성능을 위해 ↗{linkend} 이를 PHP에 로드하는 것이 권장됩니다.", "The PHP OPcache module is not properly configured. {linkstart}For better performance it is recommended ↗{linkend} to use the following settings in the php.ini:" : "PHP OPcache 모듈이 바르게 설정되지 않았습니다. {linkstart}더 나은 성능을 위해 ↗{linkend} 다음의 설정을 php.ini에서 사용하십시오:", "The PHP function \"set_time_limit\" is not available. This could result in scripts being halted mid-execution, breaking your installation. Enabling this function is strongly recommended." : "PHP 함수 \"set_time_limit\"을 사용할 수 없습니다. 스크립트가 실행 중간에 중지되어 설치를 깨트릴 수도 있습니다. 이 함수를 활성화하는 것을 추천합니다.", "Your PHP does not have FreeType support, resulting in breakage of profile pictures and the settings interface." : "PHP에 Freetype 지원이 없습니다. 프로필 사진과 설정 인터페이스가 올바르게 표시되지 않을 수도 있습니다.", "Missing index \"{indexName}\" in table \"{tableName}\"." : "테이블 \"{tableName}\"에 인덱스 \"{indexName}\"이(가) 없습니다.", "The database is missing some indexes. Due to the fact that adding indexes on big tables could take some time they were not added automatically. By running \"occ db:add-missing-indices\" those missing indexes could be added manually while the instance keeps running. Once the indexes are added queries to those tables are usually much faster." : "데이터베이스에 일부 인덱스가 없습니다. 큰 테이블에 인덱스를 추가하는 데 시간이 걸리기 때문에 자동으로 추가하지 않았습니다. 명령행에서 \"occ db:add-missing-indices\" 명령을 실행하여 인스턴스를 실행하는 동안 수동으로 인덱스를 추가할 수 있습니다. 해당 테이블에 인덱스를 추가하면 질의 속도가 다시 빨라집니다.", "Missing primary key on table \"{tableName}\"." : "\"{tableName}\" 테이블에 Primary key가 없습니다.", "This instance is missing some recommended PHP modules. For improved performance and better compatibility it is highly recommended to install them." : "이 인스턴스에 추천하는 PHP 모듈 중 일부가 존재하지 않습니다. 성능 향상과 호환성을 위하여 PHP 모듈을 설치하는 것을 추천합니다.", "Module php-imagick in this instance has no SVG support. For better compatibility it is recommended to install it." : "이 인스턴스의 모듈 php-imagick에 SVG 지원이 없습니다. 더 나은 호환성을 위해 설치를 권장합니다.", "SQLite is currently being used as the backend database. For larger installations we recommend that you switch to a different database backend." : "현재 백엔드 데이터베이스로 SQLite를 사용하고 있습니다. 대규모의 파일을 관리하려고 한다면 다른 데이터베이스 백엔드로 전환할 것을 권장합니다.", "This is particularly recommended when using the desktop client for file synchronisation." : "특히 파일 동기화를 위해 데스크톱 클라이언트를 사용할 예정인 경우 권장됩니다.", "The PHP memory limit is below the recommended value of 512MB." : "PHP 메모리 제한이 추천값인 512MB보다 작습니다.", "Some app directories are owned by a different user than the web server one. This may be the case if apps have been installed manually. Check the permissions of the following app directories:" : "일부 앱 디렉터리를 웹 서버 사용자와 다른 사용자가 소유하고 있습니다. 수동으로 앱을 설치한 경우에 발생할 수 있습니다. 다음 앱 디렉터리의 사용 권한을 확인하십시오:", "This instance uses an S3 based object store as primary storage. The uploaded files are stored temporarily on the server and thus it is recommended to have 50 GB of free space available in the temp directory of PHP. Check the logs for full details about the path and the available space. To improve this please change the temporary directory in the php.ini or make more space available in that path." : "이 인스턴스에서 S3 기반 객체 저장소를 주 저장소로 사용하고 있습니다. 업로드한 파일을 서버에 임시로 저장하기 때문에 PHP 임시 디렉터리에 최소 50 GB의 빈 공간을 두는 것을 추천합니다. 전체 경로와 사용 가능한 정보를 보려면 로그를 참조하십시오. 성능을 개선하려면 php.ini의 임시 디렉터리를 변경하거나, 해당 위치에서 사용할 수 있는 공간을 더 많이 할당하십시오.", "Error occurred while checking server setup" : "서버 설정을 확인하는 중 오류 발생", "For more details see the {linkstart}documentation ↗{linkend}." : "더 자세한 사항은 {linkstart}문서 ↗{linkend}를 참조하십시오.", "Your data directory and files are probably accessible from the Internet. The .htaccess file is not working. It is strongly recommended that you configure your web server so that the data directory is no longer accessible, or move the data directory outside the web server document root." : "데이터 디렉터리와 파일을 인터넷에서 접근할 수 있는 것 같습니다. .htaccess 파일을 사용할 수 없습니다. 웹 서버 설정을 변경하여 데이터 디렉터리에 접근할 수 없도록 하거나, 데이터 디렉터리를 웹 서버 문서 디렉터리 밖으로 옮기는 것을 추천합니다.", "The \"{header}\" HTTP header is not set to \"{expected}\". This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly." : "\"{header}\" HTTP 헤더가 \"{expected}\"(으)로 설정되어 있지 않습니다. 잠재적인 정보 유출 및 보안 위협이 될 수 있으므로 설정을 변경하는 것을 추천합니다.", "The \"{header}\" HTTP header is not set to \"{expected}\". Some features might not work correctly, as it is recommended to adjust this setting accordingly." : "\"{header}\" HTTP 헤더가 \"{expected}\"(으)로 설정되어 있지 않습니다. 일부 기능이 올바르게 작동하지 않을 수 있으므로 설정을 변경하는 것을 추천합니다.", "The \"{header}\" HTTP header doesn't contain \"{expected}\". This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly." : "\"{header}\" HTTP 헤더가 \"{expected}\"을(를) 포함하고 있지 않습니다. 잠재적인 정보 유출 및 보안 위협이 될 수 있으므로 설정을 변경하는 것을 추천합니다.", "unknown text" : "알 수 없는 텍스트", "Hello world!" : "Hello world!", "sunny" : "맑음", "Hello {name}, the weather is {weather}" : "{name} 님 안녕하세요, 오늘 날씨는 {weather}입니다", "Hello {name}" : "{name} 님 안녕하세요", "These are your search results" : "검색 결과입니다", "new" : "새로운 항목", "_download %n file_::_download %n files_" : ["파일 %n개 다운로드"], "The update is in progress, leaving this page might interrupt the process in some environments." : "업데이트 중입니다. 일부 환경에서 이 페이지를 닫을 경우 작업이 중단될 수 있습니다.", "Update to {version}" : "{version}(으)로 업데이트", "An error occurred." : "오류가 발생했습니다.", "Please reload the page." : "페이지를 새로 고치십시오.", "The update was unsuccessful. For more information check our forum post covering this issue." : "업데이트에 실패했습니다. 더 많은 정보를 보려면 이 문제에 대한 포럼 글을 참조하십시오.", "The update was unsuccessful. Please report this issue to the Nextcloud community." : "업데이트가 실패했습니다. 이 문제를 Nextcloud 커뮤니티에 보고하여 주십시오.", "_The update was successful. Redirecting you to {productName} in %n second._::_The update was successful. Redirecting you to {productName} in %n seconds._" : ["업데이트가 성공했습니다. %n초 후 {productName}(으)로 이동합니다."], "Log in" : "로그인", "Logging in …" : "로그인 중 …", "Server side authentication failed!" : "서버 인증 실패!", "Please contact your administrator." : "관리자에게 문의하십시오.", "An internal error occurred." : "내부 오류가 발생했습니다.", "Please try again or contact your administrator." : "다시 시도하거나 관리자에게 연락하십시오.", "Username or email" : "사용자 이름 또는 이메일", "Password" : "암호", "Toggle password visibility" : "암호 보이기/숨기기", "Wrong username or password." : "사용자 이름이나 암호가 잘못되었습니다.", "User disabled" : "사용자 비활성화됨", "We have detected multiple invalid login attempts from your IP. Therefore your next login is throttled up to 30 seconds." : "사용 중인 IP에서 여러 번의 잘못된 로그인 시도를 감지했습니다. 30초 후에 다시 로그인할 수 있습니다.", "Your account is not setup for passwordless login." : "당신의 계정은 비밀번호 없이 로그인하도록 설정되지 않았습니다.", "Browser not supported" : "브라우저를 지원하지 않습니다.", "Passwordless authentication is not supported in your browser." : "당신의 브라우저에서 비밀번호 없는 인증을 지원하지 않습니다.", "Your connection is not secure" : "당신의 연결이 안전하지 않습니다.", "Passwordless authentication is only available over a secure connection." : "비밀번호 없는 인증은 보안 연결에서만 사용할 수 있습니다.", "Reset password" : "암호 재설정", "A password reset message has been sent to the email address of this account. If you do not receive it, check your spam/junk folders or ask your local administrator for help." : "비밀번호 초기화 메시지가 이 계정의 이메일 주소로 발송되었습니다. 메일을 받지 못했다면, 스팸/정크 폴더를 확인하거나 로컬 관리자에게 문의하십시오.", "If it is not there ask your local administrator." : "그곳에 없다면 로컬 관리자에게 문의하십시오.", "Couldn't send reset email. Please contact your administrator." : "재설정 메일을 보낼 수 없습니다. 관리자에게 문의하십시오.", "Password cannot be changed. Please contact your administrator." : "암호를 변경할 수 없습니다. 관리자에게 문의하십시오.", "Back to login" : "로그인으로 돌아가기", "New password" : "새 암호", "Your files are encrypted. There will be no way to get your data back after your password is reset. If you are not sure what to do, please contact your administrator before you continue. Do you really want to continue?" : "내 파일이 암호화되어 있습니다. 암호를 초기화하면 데이터를 복구할 수 없습니다.
무엇을 해야 할 지 잘 모르겠으면 시스템 관리자에게 연락하십시오.
그래도 계속 진행하시겠습니까?", "I know what I'm doing" : "지금 하려는 것을 알고 있습니다", "Resetting password" : "비밀번호 재설정", "Recommended apps" : "추천되는 앱", "Loading apps …" : "앱 로딩중 ...", "Could not fetch list of apps from the App Store." : "앱스토어로부터 앱 목록을 가져올 수 없음", "Installing apps …" : "앱 설치중...", "App download or installation failed" : "앱 다운로드 또는 설치에 실패함", "Cannot install this app because it is not compatible" : "호환되지 않아 앱을 설치할 수 없습니다.", "Cannot install this app" : "앱을 설치할 수 없음", "Cancel" : "취소", "Schedule work & meetings, synced with all your devices." : "업우와 회의 일정을 짜고, 당신의 모든 기기와 동기화하세요.", "Keep your colleagues and friends in one place without leaking their private info." : "개인정보 누출 없이 동료와 친구들을 한 곳으로 모아두세요.", "Simple email app nicely integrated with Files, Contacts and Calendar." : "파일 연락처 달력과 통합되는 간단한 이메일 앱", "Chatting, video calls, screensharing, online meetings and web conferencing – in your browser and with mobile apps." : "당신의 브라우저와 모바일 앱 속의 채팅, 영상 통화, 화면 공유, 온라인 미팅 그리고 웹 회의", "Collaboratively edit office documents." : "오피스 문서를 함께 편집하세요.", "Forgot password?" : "암호를 잊으셨습니까?", "Log in with a device" : "기기로 로그인", "Back" : "뒤로", "Login form is disabled." : "로그인 양식이 비활성화 되었습니다.", "Reset search" : "검색 초기화", "Search for {name} only" : "{name}(으)로만 검색", "No results for {query}" : "{query}에 대한 결과가 없음", "Start typing to search" : "검색어 입력", "Loading more results …" : "더 많은 결과 불러오는 중...", "Load more results" : "더 많은 결과 불러오기", "Search" : "검색", "An error occurred while searching for {type}" : "{type} 탐색중 오류 발생", "_Please enter {minSearchLength} character or more to search_::_Please enter {minSearchLength} characters or more to search_" : ["{minSearchLength}개 이상의 문자를 입럭하시오"], "Search {types} …" : "{types} 검색 ...", "Settings" : "설정", "Could not load your contacts" : "연락처를 불러올 수 없음", "Search contacts …" : "연락처 검색…", "No contacts found" : "연락처를 찾을 수 없음", "Show all contacts …" : "모든 연락처 보기 …", "Install the Contacts app" : "Contacts 앱 설치", "Loading your contacts …" : "연락처 불러오는 중 …", "Looking for {term} …" : "{term} 검색 중 …", "No" : "아니요", "Yes" : "예", "No files in here" : "여기에 파일이 없음", "New folder" : "새 폴더", "No more subfolders in here" : "더 이상의 하위 폴더 없음", "Name" : "이름", "Size" : "크기", "Modified" : "수정한 날짜", "\"{name}\" is an invalid file name." : "\"{name}\"은(는)
# redMine - project management software
# Copyright (C) 2006-2007  Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

require File.dirname(__FILE__) + '/../test_helper'
require 'issues_controller'

# Re-raise errors caught by the controller.
class IssuesController; def rescue_action(e) raise e end; end

class IssuesControllerTest < Test::Unit::TestCase
  fixtures :projects,
           :users,
           :roles,
           :members,
           :issues,
           :issue_statuses,
           :trackers,
           :projects_trackers,
           :issue_categories,
           :enabled_modules,
           :enumerations,
           :attachments,
           :workflows,
           :custom_fields,
           :custom_values,
           :custom_fields_trackers,
           :time_entries,
           :journals,
           :journal_details
  
  def setup
    @controller = IssuesController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
    User.current = nil
  end

  def test_index
    get :index
    assert_response :success
    assert_template 'index.rhtml'
    assert_not_nil assigns(:issues)
    assert_nil assigns(:project)
    assert_tag :tag => 'a', :content => /Can't print recipes/
    assert_tag :tag => 'a', :content => /Subproject issue/
    # private projects hidden
    assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
    assert_no_tag :tag => 'a', :content => /Issue on project 2/
  end

  def test_index_with_project
    Setting.display_subprojects_issues = 0
    get :index, :project_id => 1
    assert_response :success
    assert_template 'index.rhtml'
    assert_not_nil assigns(:issues)
    assert_tag :tag => 'a', :content => /Can't print recipes/
    assert_no_tag :tag => 'a', :content => /Subproject issue/
  end
  
  def test_index_with_project_and_subprojects
    Setting.display_subprojects_issues = 1
    get :index, :project_id => 1
    assert_response :success
    assert_template 'index.rhtml'
    assert_not_nil assigns(:issues)
    assert_tag :tag => 'a', :content => /Can't print recipes/
    assert_tag :tag => 'a', :content => /Subproject issue/
    assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
  end
  
  def test_index_with_project_and_subprojects_should_show_private_subprojects
    @request.session[:user_id] = 2
    Setting.display_subprojects_issues = 1
    get :index, :project_id => 1
    assert_response :success
    assert_template 'index.rhtml'
    assert_not_nil assigns(:issues)
    assert_tag :tag => 'a', :content => /Can't print recipes/
    assert_tag :tag => 'a', :content => /Subproject issue/
    assert_tag :tag => 'a', :content => /Issue of a private subproject/
  end
  
  def test_index_with_project_and_filter
    get :index, :project_id => 1, :set_filter => 1
    assert_response :success
    assert_template 'index.rhtml'
    assert_not_nil assigns(:issues)
  end
  
  def test_index_csv_with_project
    get :index, :format => 'csv'
    assert_response :success
    assert_not_nil assigns(:issues)
    assert_equal 'text/csv', @response.content_type

    get :index, :project_id => 1, :format => 'csv'
    assert_response :success
    assert_not_nil assigns(:issues)
    assert_equal 'text/csv', @response.content_type
  end
  
  def test_index_pdf
    get :index, :format => 'pdf'
    assert_response :success
    assert_not_nil assigns(:issues)
    assert_equal 'application/pdf', @response.content_type
    
    get :index, :project_id => 1, :format => 'pdf'
    assert_response :success
    assert_not_nil assigns(:issues)
    assert_equal 'application/pdf', @response.content_type
  end
  
  def test_changes
    get :changes, :project_id => 1
    assert_response :success
    assert_not_nil assigns(:journals)
    assert_equal 'application/atom+xml', @response.content_type
  end
  
  def test_show_by_anonymous
    get :show, :id => 1
    assert_response :success
    assert_template 'show.rhtml'
    assert_not_nil assigns(:issue)
    assert_equal Issue.find(1), assigns(:issue)
    
    # anonymous role is allowed to add a note
    assert_tag :tag => 'form',
               :descendant => { :tag => 'fieldset',
                                :child => { :tag => 'legend', 
                                            :content => /Notes/ } }
  end
  
  def test_show_by_manager
    @request.session[:user_id] = 2
    get :show, :id => 1
    assert_response :success
    
    assert_tag :tag => 'form',
               :descendant => { :tag => 'fieldset',
                                :child => { :tag => 'legend', 
                                            :content => /Change properties/ } },
               :descendant => { :tag => 'fieldset',
                                :child => { :tag => 'legend', 
                                            :content => /Log time/ } },
               :descendant => { :tag => 'fieldset',
                                :child => { :tag => 'legend', 
                                            :content => /Notes/ } }
  end

  def test_get_new
    @request.session[:user_id] = 2
    get :new, :project_id => 1, :tracker_id => 1
    assert_response :success
    assert_template 'new'
    
    assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
                                                 :value => 'Default string' }
  end

  def test_get_new_without_tracker_id
    @request.session[:user_id] = 2
    get :new, :project_id => 1
    assert_response :success
    assert_template 'new'
    
    issue = assigns(:issue)
    assert_not_nil issue
    assert_equal Project.find(1).trackers.first, issue.tracker
  end
  
  def test_update_new_form
    @request.session[:user_id] = 2
    xhr :post, :new, :project_id => 1,
                     :issue => {:tracker_id => 2, 
                                :subject => 'This is the test_new issue',
                                :description => 'This is the description',
                                :priority_id => 5}
    assert_response :success
    assert_template 'new'
  end
  
  def test_post_new
    @request.session[:user_id] = 2
    post :new, :project_id => 1, 
               :issue => {:tracker_id => 1,
                          :subject => 'This is the test_new issue',
                          :description => 'This is the description',
                          :priority_id => 5,
                          :estimated_hours => '',
                          :custom_field_values => {'2' => 'Value for field 2'}}
    assert_redirected_to 'issues/show'
    
    issue = Issue.find_by_subject('This is the test_new issue')
    assert_not_nil issue
    assert_equal 2, issue.author_id
    assert_nil issue.estimated_hours
    v = issue.custom_values.find_by_custom_field_id(2)
    assert_not_nil v
    assert_equal 'Value for field 2', v.value
  end
  
  def test_post_new_without_custom_fields_param
    @request.session[:user_id] = 2
    post :new, :project_id => 1, 
               :issue => {:tracker_id => 1,
                          :subject => 'This is the test_new issue',
                          :description => 'This is the description',
                          :priority_id => 5}
    assert_redirected_to 'issues/show'
  end
  
  def test_post_new_with_required_custom_field_and_without_custom_fields_param
    field = IssueCustomField.find_by_name('Database')
    field.update_attribute(:is_required, true)

    @request.session[:user_id] = 2
    post :new, :project_id => 1, 
               :issue => {:tracker_id => 1,
                          :subject => 'This is the test_new issue',
                          :description => 'This is the description',
                          :priority_id => 5}
    assert_response :success
    assert_template 'new'
    issue = assigns(:issue)
    assert_not_nil issue
    assert_equal 'activerecord_error_invalid', issue.errors.on(:custom_values)
  end
  
  def test_post_should_preserve_fields_values_on_validation_failure
    @request.session[:user_id] = 2
    post :new, :project_id => 1, 
               :issue => {:tracker_id => 1,
                          :subject => 'This is the test_new issue',
                          # empty description
                          :description => '',
                          :priority_id => 6,
                          :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
    assert_response :success
    assert_template 'new'
    
    assert_tag :input, :attributes => { :name => 'issue[subject]',
                                        :value => 'This is the test_new issue' }
    assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
                        :child => { :tag => 'option', :attributes => { :selected => 'selected',
                                                                       :value => '6' },
                                                      :content => 'High' }  
    # Custom fields
    assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
                        :child => { :tag => 'option', :attributes => { :selected => 'selected',
                                                                       :value => 'Oracle' },
                                                      :content => 'Oracle' }  
    assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
                                        :value => 'Value for field 2'}
  end
  
  def test_copy_issue
    @request.session[:user_id] = 2
    get :new, :project_id => 1, :copy_from => 1
    assert_template 'new'
    assert_not_nil assigns(:issue)
    orig = Issue.find(1)
    assert_equal orig.subject, assigns(:issue).subject
  end
  
  def test_get_edit
    @request.session[:user_id] = 2
    get :edit, :id => 1
    assert_response :success
    assert_template 'edit'
    assert_not_nil assigns(:issue)
    assert_equal Issue.find(1), assigns(:issue)
  end
  
  def test_get_edit_with_params
    @request.session[:user_id] = 2
    get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
    assert_response :success
    assert_template 'edit'
    
    issue = assigns(:issue)
    assert_not_nil issue
    
    assert_equal 5, issue.status_id
    assert_tag :select, :attributes => { :name => 'issue[status_id]' },
                        :child => { :tag => 'option', 
                                    :content => 'Closed',
                                    :attributes => { :selected => 'selected' } }
                                    
    assert_equal 7, issue.priority_id
    assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
                        :child => { :tag => 'option', 
                                    :content => 'Urgent',
                                    :attributes => { :selected => 'selected' } }
  end
  
  def test_reply_to_issue
    @request.session[:user_id] = 2
    get :reply, :id => 1
    assert_response :success
    assert_select_rjs :show, "update"
  end

  def test_reply_to_note
    @request.session[:user_id] = 2
    get :reply, :id => 1, :journal_id => 2
    assert_response :success
    assert_select_rjs :show, "update"
  end

  def test_post_edit_without_custom_fields_param
    @request.session[:user_id] = 2
    ActionMailer::Base.deliveries.clear
    
    issue = Issue.find(1)
    assert_equal '125', issue.custom_value_for(2).value
    old_subject = issue.subject
    new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
    
    assert_difference('Journal.count') do
      assert_difference('JournalDetail.count', 2) do
        post :edit, :id => 1, :issue => {:subject => new_subject,
                                         :priority_id => '6',
                                         :category_id => '1' # no change
                                        }
      end
    end
    assert_redirected_to 'issues/show/1'
    issue.reload
    assert_equal new_subject, issue.subject
    # Make sure custom fields were not cleared
    assert_equal '125', issue.custom_value_for(2).value
    
    mail = ActionMailer::Base.deliveries.last
    assert_kind_of TMail::Mail, mail
    assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
    assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
  end
  
  def test_post_edit_with_custom_field_change
    @request.session[:user_id] = 2
    issue = Issue.find(1)
    assert_equal '125', issue.custom_value_for(2).value
    
    assert_difference('Journal.count') do
      assert_difference('JournalDetail.count', 3) do
        post :edit, :id => 1, :issue => {:subject => 'Custom field change',
                                         :priority_id => '6',
                                         :category_id => '1', # no change
                                         :custom_field_values => { '2' => 'New custom value' }
                                        }
      end
    end
    assert_redirected_to 'issues/show/1'
    issue.reload
    assert_equal 'New custom value', issue.custom_value_for(2).value
    
    mail = ActionMailer::Base.deliveries.last
    assert_kind_of TMail::Mail, mail
    assert mail.body.include?("Searchable field changed from 125 to New custom value")
  end
  
  def test_post_edit_with_status_and_assignee_change
    issue = Issue.find(1)
    assert_equal 1, issue.status_id
    @request.session[:user_id] = 2
    assert_difference('TimeEntry.count', 0) do
      post :edit,
           :id => 1,
           :issue => { :status_id => 2, :assigned_to_id => 3 },
           :notes => 'Assigned to dlopper',
           :time_entry => { :hours => '', :comments => '', :activity_id => Enumeration.get_values('ACTI').first }
    end
    assert_redirected_to 'issues/show/1'
    issue.reload
    assert_equal 2, issue.status_id
    j = issue.journals.find(:first, :order => 'id DESC')
    assert_equal 'Assigned to dlopper', j.notes
    assert_equal 2, j.details.size
    
    mail = ActionMailer::Base.deliveries.last
    assert mail.body.include?("Status changed from New to Assigned")
  end
  
  def test_post_edit_with_note_only
    notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
    # anonymous user
    post :edit,
         :id => 1,
         :notes => notes
    assert_redirected_to 'issues/show/1'
    j = Issue.find(1).journals.find(:first, :order => 'id DESC')
    assert_equal notes, j.notes
    assert_equal 0, j.details.size
    assert_equal User.anonymous, j.user
    
    mail = ActionMailer::Base.deliveries.last
    assert mail.body.include?(notes)
  end
  
  def test_post_edit_with_note_and_spent_time
    @request.session[:user_id] = 2
    spent_hours_before = Issue.find(1).spent_hours
    assert_difference('TimeEntry.count') do
      post :edit,
           :id => 1,
           :notes => '2.5 hours added',
           :time_entry => { :hours => '2.5', :comments => '', :activity_id => Enumeration.get_values('ACTI').first }
    end
    assert_redirected_to 'issues/show/1'
    
    issue = Issue.find(1)
    
    j = issue.journals.find(:first, :order => 'id DESC')
    assert_equal '2.5 hours added', j.notes
    assert_equal 0, j.details.size
    
    t = issue.time_entries.find(:first, :order => 'id DESC')
    assert_not_nil t
    assert_equal 2.5, t.hours
    assert_equal spent_hours_before + 2.5, issue.spent_hours
  end
  
  def test_post_edit_with_attachment_only
    set_tmp_attachments_directory
    
    # anonymous user
    post :edit,
         :id => 1,
         :notes => '',
         :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
    assert_redirected_to 'issues/show/1'
    j = Issue.find(1).journals.find(:first, :order => 'id DESC')
    assert j.notes.blank?
    assert_equal 1, j.details.size
    assert_equal 'testfile.txt', j.details.first.value
    assert_equal User.anonymous, j.user
    
    mail = ActionMailer::Base.deliveries.last
    assert mail.body.include?('testfile.txt')
  end
  
  def test_post_edit_with_no_change
    issue = Issue.find(1)
    issue.journals.clear
    ActionMailer::Base.deliveries.clear
    
    post :edit,
         :id => 1,
         :notes => ''
    assert_redirected_to 'issues/show/1'
    
    issue.reload
    assert issue.journals.empty?
    # No email should be sent
    assert ActionMailer::Base.deliveries.empty?
  end

  def test_bulk_edit
    @request.session[:user_id] = 2
    # update issues priority
    post :bulk_edit, :ids => [1, 2], :priority_id => 7, :notes => 'Bulk editing', :assigned_to_id => ''
    assert_response 302
    # check that the issues were updated
    assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
    assert_equal 'Bulk editing', Issue.find(1).journals.find(:first, :order => 'created_on DESC').notes
  end

  def test_bulk_unassign
    assert_not_nil Issue.find(2).assigned_to
    @request.session[:user_id] = 2
    # unassign issues
    post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
    assert_response 302
    # check that the issues were updated
    assert_nil Issue.find(2).assigned_to
  end
  
  def test_move_one_issue_to_another_project
    @request.session[:user_id] = 1
    post :move, :id => 1, :new_project_id => 2
    assert_redirected_to 'projects/ecookbook/issues'
    assert_equal 2, Issue.find(1).project_id
  end

  def test_bulk_move_to_another_project
    @request.session[:user_id] = 1
    post :move, :ids => [1, 2], :new_project_id => 2
    assert_redirected_to 'projects/ecookbook/issues'
    # Issues moved to project 2
    assert_equal 2, Issue.find(1).project_id
    assert_equal 2, Issue.find(2).project_id
    # No tracker change
    assert_equal 1, Issue.find(1).tracker_id
    assert_equal 2, Issue.find(2).tracker_id
  end
 
  def test_bulk_move_to_another_tracker
    @request.session[:user_id] = 1
    post :move, :ids => [1, 2], :new_tracker_id => 2
    assert_redirected_to 'projects/ecookbook/issues'
    assert_equal 2, Issue.find(1).tracker_id
    assert_equal 2, Issue.find(2).tracker_id
  end
  
  def test_context_menu_one_issue
    @request.session[:user_id] = 2
    get :context_menu, :ids => [1]
    assert_response :success
    assert_template 'context_menu'
    assert_tag :tag => 'a', :content => 'Edit',
                            :attributes => { :href => '/issues/edit/1',
                                             :class => 'icon-edit' }
    assert_tag :tag => 'a', :content => 'Closed',
                            :attributes => { :href => '/issues/edit/1?issue%5Bstatus_id%5D=5',
                                             :class => '' }
    assert_tag :tag => 'a', :content => 'Immediate',
                            :attributes => { :href => '/issues/edit/1?issue%5Bpriority_id%5D=8',
                                             :class => '' }
    assert_tag :tag => 'a', :content => 'Dave Lopper',
                            :attributes => { :href => '/issues/edit/1?issue%5Bassigned_to_id%5D=3',
                                             :class => '' }
    assert_tag :tag => 'a', :content => 'Copy',
                            :attributes => { :href => '/projects/ecookbook/issues/new?copy_from=1',
                                             :class => 'icon-copy' }
    assert_tag :tag => 'a', :content => 'Move',
                            :attributes => { :href => '/issues/move?ids%5B%5D=1',
                                             :class => 'icon-move' }
    assert_tag :tag => 'a', :content => 'Delete',
                            :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
                                             :class => 'icon-del' }
  end

  def test_context_menu_one_issue_by_anonymous
    get :context_menu, :ids => [1]
    assert_response :success
    assert_template 'context_menu'
    assert_tag :tag => 'a', :content => 'Delete',
                            :attributes => { :href => '#',
                                             :class => 'icon-del disabled' }
  end
  
  def test_context_menu_multiple_issues_of_same_project
    @request.session[:user_id] = 2
    get :context_menu, :ids => [1, 2]
    assert_response :success
    assert_template 'context_menu'
    assert_tag :tag => 'a', :content => 'Edit',
                            :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
                                             :class => 'icon-edit' }
    assert_tag :tag => 'a', :content => 'Move',
                            :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
                                             :class => 'icon-move' }
    assert_tag :tag => 'a', :content => 'Delete',
                            :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
                                             :class => 'icon-del' }
  end

  def test_context_menu_multiple_issues_of_different_project
    @request.session[:user_id] = 2
    get :context_menu, :ids => [1, 2, 4]
    assert_response :success
    assert_template 'context_menu'
    assert_tag :tag => 'a', :content => 'Delete',
                            :attributes => { :href => '#',
                                             :class => 'icon-del disabled' }
  end
  
  def test_destroy_issue_with_no_time_entries
    assert_nil TimeEntry.find_by_issue_id(2)
    @request.session[:user_id] = 2
    post :destroy, :id => 2
    assert_redirected_to 'projects/ecookbook/issues'
    assert_nil Issue.find_by_id(2)
  end

  def test_destroy_issues_with_time_entries
    @request.session[:user_id] = 2
    post :destroy, :ids => [1, 3]
    assert_response :success
    assert_template 'destroy'
    assert_not_nil assigns(:hours)
    assert Issue.find_by_id(1) && Issue.find_by_id(3)
  end

  def test_destroy_issues_and_destroy_time_entries
    @request.session[:user_id] = 2
    post :destroy, :ids => [1, 3], :todo => 'destroy'
    assert_redirected_to 'projects/ecookbook/issues'
    assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
    assert_nil TimeEntry.find_by_id([1, 2])
  end

  def test_destroy_issues_and_assign_time_entries_to_project
    @request.session[:user_id] = 2
    post :destroy, :ids => [1, 3], :todo => 'nullify'
    assert_redirected_to 'projects/ecookbook/issues'
    assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
    assert_nil TimeEntry.find(1).issue_id
    assert_nil TimeEntry.find(2).issue_id
  end
  
  def test_destroy_issues_and_reassign_time_entries_to_another_issue
    @request.session[:user_id] = 2
    post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
    assert_redirected_to 'projects/ecookbook/issues'
    assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
    assert_equal 2, TimeEntry.find(1).issue_id
    assert_equal 2, TimeEntry.find(2).issue_id
  end
  
  def test_destroy_attachment
    issue = Issue.find(3)
    a = issue.attachments.size
    @request.session[:user_id] = 2
    post :destroy_attachment, :id => 3, :attachment_id => 1
    assert_redirected_to 'issues/show/3'
    assert_nil Attachment.find_by_id(1)
    issue.reload
    assert_equal((a-1), issue.attachments.size)
    j = issue.journals.find(:first, :order => 'created_on DESC')
    assert_equal 'attachment', j.details.first.property
  end
end