All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jio.test.pbt.rest.RestPropBuilder Maven / Gradle / Ivy

Go to download

JIO test library based on Property Based Testing and Java Flight Recording Debuggers

There is a newer version: 3.0.0-RC2
Show newest version
package jio.test.pbt.rest;

import fun.gen.Gen;
import jio.BiLambda;
import jio.IO;
import jio.Lambda;
import jio.test.pbt.Property;
import jio.test.pbt.PropertyBuilder;
import jio.test.pbt.TestFailure;
import jio.test.pbt.TestResult;
import jsonvalues.*;
import jsonvalues.spec.JsParserException;

import java.net.http.HttpResponse;
import java.util.function.BiFunction;
import java.util.function.Function;

import static java.util.Objects.requireNonNull;

/**
 * An abstract base class for building property tests for RESTful APIs. This class provides a flexible framework for
 * defining property tests for HTTP POST, GET, and DELETE operations on a RESTful API endpoint.
 *
 * @param   The type of data generated to feed the property tests.
 * @param  The concrete subclass type for fluent builder methods.
 */
abstract class RestPropBuilder> {

  @SuppressWarnings("UnnecessaryLambda")
  final static Function, TestResult> DEFAULT_RESP_ASSERT =
      resp -> resp.statusCode() < 300 ?
              TestResult.SUCCESS :
              TestFailure.reason("Expected status code < 300, but got a " + resp.statusCode());
  final String name;
  final BiLambda> post;
  final BiLambda> get;
  final BiLambda> delete;
  final Gen gen;
  @SuppressWarnings("UnnecessaryLambda")
  private final Function, IO>> getIdFromPath =
      path -> (body, resp) -> {
        try {
          JsObj respBody = JsObj.parse(resp.body());
          JsValue id = respBody.get(path);
          return id == JsNothing.NOTHING ?
                 IO.fail(TestFailure.reason(path + " not found in the following json: " + resp.body()))
                                         :
                 IO.succeed(id.toString());
        } catch (JsParserException e) {
          return IO.fail(TestFailure.reason("resp body is not a Json well-formed: " + resp.body()));
        }
      };
  Function, TestResult> postAssert = DEFAULT_RESP_ASSERT;
  Function, TestResult> getAssert = DEFAULT_RESP_ASSERT;
  Function, TestResult> deleteAssert = DEFAULT_RESP_ASSERT;
  BiFunction, IO> getId;

  /**
   * Creates a new instance of the RestPropBuilder class with the specified parameters.
   *
   * @param name     The name of the property test.
   * @param gen      The data generator that produces pseudorandom data for testing.
   * @param p_post   The lambda function representing the HTTP POST operation.
   * @param p_get    The lambda function representing the HTTP GET operation.
   * @param p_delete The lambda function representing the HTTP DELETE operation.
   */
  public RestPropBuilder(String name,
                         Gen gen,
                         BiLambda> p_post,
                         BiLambda> p_get,
                         BiLambda> p_delete
                        ) {
    this.post = requireNonNull(p_post);
    this.get = requireNonNull(p_get);
    this.delete = requireNonNull(p_delete);
    this.name = requireNonNull(name);
    this.gen = requireNonNull(gen);
    this.getId = getIdFromPath.apply(JsPath.fromKey("id"));
  }

  /**
   * Sets the assertion function for the HTTP POST operation.
   *
   * @param postAssert The assertion function for the HTTP POST operation.
   * @return This RestPropBuilder instance with the updated assertion function.
   */
  @SuppressWarnings("unchecked")
  public PropBuilder withPostAssert(final Function, TestResult> postAssert) {
    this.postAssert = requireNonNull(postAssert);
    return (PropBuilder) this;
  }

  /**
   * Sets the assertion function for the HTTP GET operation.
   *
   * @param getAssert The assertion function for the HTTP GET operation.
   * @return This RestPropBuilder instance with the updated assertion function.
   */
  @SuppressWarnings("unchecked")
  public PropBuilder withGetAssert(final Function, TestResult> getAssert) {
    this.getAssert = requireNonNull(getAssert);
    return (PropBuilder) this;
  }

  /**
   * Sets the assertion function for the HTTP DELETE operation.
   *
   * @param deleteAssert The assertion function for the HTTP DELETE operation.
   * @return This RestPropBuilder instance with the updated assertion function.
   */
  @SuppressWarnings("unchecked")
  public PropBuilder withDeleteAssert(final Function, TestResult> deleteAssert) {
    this.deleteAssert = requireNonNull(deleteAssert);
    return (PropBuilder) this;
  }

  /**
   * Sets the function to extract an ID for subsequent HTTP requests. You can choose from two specific ways to extract
   * the ID:
   * 
    *
  • Use {@link #withGetIdFromReqBody(Function)} to extract the ID from the request body of type O.
  • *
  • Use {@link #withGetIdFromJSONRespPath(JsPath)} to extract the ID from the HTTP response using a specific path.
  • *
* * @param getId The function to extract an ID for subsequent HTTP requests. * @return This RestPropBuilder instance with the updated ID extraction method. */ @SuppressWarnings("unchecked") public PropBuilder withGetId(BiFunction, IO> getId) { this.getId = requireNonNull(getId); return (PropBuilder) this; } /** * Sets the path to extract an ID from the JSON response and use it in subsequent HTTP requests. * * @param path The path to extract an ID from the HTTP response. * @return This RestPropBuilder instance with the updated ID extraction path. */ @SuppressWarnings("unchecked") public PropBuilder withGetIdFromJSONRespPath(final JsPath path) { this.getId = getIdFromPath.apply(requireNonNull(path)); return (PropBuilder) this; } /** * Sets the function to extract an ID from the request body of type O and use it in subsequent HTTP requests. * * @param p_getId The function to extract an ID from the request body of type O. * @return This RestPropBuilder instance with the updated ID extraction function. */ @SuppressWarnings("unchecked") public PropBuilder withGetIdFromReqBody(final Function p_getId) { requireNonNull(p_getId); this.getId = (body, resp) -> { String id = p_getId.apply(body); return id == null || id.isBlank() || id.isEmpty() ? IO.fail(TestFailure.reason("id not found")) : IO.succeed(id); }; return (PropBuilder) this; } Lambda, IdResp> assertResp( Function, TestResult> assertResp, String id ) { return resp -> { TestResult result = assertResp.apply(resp); if (result instanceof TestFailure f) { return IO.fail(f); } return IO.succeed(new IdResp(id, resp)); }; } /** * returns a property builder. If no further customization is needed, you can use {@link #build()} * * @return a property builder */ public abstract PropertyBuilder get(); /** * Build a property with the default parameters. For further customization (times of generations, description, * executor etc), use {@link #get()} that returns a property builder * * @return a property */ public Property build() { return get().get(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy