package gaej2011.controller;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.junit.matchers.JUnitMatchers.*;
import gaej2011.meta.MinutesMeta;
import gaej2011.model.Minutes;
import gaej2011.service.MemoService;
import gaej2011.service.MinutesService;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Future;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.junit.Test;
import org.slim3.datastore.Datastore;
import org.slim3.tester.ControllerTestCase;
import org.slim3.tester.TestEnvironment;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.mail.MailServicePb.MailMessage;
import com.google.apphosting.api.ApiProxy;
import com.google.apphosting.api.ApiProxy.ApiConfig;
import com.google.apphosting.api.ApiProxy.ApiProxyException;
import com.google.apphosting.api.ApiProxy.Delegate;
import com.google.apphosting.api.ApiProxy.Environment;
import com.google.apphosting.api.ApiProxy.LogRecord;
public class MinutesControllerTest extends ControllerTestCase {
static final String PATH = "/Minutes";
@Test
public void 議事録のタイトルを作成できる() throws NullPointerException,
IllegalArgumentException, IOException, ServletException {
// test@example.com というユーザがログイン中、という状態を作っておく。
TestEnvironment environment =
(TestEnvironment) ApiProxy.getCurrentEnvironment();
environment.setEmail("test@example.com");
tester.request.setMethod("POST");
tester.param("title", " テスト用議事録1");
tester.start(PATH);
assertThat(
"MinutesController のインスタンスが使用される",
tester.getController(),
instanceOf(MinutesController.class));
assertThat(
" レスポンスコードが204",
tester.response.getStatus(),
is(HttpServletResponse.SC_NO_CONTENT));
}
@Test
public void 議事録のタイトルを作成できる_パラメータを指定しない() throws NullPointerException,
IllegalArgumentException, IOException, ServletException {
// test@example.com というユーザがログイン中、という状態を作っておく。
TestEnvironment environment =
(TestEnvironment) ApiProxy.getCurrentEnvironment();
environment.setEmail("test@example.com");
tester.request.setMethod("POST");
tester.start(PATH);
assertThat(
"MinutesController のインスタンスが使用される",
tester.getController(),
instanceOf(MinutesController.class));
assertThat(
" レスポンスコードが400",
tester.response.getStatus(),
is(HttpServletResponse.SC_BAD_REQUEST));
}
@Test
public void 議事録のタイトルを作成できる_ログインしていない() throws NullPointerException,
IllegalArgumentException, IOException, ServletException {
tester.request.setMethod("POST");
tester.start(PATH);
assertThat(
" レスポンスコードが401",
tester.response.getStatus(),
is(HttpServletResponse.SC_UNAUTHORIZED));
}
@Test
public void 議事録の一覧を新しい順に表示できる() throws NullPointerException,
IllegalArgumentException, IOException, ServletException {
for (int i = 0; i < 5; i++) {
Minutes minutes = new Minutes();
minutes.setTitle(" テスト用議事録" + i);
minutes.setCreatedAt(new Date());
Datastore.put(minutes);
}
tester.request.setMethod("GET");
tester.start(PATH);
assertThat(
"MinutesController のインスタンスが使用される",
tester.getController(),
instanceOf(MinutesController.class));
assertThat(
" レスポンスコードが200",
tester.response.getStatus(),
is(HttpServletResponse.SC_OK));
assertThat(
"Content-Type はapplication/json",
tester.response.getContentType(),
is("application/json"));
assertThat(
"Chancter-Encoding はutf-8",
tester.response.getCharacterEncoding(),
is("utf-8"));
String json = tester.response.getOutputAsString();
System.out.println(json);
Minutes[] minutes = MinutesMeta.get().jsonToModels(json);
assertThat(" 全ての議事録が返る", minutes.length, is(5));
}
@Test
public void 議事録のTSVをダウンロードする() throws IOException, NullPointerException,
IllegalArgumentException, ServletException {
// test@example.com というユーザがログイン中、という状態を作っておく。
TestEnvironment environment =
(TestEnvironment) ApiProxy.getCurrentEnvironment();
environment.setEmail("test@example.com");
Key minutesKey = MinutesService.put(" テスト用議事録");
for (int i = 0; i < 5; i++) {
MemoService.put(minutesKey, "memo" + i);
}
Minutes minutes = Datastore.get(Minutes.class, minutesKey);
BlobKey blobKey = MinutesService.exportAsTSV(minutes);
tester.param("download", blobKey.getKeyString());
tester.start(PATH);
assertThat(
"MinutesController のインスタンスが使用される",
tester.getController(),
instanceOf(MinutesController.class));
assertThat(
" レスポンスコードが200",
tester.response.getStatus(),
is(HttpServletResponse.SC_OK));
assertThat(
"Content-Type はTSV",
tester.response.getContentType(),
is("text/tab-separated-values"));
assertThat(
"Content-Length が指定されている",
tester.response.getContentLength(),
is(not(0)));
assertThat(
"Content-disposition ヘッダにファイル名が指定されている",
tester.response.getHeader("Content-disposition"),
is("attachment; テスト用議事録"));
assertThat(
" コンテントのサイズ=Content-Length",
tester.response.getOutputAsByteArray().length,
is(tester.response.getContentLength()));
}
@Test
public void 議事録を追加するとメールが送信される() throws NullPointerException,
IllegalArgumentException, IOException, ServletException {
// test@example.com というユーザがログイン中、という状態を作っておく。
TestEnvironment environment =
(TestEnvironment) ApiProxy.getCurrentEnvironment();
environment.setEmail("test@example.com");
// メール送信API をフックするApiProxy.Delegate を適用しておく。
@SuppressWarnings("rawtypes")
Delegate parentDelegate = ApiProxy.getDelegate();
MailDelegate mailDelegate = new MailDelegate();
ApiProxy.setDelegate(mailDelegate);
tester.request.setMethod("POST");
tester.param("title", " テスト用議事録1");
tester.start(PATH);
assertThat(" メールが送信される", mailDelegate.messages.size(), is(1));
MailMessage mail = mailDelegate.messages.get(0);
assertThat(
"From",
mail.getSender(),
is("minutes@yourappid.appspotmail.com"));
assertThat("Subject", mail.getSubject(), is(" 新しい議事録が追加されました"));
assertThat(
"Body",
mail.getTextBody(Charset.forName("utf-8")),
containsString("http://localhost/minutes.html?minutes="));
// ApiProxy.Delegate をもとに戻す。
ApiProxy.setDelegate(parentDelegate);
}
@Test
public void 議事録の削除要求を受け付ける_ログインしていない() throws NullPointerException,
IllegalArgumentException, IOException, ServletException {
Key minutesKey = MinutesService.put(" テスト用議事録");
for (int i = 0; i < 5; i++) {
MemoService.put(minutesKey, "memo" + i);
}
tester.param("delete", Datastore.keyToString(minutesKey));
tester.start(PATH);
assertThat(
"MinutesController のインスタンスが使用される",
tester.getController(),
instanceOf(MinutesController.class));
assertThat(
" レスポンスコードが401",
tester.response.getStatus(),
is(HttpServletResponse.SC_UNAUTHORIZED));
}
@Test
public void 議事録の削除要求を受け付ける_議事録作成者以外が要求する() throws NullPointerException,
IllegalArgumentException, IOException, ServletException {
// test@example.com というユーザがログイン中、という状態を作っておく。
TestEnvironment environment =
(TestEnvironment) ApiProxy.getCurrentEnvironment();
environment.setEmail("test@example.com");
Key minutesKey = MinutesService.put(" テスト用議事録");
for (int i = 0; i < 5; i++) {
MemoService.put(minutesKey, "memo" + i);
}
// 作成者以外がログインした状態にする
environment.setEmail("other@example.com");
tester.param("delete", Datastore.keyToString(minutesKey));
tester.start(PATH);
assertThat(
"MinutesController のインスタンスが使用される",
tester.getController(),
instanceOf(MinutesController.class));
assertThat(
" レスポンスコードが403",
tester.response.getStatus(),
is(HttpServletResponse.SC_FORBIDDEN));
}
@Test
public void 議事録の削除要求を受け付ける() throws NullPointerException,
IllegalArgumentException, IOException, ServletException {
// test@example.com というユーザがログイン中、という状態を作っておく。
TestEnvironment environment =
(TestEnvironment) ApiProxy.getCurrentEnvironment();
environment.setEmail("test@example.com");
Key minutesKey = MinutesService.put(" テスト用議事録");
for (int i = 0; i < 5; i++) {
MemoService.put(minutesKey, "memo" + i);
}
Minutes minutes = Datastore.get(Minutes.class, minutesKey);
// メール送信API をフックするApiProxy.Delegate を適用しておく。
@SuppressWarnings("rawtypes")
Delegate parentDelegate = ApiProxy.getDelegate();
MailDelegate mailDelegate = new MailDelegate();
ApiProxy.setDelegate(mailDelegate);
tester.param("delete", Datastore.keyToString(minutesKey));
tester.start(PATH);
assertThat(
"MinutesController のインスタンスが使用される",
tester.getController(),
instanceOf(MinutesController.class));
assertThat(
" レスポンスコードが204",
tester.response.getStatus(),
is(HttpServletResponse.SC_NO_CONTENT));
assertThat(" メールが送信される", mailDelegate.messages.size(), is(1));
MailMessage mail = mailDelegate.messages.get(0);
assertThat(
"From",
mail.getSender(),
is("minutes@yourappid.appspotmail.com"));
assertThat("To", mail.getTo(0), is("test@example.com"));
assertThat(
"Subject",
mail.getSubject(),
is(" 議事録[" + minutes.getTitle() + "] がTSV に変換されました"));
assertThat(
"Body",
mail.getTextBody(Charset.forName("utf-8")),
containsString("http://localhost/minutes?download="));
// ApiProxy.Delegate をもとに戻す。
ApiProxy.setDelegate(parentDelegate);
}
static class MailDelegate implements Delegate<Environment> {
@SuppressWarnings("unchecked")
final Delegate<Environment> parent = ApiProxy.getDelegate();
public void flushLogs(Environment environment) {
parent.flushLogs(environment);
}
public List<Thread> getRequestThreads(Environment environment) {
return parent.getRequestThreads(environment);
}
public void log(Environment environment, LogRecord record) {
parent.log(environment, record);
}
public Future<byte[]> makeAsyncCall(Environment environment,
String packageName, String methodName, byte[] request,
ApiConfig apiConfig) {
inspectMailService(packageName, methodName, request);
return parent.makeAsyncCall(
environment,
packageName,
methodName,
request,
apiConfig);
}
public byte[] makeSyncCall(Environment environment, String packageName,
String methodName, byte[] request) throws ApiProxyException {
inspectMailService(packageName, methodName, request);
return parent.makeSyncCall(
environment,
packageName,
methodName,
request);
}
final List<MailMessage> messages = new ArrayList<MailMessage>();
void inspectMailService(String packageName, String methodName,
byte[] request) {
if (packageName.equals("mail")
&& (methodName.equals("SendToAdmins") || methodName
.equals("Send"))) {
// mail#SendToAdmins RPC が実行された場合
// バイト配列からProtocolBuffer オブジェクトを組立て直し、messages に追加する。
MailMessage messagePb = new MailMessage();
messagePb.mergeFrom(request);
messages.add(messagePb);
}
}
}
}