Refactor main loop of MapreduceEntityCleanupAction

This also tightens up some error-checking conditions.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=149552065
This commit is contained in:
mcilwain 2017-03-08 10:11:04 -08:00 committed by Ben McIlwain
parent 9c33245200
commit d2ca4b7234
2 changed files with 100 additions and 117 deletions

View file

@ -14,10 +14,12 @@
package google.registry.batch; package google.registry.batch;
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreService;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.net.MediaType;
import google.registry.batch.MapreduceEntityCleanupUtil.EligibleJobResults; import google.registry.batch.MapreduceEntityCleanupUtil.EligibleJobResults;
import google.registry.mapreduce.MapreduceRunner; import google.registry.mapreduce.MapreduceRunner;
import google.registry.request.Action; import google.registry.request.Action;
@ -79,6 +81,8 @@ public class MapreduceEntityCleanupAction implements Runnable {
"Do not specify both a job ID and a number of jobs to delete"; "Do not specify both a job ID and a number of jobs to delete";
private static final String ERROR_BOTH_JOB_ID_AND_DAYS_OLD = private static final String ERROR_BOTH_JOB_ID_AND_DAYS_OLD =
"Do not specify both a job ID and a days old threshold"; "Do not specify both a job ID and a days old threshold";
private static final String ERROR_NON_POSITIVE_JOBS_TO_DELETE =
"Do not specify a non-positive integer for the number of jobs to delete";
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
@ -116,7 +120,7 @@ public class MapreduceEntityCleanupAction implements Runnable {
@Override @Override
public void run() { public void run() {
response.setContentType(MediaType.PLAIN_TEXT_UTF_8); response.setContentType(PLAIN_TEXT_UTF_8);
if (jobId.isPresent()) { if (jobId.isPresent()) {
runWithJobId(); runWithJobId();
} else { } else {
@ -124,26 +128,27 @@ public class MapreduceEntityCleanupAction implements Runnable {
} }
} }
private void logSevereAndSetPayload(String message) { private void handleBadRequest(String message) {
logger.severe(message); logger.severe(message);
response.setPayload(message); response.setPayload(message);
response.setStatus(SC_BAD_REQUEST);
} }
/** Delete the job with the specified job ID, checking for conflicting parameters. */ /** Delete the job with the specified job ID, checking for conflicting parameters. */
private void runWithJobId() { private void runWithJobId() {
if (jobName.isPresent()) { if (jobName.isPresent()) {
logSevereAndSetPayload(ERROR_BOTH_JOB_ID_AND_NAME); handleBadRequest(ERROR_BOTH_JOB_ID_AND_NAME);
return; return;
} }
if (numJobsToDelete.isPresent()) { if (numJobsToDelete.isPresent()) {
logSevereAndSetPayload(ERROR_BOTH_JOB_ID_AND_NUMBER_OF_JOBS); handleBadRequest(ERROR_BOTH_JOB_ID_AND_NUMBER_OF_JOBS);
return; return;
} }
if (daysOld.isPresent()) { if (daysOld.isPresent()) {
logSevereAndSetPayload(ERROR_BOTH_JOB_ID_AND_DAYS_OLD); handleBadRequest(ERROR_BOTH_JOB_ID_AND_DAYS_OLD);
return; return;
} }
response.setPayload(requestDeletion(ImmutableSet.of(jobId.get()), true /* generatePayload */)); response.setPayload(requestDeletion(ImmutableSet.of(jobId.get()), true /* verbose */));
} }
/** /**
@ -151,60 +156,55 @@ public class MapreduceEntityCleanupAction implements Runnable {
* which are old enough. * which are old enough.
*/ */
private void runWithoutJobId() { private void runWithoutJobId() {
if (numJobsToDelete.isPresent() && numJobsToDelete.get() <= 0) {
handleBadRequest(ERROR_NON_POSITIVE_JOBS_TO_DELETE);
return;
}
int defaultedDaysOld = daysOld.or(DEFAULT_DAYS_OLD); int defaultedDaysOld = daysOld.or(DEFAULT_DAYS_OLD);
// Only generate the detailed response payload if there aren't too many jobs involved. // Only generate the detailed response payload if there aren't too many jobs involved.
boolean generatePayload = boolean verbose =
numJobsToDelete.isPresent() && (numJobsToDelete.get() <= DEFAULT_MAX_NUM_JOBS_TO_DELETE); numJobsToDelete.isPresent() && (numJobsToDelete.get() <= DEFAULT_MAX_NUM_JOBS_TO_DELETE);
Optional<StringBuilder> payloadBuilder = StringBuilder payload = new StringBuilder();
generatePayload ? Optional.of(new StringBuilder()) : Optional.<StringBuilder>absent();
String defaultPayload = "done";
// Since findEligibleJobsByJobName returns only a certain number of jobs, we must loop through // Since findEligibleJobsByJobName returns only a certain number of jobs, we must loop through
// until we find enough, requesting deletion as we go. We also stop if we don't find anything, // until we find enough, requesting deletion as we go.
// or if there are no more jobs to be found (because no cursor is returned). int numJobsProcessed = 0;
int numJobsDeletedSoFar = 0;
boolean isFirstTime = true;
Optional<String> cursor = Optional.<String>absent();
DateTime cutoffDate = clock.nowUtc().minusDays(defaultedDaysOld); DateTime cutoffDate = clock.nowUtc().minusDays(defaultedDaysOld);
while ((isFirstTime || cursor.isPresent()) Optional<String> cursor = Optional.absent();
&& (!numJobsToDelete.isPresent() || (numJobsDeletedSoFar < numJobsToDelete.get()))) { do {
isFirstTime = false; EligibleJobResults batch =
EligibleJobResults eligibleJobResults =
mapreduceEntityCleanupUtil.findEligibleJobsByJobName( mapreduceEntityCleanupUtil.findEligibleJobsByJobName(
jobName.orNull(), cutoffDate, numJobsToDelete, force.or(false), cursor); jobName.orNull(), cutoffDate, numJobsToDelete, force.or(false), cursor);
cursor = eligibleJobResults.cursor(); cursor = batch.cursor();
if (eligibleJobResults.eligibleJobs().isEmpty()) { // Individual batches can come back empty if none of the returned jobs meet the requirements
logger.infofmt( // or if all jobs have been exhausted.
"No eligible job with name '%s' older than %s days old.", if (!batch.eligibleJobs().isEmpty()) {
jobName.or("(null)"), defaultedDaysOld); String payloadChunk = requestDeletion(batch.eligibleJobs(), verbose);
if (generatePayload) { if (verbose) {
payloadBuilder.get().append("No eligible job."); payload.append(payloadChunk);
} }
defaultPayload = "No eligible job."; numJobsProcessed += batch.eligibleJobs().size();
} else {
String payloadChunk = requestDeletion(eligibleJobResults.eligibleJobs(), generatePayload);
if (generatePayload) {
payloadBuilder.get().append(payloadChunk);
}
numJobsDeletedSoFar += eligibleJobResults.eligibleJobs().size();
} }
} // Stop iterating when all jobs have been exhausted (cursor is absent) or enough have been
// processed.
} while (cursor.isPresent()
&& (!numJobsToDelete.isPresent() || (numJobsProcessed < numJobsToDelete.get())));
logger.infofmt("A total of %s job(s) processed", numJobsDeletedSoFar); if (numJobsProcessed == 0) {
if (generatePayload) { logger.infofmt(
payloadBuilder "No eligible jobs found with name '%s' older than %s days old.",
.get() jobName.or("(any)"), defaultedDaysOld);
.append(String.format("A total of %d job(s) processed\n", numJobsDeletedSoFar)); payload.append("No eligible jobs found");
response.setPayload(payloadBuilder.get().toString());
} else { } else {
response.setPayload(defaultPayload); logger.infofmt("A total of %s job(s) processed.", numJobsProcessed);
payload.append(String.format("A total of %d job(s) processed", numJobsProcessed));
} }
response.setPayload(payload.toString());
} }
private String requestDeletion(Set<String> actualJobIds, boolean generatePayload) { private String requestDeletion(Set<String> actualJobIds, boolean verbose) {
Optional<StringBuilder> payloadChunkBuilder = Optional<StringBuilder> payloadChunkBuilder =
generatePayload ? Optional.of(new StringBuilder()) : Optional.<StringBuilder>absent(); verbose ? Optional.of(new StringBuilder()) : Optional.<StringBuilder>absent();
int errorCount = 0; int errorCount = 0;
for (String actualJobId : actualJobIds) { for (String actualJobId : actualJobIds) {
Optional<String> error = Optional<String> error =

View file

@ -15,7 +15,9 @@
package google.registry.batch; package google.registry.batch;
import static com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService; import static com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService;
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_OK; import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
@ -45,7 +47,6 @@ import com.google.appengine.tools.pipeline.impl.model.ShardedValue;
import com.google.appengine.tools.pipeline.impl.model.Slot; import com.google.appengine.tools.pipeline.impl.model.Slot;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.net.MediaType;
import google.registry.testing.ExceptionRule; import google.registry.testing.ExceptionRule;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse; import google.registry.testing.FakeResponse;
@ -61,8 +62,7 @@ import org.junit.runners.JUnit4;
public class MapreduceEntityCleanupActionTest public class MapreduceEntityCleanupActionTest
extends MapreduceTestCase<MapreduceEntityCleanupAction> { extends MapreduceTestCase<MapreduceEntityCleanupAction> {
@Rule @Rule public final ExceptionRule thrown = new ExceptionRule();
public final ExceptionRule thrown = new ExceptionRule();
private static final DatastoreService datastore = getDatastoreService(); private static final DatastoreService datastore = getDatastoreService();
private static final FetchOptions FETCH_OPTIONS = FetchOptions.Builder.withChunkSize(200); private static final FetchOptions FETCH_OPTIONS = FetchOptions.Builder.withChunkSize(200);
@ -118,17 +118,6 @@ public class MapreduceEntityCleanupActionTest
return pipelineService.startNewPipeline(mapReduceJob, new JobSetting.OnQueue(QUEUE_NAME)); return pipelineService.startNewPipeline(mapReduceJob, new JobSetting.OnQueue(QUEUE_NAME));
} }
/*
private void setJobIdAndJobName(Optional<String> jobId, Optional<String> jobName) {
setJobIdJobNameAndDaysOld(jobId, jobName, Optional.<Integer>absent());
}
*/
private void setAnyJob() {
setJobIdJobNameAndDaysOld(
Optional.<String>absent(), Optional.<String>absent(), Optional.<Integer>absent());
}
private void setAnyJobAndDaysOld(int daysOld) { private void setAnyJobAndDaysOld(int daysOld) {
setJobIdJobNameAndDaysOld( setJobIdJobNameAndDaysOld(
Optional.<String>absent(), Optional.<String>absent(), Optional.<Integer>of(daysOld)); Optional.<String>absent(), Optional.<String>absent(), Optional.<Integer>of(daysOld));
@ -205,7 +194,7 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo( assertThat(response.getPayload()).isEqualTo(
jobId jobId
+ ": deletion requested\n" + ": deletion requested\n"
@ -240,8 +229,8 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo("No eligible job."); assertThat(response.getPayload()).isEqualTo("No eligible jobs found");
assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1); assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1);
} }
@ -254,9 +243,9 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1); assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1);
assertThat(response.getPayload()).isEqualTo("No eligible job."); assertThat(response.getPayload()).isEqualTo("No eligible jobs found");
} }
@Test @Test
@ -268,49 +257,25 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo("done"); assertThat(response.getPayload()).isEqualTo("A total of 1 job(s) processed");
executeTasksUntilEmpty(QUEUE_NAME, clock); executeTasksUntilEmpty(QUEUE_NAME, clock);
assertNumMapreducesAndShardedJobs(0, 0); assertNumMapreducesAndShardedJobs(0, 0);
assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1); assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1);
} }
@Test
public void testDeleteZeroJobs_succeeds() throws Exception {
createMapreduce("jobname");
executeTasksUntilEmpty(QUEUE_NAME, clock);
action = new MapreduceEntityCleanupAction(
Optional.<String>absent(), // jobId
Optional.<String>absent(), // jobName
Optional.<Integer>of(0), // numJobsToDelete
Optional.<Integer>absent(), // daysOld
Optional.<Boolean>absent(), // force
mapreduceEntityCleanupUtil,
clock,
DatastoreServiceFactory.getDatastoreService(),
response);
action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo("A total of 0 job(s) processed\n");
executeTasksUntilEmpty(QUEUE_NAME, clock);
assertNumMapreducesAndShardedJobs(1, 3);
assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(0);
}
@Test @Test
public void testAnyJob_fails() throws Exception { public void testAnyJob_fails() throws Exception {
createMapreduce("jobname"); createMapreduce("jobname");
executeTasksUntilEmpty(QUEUE_NAME, clock); executeTasksUntilEmpty(QUEUE_NAME, clock);
setAnyJob(); setJobIdJobNameAndDaysOld(
Optional.<String>absent(), Optional.<String>absent(), Optional.<Integer>absent());
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo("No eligible job."); assertThat(response.getPayload()).isEqualTo("No eligible jobs found");
assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1); assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1);
} }
@ -323,8 +288,8 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo("done"); assertThat(response.getPayload()).isEqualTo("A total of 1 job(s) processed");
executeTasksUntilEmpty(QUEUE_NAME, clock); executeTasksUntilEmpty(QUEUE_NAME, clock);
assertNumMapreducesAndShardedJobs(0, 0); assertNumMapreducesAndShardedJobs(0, 0);
assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1); assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1);
@ -337,7 +302,7 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo( assertThat(response.getPayload()).isEqualTo(
"nonexistent: deletion requested\n" "nonexistent: deletion requested\n"
+ "successfully requested async deletion of 1 job(s); errors received on 0\n"); + "successfully requested async deletion of 1 job(s); errors received on 0\n");
@ -356,8 +321,8 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo("done"); assertThat(response.getPayload()).isEqualTo("A total of 2 job(s) processed");
executeTasksUntilEmpty(QUEUE_NAME, clock); executeTasksUntilEmpty(QUEUE_NAME, clock);
assertNumMapreducesAndShardedJobs(0, 0); assertNumMapreducesAndShardedJobs(0, 0);
assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1); assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1);
@ -374,8 +339,8 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo("done"); assertThat(response.getPayload()).isEqualTo("A total of 2 job(s) processed");
executeTasksUntilEmpty(QUEUE_NAME, clock); executeTasksUntilEmpty(QUEUE_NAME, clock);
assertNumMapreducesAndShardedJobs(0, 0); assertNumMapreducesAndShardedJobs(0, 0);
assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(2); assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(2);
@ -401,11 +366,11 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).endsWith( assertThat(response.getPayload()).endsWith(
": deletion requested\n" ": deletion requested\n"
+ "successfully requested async deletion of 1 job(s); errors received on 0\n" + "successfully requested async deletion of 1 job(s); errors received on 0\n"
+ "A total of 1 job(s) processed\n"); + "A total of 1 job(s) processed");
executeTasksUntilEmpty(QUEUE_NAME, clock); executeTasksUntilEmpty(QUEUE_NAME, clock);
assertNumMapreducesAndShardedJobs(1, 3); assertNumMapreducesAndShardedJobs(1, 3);
assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1); assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1);
@ -421,8 +386,8 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo("done"); assertThat(response.getPayload()).isEqualTo("A total of 1 job(s) processed");
executeTasksUntilEmpty(QUEUE_NAME, clock); executeTasksUntilEmpty(QUEUE_NAME, clock);
assertNumMapreducesAndShardedJobs(1, 3); assertNumMapreducesAndShardedJobs(1, 3);
assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1); assertThat(mapreduceEntityCleanupUtil.getNumSearchesPerformed()).isEqualTo(1);
@ -438,7 +403,7 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).endsWith( assertThat(response.getPayload()).endsWith(
": deletion requested\n" ": deletion requested\n"
+ "successfully requested async deletion of 1 job(s); errors received on 0\n"); + "successfully requested async deletion of 1 job(s); errors received on 0\n");
@ -457,7 +422,7 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo( assertThat(response.getPayload()).isEqualTo(
jobId1 jobId1
+ ": deletion requested\n" + ": deletion requested\n"
@ -479,7 +444,7 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response2.getStatus()).isEqualTo(SC_OK); assertThat(response2.getStatus()).isEqualTo(SC_OK);
assertThat(response2.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response2.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response2.getPayload()).isEqualTo( assertThat(response2.getPayload()).isEqualTo(
jobId2 jobId2
+ ": deletion requested\n" + ": deletion requested\n"
@ -498,7 +463,7 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).endsWith( assertThat(response.getPayload()).endsWith(
": Job is not in FINALIZED or STOPPED state\n" ": Job is not in FINALIZED or STOPPED state\n"
+ "successfully requested async deletion of 0 job(s); errors received on 1\n"); + "successfully requested async deletion of 0 job(s); errors received on 1\n");
@ -524,7 +489,7 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).endsWith( assertThat(response.getPayload()).endsWith(
": deletion requested\n" ": deletion requested\n"
+ "successfully requested async deletion of 1 job(s); errors received on 0\n"); + "successfully requested async deletion of 1 job(s); errors received on 0\n");
@ -540,8 +505,8 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()).isEqualTo("Do not specify both a job ID and a job name"); assertThat(response.getPayload()).isEqualTo("Do not specify both a job ID and a job name");
assertNumMapreducesAndShardedJobs(0, 0); assertNumMapreducesAndShardedJobs(0, 0);
} }
@ -552,8 +517,8 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()) assertThat(response.getPayload())
.isEqualTo("Do not specify both a job ID and a days old threshold"); .isEqualTo("Do not specify both a job ID and a days old threshold");
assertNumMapreducesAndShardedJobs(0, 0); assertNumMapreducesAndShardedJobs(0, 0);
@ -561,7 +526,6 @@ public class MapreduceEntityCleanupActionTest
@Test @Test
public void testJobIdAndNumJobs_fails() throws Exception { public void testJobIdAndNumJobs_fails() throws Exception {
clock.setTo(DateTime.now(UTC));
action = new MapreduceEntityCleanupAction( action = new MapreduceEntityCleanupAction(
Optional.of("jobid"), Optional.of("jobid"),
Optional.<String>absent(), // jobName Optional.<String>absent(), // jobName
@ -575,10 +539,29 @@ public class MapreduceEntityCleanupActionTest
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8); assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
assertThat(response.getPayload()) assertThat(response.getPayload())
.isEqualTo("Do not specify both a job ID and a number of jobs to delete"); .isEqualTo("Do not specify both a job ID and a number of jobs to delete");
assertNumMapreducesAndShardedJobs(0, 0); assertNumMapreducesAndShardedJobs(0, 0);
} }
@Test
public void testDeleteZeroJobs_throwsUsageError() throws Exception {
new MapreduceEntityCleanupAction(
Optional.<String>absent(), // jobId
Optional.<String>absent(), // jobName
Optional.<Integer>of(0), // numJobsToDelete
Optional.<Integer>absent(), // daysOld
Optional.<Boolean>absent(), // force
mapreduceEntityCleanupUtil,
clock,
DatastoreServiceFactory.getDatastoreService(),
response)
.run();
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
assertThat(response.getPayload())
.isEqualTo("Do not specify a non-positive integer for the number of jobs to delete");
}
} }