001/*
002 * acme4j - Java ACME client
003 *
004 * Copyright (C) 2018 Richard "Shred" Körber
005 *   http://acme4j.shredzone.org
006 *
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
013 */
014package org.shredzone.acme4j;
015
016import static org.assertj.core.api.Assertions.assertThat;
017import static org.shredzone.acme4j.toolbox.TestUtils.getJSON;
018import static org.shredzone.acme4j.toolbox.TestUtils.url;
019
020import java.net.URL;
021import java.time.Instant;
022import java.util.Optional;
023
024import edu.umd.cs.findbugs.annotations.Nullable;
025import org.junit.jupiter.api.Test;
026import org.shredzone.acme4j.exception.AcmeException;
027import org.shredzone.acme4j.toolbox.JSON;
028import org.shredzone.acme4j.toolbox.TestUtils;
029
030/**
031 * Unit tests for {@link AcmeJsonResource}.
032 */
033public class AcmeJsonResourceTest {
034
035    private static final JSON JSON_DATA = getJSON("newAccountResponse");
036    private static final URL LOCATION_URL = url("https://example.com/acme/resource/123");
037
038    /**
039     * Test {@link AcmeJsonResource#AcmeJsonResource(Login, URL)}.
040     */
041    @Test
042    public void testLoginConstructor() {
043        var login = TestUtils.login();
044
045        var resource = new DummyJsonResource(login, LOCATION_URL);
046        assertThat(resource.getLogin()).isEqualTo(login);
047        assertThat(resource.getSession()).isEqualTo(login.getSession());
048        assertThat(resource.getLocation()).isEqualTo(LOCATION_URL);
049        assertThat(resource.isValid()).isFalse();
050        assertThat(resource.getRetryAfter()).isEmpty();
051        assertUpdateInvoked(resource, 0);
052
053        assertThat(resource.getJSON()).isEqualTo(JSON_DATA);
054        assertThat(resource.isValid()).isTrue();
055        assertUpdateInvoked(resource, 1);
056    }
057
058    /**
059     * Test {@link AcmeJsonResource#setJSON(JSON)}.
060     */
061    @Test
062    public void testSetJson() {
063        var login = TestUtils.login();
064
065        var jsonData2 = getJSON("requestOrderResponse");
066
067        var resource = new DummyJsonResource(login, LOCATION_URL);
068        assertThat(resource.isValid()).isFalse();
069        assertUpdateInvoked(resource, 0);
070
071        resource.setJSON(JSON_DATA);
072        assertThat(resource.getJSON()).isEqualTo(JSON_DATA);
073        assertThat(resource.isValid()).isTrue();
074        assertUpdateInvoked(resource, 0);
075
076        resource.setJSON(jsonData2);
077        assertThat(resource.getJSON()).isEqualTo(jsonData2);
078        assertThat(resource.isValid()).isTrue();
079        assertUpdateInvoked(resource, 0);
080    }
081
082    /**
083     * Test Retry-After
084     */
085    @Test
086    public void testRetryAfter() {
087        var login = TestUtils.login();
088        var retryAfter = Instant.now().plusSeconds(30L);
089        var jsonData = getJSON("requestOrderResponse");
090
091        var resource = new DummyJsonResource(login, LOCATION_URL, jsonData, retryAfter);
092        assertThat(resource.isValid()).isTrue();
093        assertThat(resource.getJSON()).isEqualTo(jsonData);
094        assertThat(resource.getRetryAfter()).hasValue(retryAfter);
095        assertUpdateInvoked(resource, 0);
096
097        resource.setRetryAfter(null);
098        assertThat(resource.getRetryAfter()).isEmpty();
099    }
100
101    /**
102     * Test {@link AcmeJsonResource#invalidate()}.
103     */
104    @Test
105    public void testInvalidate() {
106        var login = TestUtils.login();
107
108        var resource = new DummyJsonResource(login, LOCATION_URL);
109        assertThat(resource.isValid()).isFalse();
110        assertUpdateInvoked(resource, 0);
111
112        resource.setJSON(JSON_DATA);
113        assertThat(resource.isValid()).isTrue();
114        assertUpdateInvoked(resource, 0);
115
116        resource.invalidate();
117        assertThat(resource.isValid()).isFalse();
118        assertUpdateInvoked(resource, 0);
119
120        assertThat(resource.getJSON()).isEqualTo(JSON_DATA);
121        assertThat(resource.isValid()).isTrue();
122        assertUpdateInvoked(resource, 1);
123    }
124
125    /**
126     * Assert that {@link AcmeJsonResource#update()} has been invoked a given number of
127     * times.
128     *
129     * @param resource
130     *            {@link AcmeJsonResource} to test
131     * @param count
132     *            Expected number of times
133     */
134    private static void assertUpdateInvoked(AcmeJsonResource resource, int count) {
135        var dummy = (DummyJsonResource) resource;
136        assertThat(dummy.updateCount).as("update counter").isEqualTo(count);
137    }
138
139    /**
140     * Minimum implementation of {@link AcmeJsonResource}.
141     */
142    private static class DummyJsonResource extends AcmeJsonResource {
143        private static final long serialVersionUID = -6459238185161771948L;
144
145        private int updateCount = 0;
146
147        public DummyJsonResource(Login login, URL location) {
148            super(login, location);
149        }
150
151        public DummyJsonResource(Login login, URL location, JSON json, @Nullable Instant retryAfter) {
152            super(login, location);
153            setJSON(json);
154            setRetryAfter(retryAfter);
155        }
156
157        @Override
158        public Optional<Instant> fetch() throws AcmeException {
159            // fetch() is tested individually in all AcmeJsonResource subclasses.
160            // Here we just simulate the update, by setting a JSON.
161            updateCount++;
162            setJSON(JSON_DATA);
163            return Optional.empty();
164        }
165    }
166
167}