001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2023-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.web.testlib;
019
020import static com.google.common.truth.Truth.assertThat;
021import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
022import static com.google.common.util.concurrent.Futures.immediateFuture;
023
024import static dev.enola.common.context.testlib.SingletonRule.$;
025
026import static org.junit.Assert.assertThrows;
027
028import com.google.common.net.MediaType;
029
030import dev.enola.common.context.Context;
031import dev.enola.common.context.TLC;
032import dev.enola.common.context.testlib.SingletonRule;
033import dev.enola.common.io.mediatype.MediaTypeProviders;
034import dev.enola.common.io.resource.ResourceProviders;
035import dev.enola.common.io.resource.StringResource;
036import dev.enola.web.StaticWebHandler;
037import dev.enola.web.WebHandlers;
038import dev.enola.web.WebServer;
039
040import org.junit.Rule;
041import org.junit.Test;
042
043import java.io.IOException;
044import java.net.URI;
045
046public abstract class WebServerTestAbstract {
047
048    public @Rule SingletonRule r = $(MediaTypeProviders.set(new MediaTypeProviders()));
049
050    protected abstract WebServer create(WebHandlers handlers) throws IOException;
051
052    private final WebHandlers handlers() {
053        var handlers = new WebHandlers();
054
055        var hello = StringResource.of("hello, world", MediaType.PLAIN_TEXT_UTF_8);
056        handlers.register("/hello", uri -> immediateFuture(hello));
057
058        handlers.register("/abc/xyz/", new StaticWebHandler("/abc/xyz/", "static"));
059
060        handlers.register(
061                "/error1", uri -> immediateFailedFuture(new IllegalArgumentException("oink")));
062        handlers.register(
063                "/error2",
064                uri -> {
065                    throw new IllegalArgumentException("oink");
066                });
067
068        handlers.register(
069                "/context",
070                uri -> immediateFuture(StringResource.of(TLC.get(TestCtxKey.MAGIC).toString())));
071
072        return handlers;
073    }
074
075    @Test
076    public void testServer() throws IOException, InterruptedException {
077        try (var ctx = TLC.open()) {
078            ctx.push(TestCtxKey.MAGIC, 123);
079            try (var server = create(handlers())) {
080                server.start();
081
082                var prefix = "http://localhost:" + server.getInetAddress().getPort();
083                var rp = new ResourceProviders();
084
085                // IPv6 NOK :( var uri = URI.create("http://" + server.getInetAddress() + "/hello");
086                var uri1 = URI.create(prefix + "/hello");
087                var response1 = rp.getResource(uri1);
088                assertThat(response1.mediaType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8);
089                assertThat(response1.charSource().read()).isEqualTo("hello, world");
090
091                var contextURI = URI.create(prefix + "/context");
092                var responseFromTLC = rp.getResource(contextURI);
093                assertThat(responseFromTLC.charSource().read()).isEqualTo("123");
094
095                var uri2 = URI.create(prefix + "/abc/xyz/hello.txt");
096                var response2 = rp.getResource(uri2);
097                assertThat(response2.mediaType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8);
098                assertThat(response2.charSource().read()).isEqualTo("hi, you\n");
099
100                var error1 = URI.create(prefix + "/error1");
101                assertThrows(IOException.class, () -> rp.getNonNull(error1).byteSource().read());
102                // var errorResponse1 = rp.getResource(error1);
103                // Assert.assertThrows(IOException.class, () -> errorResponse1.charSource().read());
104
105                var error2 = URI.create(prefix + "/error2");
106                assertThrows(IOException.class, () -> rp.getNonNull(error2).byteSource().read());
107                // var errorResponse2 = rp.getResource(error2);
108                // Assert.assertThrows(IOException.class, () -> errorResponse2.charSource().read());
109
110                // TODO expect HTTP Error 500
111            }
112        }
113    }
114
115    private enum TestCtxKey implements Context.Key<Integer> {
116        MAGIC
117    }
118}