public AssessmentResult verify(
      final Action expectedAction,
      final String input,
      final String ip) throws IOException {
    final String[] parts = input.split("\\" + SEPARATOR, 4);

    // we allow missing actions, if we're missing 1 part, assume it's the action
    if (parts.length < 4) {
      throw new BadRequestException("too few parts");
    }

    final String prefix = parts[0];
    final String siteKey = parts[1].toLowerCase(Locale.ROOT).strip();
    final String action = parts[2];
    String token = parts[3];

    String provider = prefix;
    if (prefix.endsWith(SHORT_SUFFIX)) {
      // This is a "short" solution that points to the actual solution. We need to fetch the
      // full solution before proceeding
      provider = prefix.substring(0, prefix.length() - SHORT_SUFFIX.length());
      token = shortCodeExpander.retrieve(token).orElseThrow(() -> new BadRequestException("invalid shortcode"));
    }

    final CaptchaClient client = this.captchaClientMap.get(provider);
    if (client == null) {
      throw new BadRequestException("invalid captcha scheme");
    }

    final Action parsedAction = Action.parse(action)
        .orElseThrow(() -> {
          Metrics.counter(INVALID_ACTION_COUNTER_NAME, "action", action).increment();
          return new BadRequestException("invalid captcha action");
        });

    if (!parsedAction.equals(expectedAction)) {
      Metrics.counter(INVALID_ACTION_COUNTER_NAME, "action", action).increment();
      throw new BadRequestException("invalid captcha action");
    }

    final Set<String> allowedSiteKeys = client.validSiteKeys(parsedAction);
    if (!allowedSiteKeys.contains(siteKey)) {
      logger.debug("invalid site-key {}, action={}, token={}", siteKey, action, token);
      Metrics.counter(INVALID_SITEKEY_COUNTER_NAME, "action", action).increment();
      throw new BadRequestException("invalid captcha site-key");
    }

    final AssessmentResult result = client.verify(siteKey, parsedAction, token, ip);
    Metrics.counter(ASSESSMENTS_COUNTER_NAME,
            "action", action,
            "score", result.getScoreString(),
            "provider", provider)
        .increment();
    return result;
  }
@Test
  public void choose() throws IOException {
    String ainput = String.join(SEPARATOR, PREFIX_A, CHALLENGE_SITE_KEY, "challenge", TOKEN);
    String binput = String.join(SEPARATOR, PREFIX_B, CHALLENGE_SITE_KEY, "challenge", TOKEN);
    final CaptchaClient a = mockClient(PREFIX_A);
    final CaptchaClient b = mockClient(PREFIX_B);

    new CaptchaChecker(null, List.of(a, b)).verify(Action.CHALLENGE, ainput, null);
    verify(a, times(1)).verify(any(), any(), any(), any());

    new CaptchaChecker(null, List.of(a, b)).verify(Action.CHALLENGE, binput, null);
    verify(b, times(1)).verify(any(), any(), any(), any());
  }