-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ThirdPartySyncCommand - Adding new parameters #614
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,15 @@ | ||
package com.box.l10n.mojito.cli.command; | ||
|
||
import com.box.l10n.mojito.rest.client.ThirdPartySync; | ||
import com.box.l10n.mojito.rest.ThirdPartySyncAction; | ||
|
||
/** | ||
* | ||
* @author sdemyanenko | ||
*/ | ||
public class ThirdPartySyncActionsConverter extends EnumConverter<ThirdPartySync.Action> { | ||
public class ThirdPartySyncActionsConverter extends EnumConverter<ThirdPartySyncAction> { | ||
|
||
@Override | ||
protected Class<ThirdPartySync.Action> getGenericClass() { | ||
return ThirdPartySync.Action.class; | ||
protected Class<ThirdPartySyncAction> getGenericClass() { | ||
return ThirdPartySyncAction.class; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,105 @@ | ||
package com.box.l10n.mojito.cli.command; | ||
|
||
import com.box.l10n.mojito.cli.CLITestBase; | ||
import com.box.l10n.mojito.cli.utils.PollableTaskJobMatcher; | ||
import com.box.l10n.mojito.cli.utils.TestingJobListener; | ||
import com.box.l10n.mojito.entity.Repository; | ||
import com.box.l10n.mojito.rest.client.ThirdPartySync; | ||
import com.box.l10n.mojito.json.ObjectMapper; | ||
import com.box.l10n.mojito.rest.thirdparty.ThirdPartySyncAction; | ||
import com.box.l10n.mojito.service.thirdparty.ThirdPartySyncJob; | ||
import com.box.l10n.mojito.service.thirdparty.ThirdPartySyncJobInput; | ||
import org.junit.After; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.quartz.JobKey; | ||
import org.quartz.Matcher; | ||
import org.quartz.Scheduler; | ||
import org.quartz.SchedulerException; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.beans.factory.annotation.Qualifier; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertTrue; | ||
import static com.box.l10n.mojito.rest.thirdparty.ThirdPartySyncAction.MAP_TEXTUNIT; | ||
import static com.box.l10n.mojito.rest.thirdparty.ThirdPartySyncAction.PUSH_SCREENSHOT; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
public class ThirdPartySyncCommandTest extends CLITestBase { | ||
|
||
/** | ||
* logger | ||
*/ | ||
static Logger logger = LoggerFactory.getLogger(ThirdPartySyncCommandTest.class); | ||
@Autowired | ||
@Qualifier("fail_on_unknown_properties_false") | ||
ObjectMapper objectMapper; | ||
|
||
@Autowired | ||
Scheduler scheduler; | ||
|
||
Matcher<JobKey> jobMatcher; | ||
TestingJobListener testingJobListener; | ||
|
||
@Before | ||
public void setUp() throws SchedulerException { | ||
testingJobListener = new TestingJobListener(objectMapper); | ||
jobMatcher = new PollableTaskJobMatcher<>(ThirdPartySyncJob.class); | ||
scheduler.getListenerManager().addJobListener(testingJobListener, jobMatcher); | ||
aurambaj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
@After | ||
public void tearDown() throws SchedulerException { | ||
scheduler.getListenerManager().removeJobListener(testingJobListener.getName()); | ||
scheduler.getListenerManager().removeJobListenerMatcher(testingJobListener.getName(), jobMatcher); | ||
aurambaj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
@Test | ||
public void execute() throws Exception { | ||
|
||
String repoName = testIdWatcher.getEntityName("thirdpartysync_execute"); | ||
|
||
Repository repository = repositoryService.createRepository(repoName, repoName + " description", null, false); | ||
getL10nJCommander().run("thirdparty-sync", "-r", repository.getName(), "-p", "does-not-matter-yet", "-ps", " _"); | ||
String projectId = testIdWatcher.getEntityName("projectId"); | ||
|
||
// TODO: For a plural separator like " _" this test will fail. The current version we have for | ||
// JCommander trims the argument values, even when quoted. | ||
// https://github.com/cbeust/jcommander/issues/417 | ||
// https://github.com/cbeust/jcommander/commit/4aec38b4a0ea63a8dc6f41636fa81c2ebafddc18 | ||
String pluralSeparator = "_"; | ||
String skipTextUnitPattern = "%skip_text_pattern"; | ||
String skipAssetPattern = "%skip_asset_pattern%"; | ||
List<String> options = Arrays.asList( | ||
"special-option=value@of%Option", | ||
"smartling-placeholder-custom=\\{\\{\\}\\}|\\{\\{?.+?\\}\\}?|\\%\\%\\(.+?\\)s|\\%\\(.+?\\)s|\\%\\(.+?\\)d|\\%\\%s|\\%s" | ||
); | ||
|
||
getL10nJCommander().run("thirdparty-sync", | ||
"-r", repository.getName(), | ||
"-p", projectId, | ||
"-a", MAP_TEXTUNIT.name(), PUSH_SCREENSHOT.name(), | ||
"-ps", pluralSeparator, | ||
"-st", skipTextUnitPattern, | ||
"-sa", skipAssetPattern, | ||
"-o", options.get(0), options.get(1)); | ||
|
||
String output = outputCapture.toString(); | ||
assertThat(output).contains("repository: " + repository.getName()); | ||
assertThat(output).contains("project id: " + projectId); | ||
assertThat(output).contains("actions: " + Arrays.asList(ThirdPartySyncAction.MAP_TEXTUNIT, ThirdPartySyncAction.PUSH_SCREENSHOT).toString()); | ||
assertThat(output).contains("skip-text-units-with-pattern: " + skipTextUnitPattern); | ||
assertThat(output).contains("skip-assets-path-pattern: " + skipAssetPattern); | ||
assertThat(output).contains("options: " + options.toString()); | ||
|
||
waitForCondition("Ensure ThirdPartySyncJob gets executed", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess that listener is ok for now though I think changing the application context is not too great if we ever start running test in parallel, etc. So my preference would be avoiding the listener and just wait for the state (or different part of the state) to get to what we expect which would indicate a successful migration. On top of my head I don't remember other test intercepting part of the request to perform input validation. In general those are more like end to end test. The parameters are not check directly but instead the results are checked inferring that the proper arguments were passed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've prioritized on testing that the parameters given to the CLI were passed around downstream all the way to the Quartz job for this test. I do agree that here is better to test final states or side effects, but since non of the parameters is being used in Since we'll still implement new methods in |
||
() -> testingJobListener.getExecuted().size() > 0); | ||
|
||
ThirdPartySyncJobInput jobInput = testingJobListener.getFirstInputMapAs(ThirdPartySyncJobInput.class); | ||
|
||
String outputString = outputCapture.toString(); | ||
assertTrue(outputString.contains(Arrays.asList(ThirdPartySync.Action.MAP_TEXTUNIT, ThirdPartySync.Action.PUSH_SCREENSHOT).toString())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not keeping that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||
assertThat(jobInput).isNotNull(); | ||
assertThat(jobInput.getRepositoryId()).isEqualTo(repository.getId()); | ||
assertThat(jobInput.getThirdPartyProjectId()).isEqualTo(projectId); | ||
assertThat(jobInput.getActions()).containsExactlyInAnyOrder(MAP_TEXTUNIT, PUSH_SCREENSHOT); | ||
assertThat(jobInput.getPluralSeparator()).isEqualTo(pluralSeparator); | ||
assertThat(jobInput.getSkipTextUnitsWithPattern()).isEqualTo(skipTextUnitPattern); | ||
assertThat(jobInput.getSkipAssetsWithPathPattern()).isEqualTo(skipAssetPattern); | ||
assertThat(jobInput.getOptions()).containsExactlyInAnyOrderElementsOf(options); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.box.l10n.mojito.cli.utils; | ||
|
||
import com.box.l10n.mojito.quartz.QuartzPollableJob; | ||
import org.quartz.JobKey; | ||
import org.quartz.Matcher; | ||
|
||
import static com.box.l10n.mojito.quartz.QuartzConfig.DYNAMIC_GROUP_NAME; | ||
|
||
public class PollableTaskJobMatcher<T extends QuartzPollableJob> implements Matcher<JobKey> { | ||
|
||
private final Class<T> target; | ||
|
||
public PollableTaskJobMatcher(Class<T> target) { | ||
this.target = target; | ||
} | ||
|
||
@Override | ||
public boolean isMatch(JobKey key) { | ||
return key.getName().startsWith(target.getName()) && DYNAMIC_GROUP_NAME.equals(key.getGroup()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package com.box.l10n.mojito.cli.utils; | ||
|
||
import com.box.l10n.mojito.json.ObjectMapper; | ||
import org.quartz.JobExecutionContext; | ||
import org.quartz.JobExecutionException; | ||
import org.quartz.JobListener; | ||
|
||
import java.util.LinkedList; | ||
import java.util.Queue; | ||
|
||
import static com.box.l10n.mojito.quartz.QuartzPollableJob.INPUT; | ||
|
||
public class TestingJobListener implements JobListener { | ||
|
||
private final Queue<JobExecutionContext> toBeExecuted = new LinkedList<>(); | ||
private final Queue<JobExecutionContext> executed = new LinkedList<>(); | ||
private final Queue<JobExecutionException> exceptions = new LinkedList<>(); | ||
private final ObjectMapper objectMapper; | ||
|
||
public TestingJobListener(ObjectMapper objectMapper) { | ||
this.objectMapper = objectMapper; | ||
} | ||
|
||
public TestingJobListener() { | ||
this.objectMapper = new ObjectMapper(); | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return "TestingJobListener"; | ||
} | ||
|
||
@Override | ||
public void jobToBeExecuted(JobExecutionContext context) { | ||
toBeExecuted.offer(context); | ||
} | ||
|
||
@Override | ||
public void jobExecutionVetoed(JobExecutionContext context) { | ||
} | ||
|
||
@Override | ||
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { | ||
executed.offer(context); | ||
exceptions.offer(jobException); | ||
} | ||
|
||
public Queue<JobExecutionContext> getExecuted() { | ||
return executed; | ||
} | ||
|
||
public Queue<JobExecutionContext> getToBeExecuted() { | ||
return toBeExecuted; | ||
} | ||
|
||
public <T> T getFirstInputMapAs(Class<T> klass) { | ||
String input = getExecuted().stream() | ||
.filter(context -> context.getMergedJobDataMap().containsKey(INPUT)) | ||
.map(context -> context.getMergedJobDataMap().getString(INPUT)) | ||
.findFirst() | ||
.orElse(""); | ||
|
||
return objectMapper.readValueUnchecked(input, klass); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.box.l10n.mojito.rest; | ||
|
||
public enum ThirdPartySyncAction { | ||
PUSH, | ||
PUSH_TRANSLATION, | ||
PULL, | ||
MAP_TEXTUNIT, | ||
PUSH_SCREENSHOT | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh sorry, maybe I wasn't super clear last time we chatted about it. I don't want the rest client and the webservice layer to be linked closely (with webapp depending on the client). The main reason is that in most cases there is not a 1:1 mapping between what's done in the server and what makes sense in the client (hibernate entities, slightly different object). I know that in many case the POJO are or feel like duplicates between the client and server but I'd rather keep that way. Let's not have the rest client as dependency of webapp, it also becomes messy and we may end up having some code going through the client instead of directly through the service which doesn't make sense. It may become confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just updated the code