### Question:
ExternalRedirectRenditionDispatcherImpl extends AbstractRenditionDispatcherImpl implements AssetRenditionDispatcher { @Override public void dispatch(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException, ServletException { final AssetRenditionParameters parameters = new AssetRenditionParameters(request); final String expression = mappings.get(parameters.getRenditionName()); final String evaluatedExpression = assetRenditions.evaluateExpression(request, expression); if (StringUtils.isNotBlank(evaluatedExpression)) { log.debug("Serving External redirect rendition [ {} ] for resolved rendition name [ {} ]", evaluatedExpression, parameters.getRenditionName()); if (cfg.redirect() == HttpServletResponse.SC_MOVED_TEMPORARILY) { response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); } else { response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); } response.setHeader("Location", UrlUtil.escape(evaluatedExpression)); } else { log.error("Could not convert [ {} ] into a valid URI", evaluatedExpression); response.sendError(HttpServletResponse.SC_NOT_FOUND, "Could not serve asset rendition."); } }  @Override String getLabel(); @Override String getName(); @Override Map<String, String> getOptions(); @Override boolean isHidden(); @Override Set<String> getRenditionNames(); @Override List<String> getTypes(); @Override void dispatch(SlingHttpServletRequest request, SlingHttpServletResponse response);  }### Answer:
@Test public void dispatch() throws IOException, ServletException { ctx.registerInjectActivateService(new ExternalRedirectRenditionDispatcherImpl(), ImmutableMap.<String, Object>builder(). put("rendition.mappings", new String[]{ "testing=${dm.domain}is/image/${dm.file}?$greyscale$"}). build()); final AssetRenditionDispatcher assetRenditionDispatcher = ctx.getService(AssetRenditionDispatcher.class); ctx.requestPathInfo().setResourcePath("/content/dam/test.png"); ctx.requestPathInfo().setExtension("rendition"); ctx.requestPathInfo().setSuffix("testing/download/asset.rendition"); assetRenditionDispatcher.dispatch(ctx.request(), ctx.response()); assertEquals(301, ctx.response().getStatus()); }
@Test public void dispatch_WithSpacesInPath() throws IOException, ServletException { ctx.registerInjectActivateService(new ExternalRedirectRenditionDispatcherImpl(), ImmutableMap.<String, Object>builder(). put("rendition.mappings", new String[]{ "testing=${asset.path}.test.500.500.${asset.extension}"}). put("redirect", 302). build()); final AssetRenditionDispatcher assetRenditionDispatcher = ctx.getService(AssetRenditionDispatcher.class); doReturn(DamUtil.resolveToAsset(ctx.resourceResolver().getResource("/content/dam/test with spaces.png"))).when(assetResolver).resolveAsset(ctx.request()); ctx.currentResource("/content/dam/test with spaces.png"); ctx.requestPathInfo().setExtension("rendition"); ctx.requestPathInfo().setSuffix("testing/download/asset.rendition"); assetRenditionDispatcher.dispatch(ctx.request(), ctx.response()); assertEquals(302, ctx.response().getStatus()); assertEquals("/content/dam/test%20with%20spaces.png.test.500.500.png", ctx.response().getHeader("Location")); }