001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.assertions;
018
019import java.io.*;
020import java.time.temporal.*;
021import java.util.*;
022import java.util.function.*;
023
024import org.apache.juneau.common.utils.*;
025import org.apache.juneau.cp.*;
026import org.apache.juneau.internal.*;
027import org.apache.juneau.serializer.*;
028
029/**
030 * Used for fluent assertion calls against dates.
031 *
032 * <h5 class='section'>Example:</h5>
033 * <p class='bjava'>
034 *    <jc>// Validates the response expiration is after the current date.</jc>
035 *    <jv>client</jv>
036 *       .get(<jsf>URL</jsf>)
037 *       .run()
038 *       .assertDateHeader(<js>"Expires"</js>).isAfterNow();
039 * </p>
040 *
041 *
042 * <h5 class='section'>Test Methods:</h5>
043 * <p>
044 * <ul class='javatree'>
045 *    <li class='jc'>{@link FluentDateAssertion}
046 *    <ul class='javatreec'>
047 *       <li class='jm'>{@link FluentDateAssertion#is(Date,ChronoUnit) is(Date,ChronoUnit)}
048 *       <li class='jm'>{@link FluentDateAssertion#isAfter(Date) isAfter(Date)}
049 *       <li class='jm'>{@link FluentDateAssertion#isAfterNow() isAfterNow()}
050 *       <li class='jm'>{@link FluentDateAssertion#isBefore(Date) isBefore(Date)}
051 *       <li class='jm'>{@link FluentDateAssertion#isBeforeNow() isBeforeNow()}
052 *       <li class='jm'>{@link FluentDateAssertion#isBetween(Date,Date) isBetween(Date,Date)}
053 *    </ul>
054 *    <li class='jc'>{@link FluentComparableAssertion}
055 *    <ul class='javatreec'>
056 *       <li class='jm'>{@link FluentComparableAssertion#isGt(Comparable) isGt(Comparable)}
057 *       <li class='jm'>{@link FluentComparableAssertion#isGte(Comparable) isGte(Comparable)}
058 *       <li class='jm'>{@link FluentComparableAssertion#isLt(Comparable) isLt(Comparable)}
059 *       <li class='jm'>{@link FluentComparableAssertion#isLte(Comparable) isLte(Comparable)}
060 *       <li class='jm'>{@link FluentComparableAssertion#isBetween(Comparable,Comparable) isBetween(Comparable,Comparable)}
061  *   </ul>
062 *    <li class='jc'>{@link FluentObjectAssertion}
063 *    <ul class='javatreec'>
064 *       <li class='jm'>{@link FluentObjectAssertion#isExists() isExists()}
065 *       <li class='jm'>{@link FluentObjectAssertion#is(Object) is(Object)}
066 *       <li class='jm'>{@link FluentObjectAssertion#is(Predicate) is(Predicate)}
067 *       <li class='jm'>{@link FluentObjectAssertion#isNot(Object) isNot(Object)}
068 *       <li class='jm'>{@link FluentObjectAssertion#isAny(Object...) isAny(Object...)}
069 *       <li class='jm'>{@link FluentObjectAssertion#isNotAny(Object...) isNotAny(Object...)}
070 *       <li class='jm'>{@link FluentObjectAssertion#isNull() isNull()}
071 *       <li class='jm'>{@link FluentObjectAssertion#isNotNull() isNotNull()}
072 *       <li class='jm'>{@link FluentObjectAssertion#isString(String) isString(String)}
073 *       <li class='jm'>{@link FluentObjectAssertion#isJson(String) isJson(String)}
074 *       <li class='jm'>{@link FluentObjectAssertion#isSame(Object) isSame(Object)}
075 *       <li class='jm'>{@link FluentObjectAssertion#isSameJsonAs(Object) isSameJsonAs(Object)}
076 *       <li class='jm'>{@link FluentObjectAssertion#isSameSortedJsonAs(Object) isSameSortedJsonAs(Object)}
077 *       <li class='jm'>{@link FluentObjectAssertion#isSameSerializedAs(Object, WriterSerializer) isSameSerializedAs(Object, WriterSerializer)}
078 *       <li class='jm'>{@link FluentObjectAssertion#isType(Class) isType(Class)}
079 *       <li class='jm'>{@link FluentObjectAssertion#isExactType(Class) isExactType(Class)}
080 *    </ul>
081 * </ul>
082 *
083 * <h5 class='section'>Transform Methods:</h5>
084 * <p>
085 * <ul class='javatree'>
086 *    <li class='jc'>{@link FluentDateAssertion}
087 *    <ul class='javatreec'>
088 *       <li class='jm'>{@link FluentDateAssertion#asEpochMillis() asEpochMillis()}
089 *       <li class='jm'>{@link FluentDateAssertion#asEpochSeconds() asEpochSeconds()}
090 *    </ul>
091 *    <li class='jc'>{@link FluentObjectAssertion}
092 *    <ul class='javatreec'>
093 *       <li class='jm'>{@link FluentObjectAssertion#asString() asString()}
094 *       <li class='jm'>{@link FluentObjectAssertion#asString(WriterSerializer) asString(WriterSerializer)}
095 *       <li class='jm'>{@link FluentObjectAssertion#asString(Function) asString(Function)}
096 *       <li class='jm'>{@link FluentObjectAssertion#asJson() asJson()}
097 *       <li class='jm'>{@link FluentObjectAssertion#asJsonSorted() asJsonSorted()}
098 *       <li class='jm'>{@link FluentObjectAssertion#asTransformed(Function) asApplied(Function)}
099 *       <li class='jm'>{@link FluentObjectAssertion#asAny() asAny()}
100 * </ul>
101 * </ul>
102 *
103 * <h5 class='section'>Configuration Methods:</h5>
104 * <p>
105 * <ul class='javatree'>
106 *    <li class='jc'>{@link Assertion}
107 *    <ul class='javatreec'>
108 *       <li class='jm'>{@link Assertion#setMsg(String, Object...) setMsg(String, Object...)}
109 *       <li class='jm'>{@link Assertion#setOut(PrintStream) setOut(PrintStream)}
110 *       <li class='jm'>{@link Assertion#setSilent() setSilent()}
111 *       <li class='jm'>{@link Assertion#setStdOut() setStdOut()}
112 *       <li class='jm'>{@link Assertion#setThrowable(Class) setThrowable(Class)}
113 *    </ul>
114 * </ul>
115 *
116 * <h5 class='section'>See Also:</h5><ul>
117 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauEcosystemOverview">Juneau Ecosystem Overview</a>
118 * </ul>
119 *
120 * @param <R> The return type.
121 */
122public class FluentDateAssertion<R> extends FluentComparableAssertion<Date,R> {
123
124   //-----------------------------------------------------------------------------------------------------------------
125   // Static
126   //-----------------------------------------------------------------------------------------------------------------
127
128   private static final Messages MESSAGES = Messages.of(FluentDateAssertion.class, "Messages");
129   private static final String
130      MSG_unexpectedValue = MESSAGES.getString("unexpectedValue"),
131      MSG_valueWasNotAfterExpected = MESSAGES.getString("valueWasNotAfterExpected"),
132      MSG_valueWasNotBeforeExpected = MESSAGES.getString("valueWasNotBeforeExpected");
133
134   //-----------------------------------------------------------------------------------------------------------------
135   // Instance
136   //-----------------------------------------------------------------------------------------------------------------
137
138   /**
139    * Constructor.
140    *
141    * @param value
142    *    The object being tested.
143    *    <br>Can be <jk>null</jk>.
144    * @param returns
145    *    The object to return after a test method is called.
146    *    <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be
147    * used on the same assertion.
148    */
149   public FluentDateAssertion(Date value, R returns) {
150      this(null, value, returns);
151   }
152
153   /**
154    * Chained constructor.
155    *
156    * <p>
157    * Used when transforming one assertion into another so that the assertion config can be used by the new assertion.
158    *
159    * @param creator
160    *    The assertion that created this assertion.
161    *    <br>Should be <jk>null</jk> if this is the top-level assertion.
162    * @param value
163    *    The object being tested.
164    *    <br>Can be <jk>null</jk>.
165    * @param returns
166    *    The object to return after a test method is called.
167    *    <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be
168    * used on the same assertion.
169    */
170   public FluentDateAssertion(Assertion creator, Date value, R returns) {
171      super(creator, value, returns);
172   }
173
174   //-----------------------------------------------------------------------------------------------------------------
175   // Transform methods
176   //-----------------------------------------------------------------------------------------------------------------
177
178   /**
179    * Returns an long assertion on the epoch milliseconds of this date.
180    *
181    * <p>
182    * If the date is <jk>null</jk>, the returned assertion is a null assertion
183    * (meaning {@link FluentLongAssertion#isExists()} returns <jk>false</jk>).
184    *
185    * @return A new assertion.
186    */
187   public FluentLongAssertion<R> asEpochMillis() {
188      return new FluentLongAssertion<>(this, valueIsNull() ? null : value().getTime(), returns());
189   }
190
191   /**
192    * Returns an long assertion on the epoch seconds of this date.
193    *
194    * <p>
195    * If the date is <jk>null</jk>, the returned assertion is a null assertion
196    * (meaning {@link FluentLongAssertion#isExists()} returns <jk>false</jk>).
197    *
198    * @return A new assertion.
199    */
200   public FluentLongAssertion<R> asEpochSeconds() {
201      return new FluentLongAssertion<>(this, valueIsNull() ? null : value().getTime() / 1000, returns());
202   }
203
204   //-----------------------------------------------------------------------------------------------------------------
205   // Test methods
206   //-----------------------------------------------------------------------------------------------------------------
207
208   /**
209    * Asserts that the value equals the specified value at the specified precision.
210    *
211    * @param value The value to check against.
212    * @param precision The precision (e.g. {@link ChronoUnit#SECONDS}).
213    * @return The fluent return object.
214    * @throws AssertionError If assertion failed.
215    */
216   public R is(Date value, ChronoUnit precision) throws AssertionError {
217      if (Utils.ne(value(), value, (x,y)->x.toInstant().truncatedTo(precision).equals(y.toInstant().truncatedTo(precision))))
218         throw error(MSG_unexpectedValue, value, value());
219      return returns();
220   }
221
222   /**
223    * Asserts that the value is after the specified value.
224    *
225    * @param value The values to check against.
226    * @return The fluent return object.
227    * @throws AssertionError If assertion failed.
228    */
229   public R isAfter(Date value) throws AssertionError {
230      Utils.assertArgNotNull("value", value);
231      if (! (value().after(value)))
232         throw error(MSG_valueWasNotAfterExpected, value, value());
233      return returns();
234   }
235
236   /**
237    * Asserts that the value is after the current date.
238    *
239    * @return The fluent return object.
240    * @throws AssertionError If assertion failed.
241    */
242   public R isAfterNow() throws AssertionError {
243      return isAfter(new Date());
244   }
245
246   /**
247    * Asserts that the value is before the specified value.
248    *
249    * @param value The values to check against.
250    * @return The fluent return object.
251    * @throws AssertionError If assertion failed.
252    */
253   public R isBefore(Date value) throws AssertionError {
254      Utils.assertArgNotNull("value", value);
255      if (! (value().before(value)))
256         throw error(MSG_valueWasNotBeforeExpected, value, value());
257      return returns();
258   }
259
260   /**
261    * Asserts that the value is before the current date.
262    *
263    * @return The fluent return object.
264    * @throws AssertionError If assertion failed.
265    */
266   public R isBeforeNow() throws AssertionError {
267      return isBefore(new Date());
268   }
269
270   /**
271    * Asserts that the value is between (inclusive) the specified upper and lower values.
272    *
273    * @param lower The lower value to check against.
274    * @param upper The upper value to check against.
275    * @return The fluent return object.
276    * @throws AssertionError If assertion failed.
277    */
278   public R isBetween(Date lower, Date upper) throws AssertionError {
279      isExists();
280      Utils.assertArgNotNull("lower", lower);
281      Utils.assertArgNotNull("upper", upper);
282      isLte(upper);
283      isGte(lower);
284      return returns();
285   }
286
287   //-----------------------------------------------------------------------------------------------------------------
288   // Fluent setters
289   //-----------------------------------------------------------------------------------------------------------------
290   @Override /* Overridden from Assertion */
291   public FluentDateAssertion<R> setMsg(String msg, Object...args) {
292      super.setMsg(msg, args);
293      return this;
294   }
295
296   @Override /* Overridden from Assertion */
297   public FluentDateAssertion<R> setOut(PrintStream value) {
298      super.setOut(value);
299      return this;
300   }
301
302   @Override /* Overridden from Assertion */
303   public FluentDateAssertion<R> setSilent() {
304      super.setSilent();
305      return this;
306   }
307
308   @Override /* Overridden from Assertion */
309   public FluentDateAssertion<R> setStdOut() {
310      super.setStdOut();
311      return this;
312   }
313
314   @Override /* Overridden from Assertion */
315   public FluentDateAssertion<R> setThrowable(Class<? extends java.lang.RuntimeException> value) {
316      super.setThrowable(value);
317      return this;
318   }
319}