/*
** Module   : HttpResponseTest.java
** Abstract : A test for the HTTP response body, header(s) and status code.
**
** Copyright (c) 2021-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
** 001 CA  20210827 Created initial version.
** 002 CA  20240725 Added cookie support for request and response.
*/

/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License
** along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.goldencode.harness.test;

import java.util.*;

import org.apache.http.*;

import com.goldencode.harness.*;

/**
 * A test applied to the HTTP response.
 */
public class HttpResponseTest
extends Report
implements WebResponseTestable
{
   /** Steps to apply for this test. */
   private List<WebResponseTestable> steps = new ArrayList<>();
   
   /** The expected response body. */
   private String body;

   /** The associated service. */
   private WebService parent;
   
   /**
    * Create a new test with the expected body.
    * 
    * @param    body
    *           The expected HTTP response body.
    */
   public HttpResponseTest(String body)
   {
      this.body = body;
   }

   /**
    * Verify the returned response to match the condition specified in the implementation class.
    * 
    * @param    response
    *           The HTTP response.
    */
   @Override
   public void execute(HttpResponse response)
   {
      setStatus(Status.PASSED, null);
      
      boolean firstFailed = false;
      
      for (WebResponseTestable step : steps)
      {
         Report r = (Report) step;

         try
         {
            step.execute(response);
         }
         catch (Throwable t)
         {
            r.setStatus(Status.FAILED, "Failure executing test: " + step.getClass().getSimpleName() + ": " + t.getMessage());
            t.printStackTrace();
         }

         if (r.getStatus() != Status.PASSED)
         {
            if (!firstFailed)
            {
               parent.setStatus(Status.FAILED, "");
            }
            
            firstFailed = true;
            parent.setReason(parent.getReason() + "\n" + r.getReason());
         }
      }
   }

   /**
    * Create a new {@link HttpResponseStatus} test and add it as a {@link #steps step}.
    * 
    * @param    code
    *           The expected status code.
    * @param    reason
    *           The expected status reason.
    */
   public void setStatus(String code, String reason)
   {
      steps.add(new HttpResponseStatus(code, reason));
   }

   /**
    * Create a new {@link HttpResponseCookie} test and add it as a {@link #steps step}.
    * 
    * @param    name
    *           The cookie name.
    * @param    value
    *           The cookie value.
    * @param    variable
    *           The variable where to save the cookie value, if the test passes.
    */
   public void addCookie(String name, String value, String variable)
   {
      steps.add(new HttpResponseCookie(name, value, variable, parent.getResourcePool()));
   }

   /**
    * Create a new {@link HttpResponseHeader} test and add it as a {@link #steps step}.
    * 
    * @param    name
    *           The header name.
    * @param    value
    *           The header value.
    * @param    variable
    *           The variable where to save the header value, if the test passes.
    */
   public void addHeader(String name, String value, String variable)
   {
      steps.add(new HttpResponseHeader(name, value, variable, parent.getResourcePool()));
   }

   /**
    * Create a new {@link HttpResponseBody} test and add it as a {@link #steps step}.
    * 
    * @param    variable
    *           The variable where to save the body, if the test passes.
    * @param    excludes
    *           A list of paths to be excluded from the match.
    * @param    includes
    *           A list of paths to be included in the match.
    */
   public void setBodyMatch(String variable, List<String> excludes, List<String> includes)
   {
      steps.add(new HttpResponseBody(body, variable, excludes, includes, parent));
   }

   /**
    * Create a new {@link HttpResponsePathExists} test and add it as a {@link #steps step}.
    * 
    * @param    path
    *           The path to test that it exists.
    * @param    variable
    *           The variable where to save the value of this path, if it exists.
    */
   public void addPathExistsMatch(String path, String variable)
   {
      steps.add(new HttpResponsePathExists(path, variable, parent));
   }

   /**
    * Create a new {@link HttpResponsePathRegex} test and add it as a {@link #steps step}.
    * 
    * @param    path
    *           The path to check.
    * @param    value
    *           The expected value.
    * @param    variable
    *           The variable where to save the value, if it matches.
    */
   public void addPathRegexMatch(String path, String value, String variable)
   {
      steps.add(new HttpResponsePathRegex(path, value, variable, parent));
   }

   /**
    * Create a new {@link HttpResponsePathExact} test and add it as a {@link #steps step}.
    * 
    * @param    path
    *           The path to check.
    * @param    value
    *           The expected value.
    * @param    variable
    *           The variable where to save the value, if it matches.
    */
   public void addPathExactMatch(String path, String value, String variable)
   {
      steps.add(new HttpResponsePathExact(path, value, variable, parent));
   }

   /**
    * Set the parent web service.
    * 
    * @param    webService
    *           The service configuration.
    */
   public void setParent(WebService webService)
   {
      this.parent = webService;
   }
}
