diff --git a/src/main/java/es/uvigo/esei/dai/hybridserver/HybridServer.java b/src/main/java/es/uvigo/esei/dai/hybridserver/HybridServer.java
new file mode 100644
index 0000000000000000000000000000000000000000..d3a3912c05bea8820310473eaeffe06b02447bb0
--- /dev/null
+++ b/src/main/java/es/uvigo/esei/dai/hybridserver/HybridServer.java
@@ -0,0 +1,88 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Map;
+import java.util.Properties;
+
+public class HybridServer implements AutoCloseable {
+ private static final int SERVICE_PORT = 8888;
+ private Thread serverThread;
+ private boolean stop;
+
+ public HybridServer() {
+ // TODO Inicializar con los parámetros por defecto
+ }
+
+ public HybridServer(Map pages) {
+ // TODO Inicializar con la base de datos en memoria conteniendo "pages"
+ }
+
+ public HybridServer(Properties properties) {
+ // TODO Inicializar con los parámetros recibidos
+ }
+
+ public int getPort() {
+ return SERVICE_PORT;
+ }
+
+ public void start() {
+ this.serverThread = new Thread() {
+ @Override
+ public void run() {
+ try (final ServerSocket serverSocket = new ServerSocket(SERVICE_PORT)) {
+ while (true) {
+ try (Socket socket = serverSocket.accept()) {
+ if (stop)
+ break;
+
+ // TODO Responder al cliente
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ };
+
+ this.stop = false;
+ this.serverThread.start();
+ }
+
+ @Override
+ public void close() {
+ this.stop = true;
+
+ try (Socket socket = new Socket("localhost", SERVICE_PORT)) {
+ // Esta conexión se hace, simplemente, para "despertar" el hilo servidor
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ this.serverThread.join();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ this.serverThread = null;
+ }
+}
diff --git a/src/main/java/es/uvigo/esei/dai/hybridserver/Launcher.java b/src/main/java/es/uvigo/esei/dai/hybridserver/Launcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..8226d68e33e400eb25cf79074f5d0247714a9e82
--- /dev/null
+++ b/src/main/java/es/uvigo/esei/dai/hybridserver/Launcher.java
@@ -0,0 +1,24 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver;
+
+public class Launcher {
+ public static void main(String[] args) {
+ // TODO Ejecutar el servidor
+ }
+}
diff --git a/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPHeaders.java b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPHeaders.java
new file mode 100644
index 0000000000000000000000000000000000000000..15aade40f84a763047e59047d07fca4d9505e182
--- /dev/null
+++ b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPHeaders.java
@@ -0,0 +1,35 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.http;
+
+public enum HTTPHeaders {
+ CONTENT_LENGTH("Content-Length"),
+ CONTENT_TYPE("Content-Type"),
+ HTTP_1_1("HTTP/1.1"),
+ CONNECTION("Connection");
+
+ private final String header;
+
+ private HTTPHeaders(String header) {
+ this.header = header;
+ }
+
+ public String getHeader() {
+ return this.header;
+ }
+}
diff --git a/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPParseException.java b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPParseException.java
new file mode 100644
index 0000000000000000000000000000000000000000..73a54ffb8084030f5e365ad97d329f9bd5c141d5
--- /dev/null
+++ b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPParseException.java
@@ -0,0 +1,41 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.http;
+
+public class HTTPParseException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public HTTPParseException() {
+ }
+
+ public HTTPParseException(String message) {
+ super(message);
+ }
+
+ public HTTPParseException(Throwable cause) {
+ super(cause);
+ }
+
+ public HTTPParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public HTTPParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPRequest.java b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a1c7077084a561463448db35c4de9b3ba8d5415f
--- /dev/null
+++ b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPRequest.java
@@ -0,0 +1,89 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.http;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Map;
+
+public class HTTPRequest {
+ public HTTPRequest(Reader reader) throws IOException, HTTPParseException {
+ // TODO Completar. Cualquier error en el procesado debe lanzar una HTTPParseException
+ }
+
+ public HTTPRequestMethod getMethod() {
+ // TODO Completar
+ return null;
+ }
+
+ public String getResourceChain() {
+ // TODO Completar
+ return null;
+ }
+
+ public String[] getResourcePath() {
+ // TODO Completar
+ return null;
+ }
+
+ public String getResourceName() {
+ // TODO Completar
+ return null;
+ }
+
+ public Map getResourceParameters() {
+ // TODO Completar
+ return null;
+ }
+
+ public String getHttpVersion() {
+ // TODO Completar
+ return null;
+ }
+
+ public Map getHeaderParameters() {
+ // TODO Completar
+ return null;
+ }
+
+ public String getContent() {
+ // TODO Completar
+ return null;
+ }
+
+ public int getContentLength() {
+ // TODO Completar
+ return -1;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder().append(this.getMethod().name()).append(' ')
+ .append(this.getResourceChain()).append(' ').append(this.getHttpVersion()).append("\r\n");
+
+ for (Map.Entry param : this.getHeaderParameters().entrySet()) {
+ sb.append(param.getKey()).append(": ").append(param.getValue()).append("\r\n");
+ }
+
+ if (this.getContentLength() > 0) {
+ sb.append("\r\n").append(this.getContent());
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPRequestMethod.java b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPRequestMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..8ddef7b62a5d6f036b4b78165008e7f25cc5c7cf
--- /dev/null
+++ b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPRequestMethod.java
@@ -0,0 +1,29 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.http;
+
+public enum HTTPRequestMethod {
+ HEAD,
+ GET,
+ POST,
+ PUT,
+ DELETE,
+ TRACE,
+ OPTIONS,
+ CONNECT;
+}
\ No newline at end of file
diff --git a/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPResponse.java b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f97a5f433ba696157cd660d5e2950747327c838
--- /dev/null
+++ b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPResponse.java
@@ -0,0 +1,97 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.http;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.List;
+import java.util.Map;
+
+public class HTTPResponse {
+ public HTTPResponse() {
+ // TODO Completar
+ }
+
+ public HTTPResponseStatus getStatus() {
+ // TODO Completar
+ return null;
+ }
+
+ public void setStatus(HTTPResponseStatus status) {
+ }
+
+ public String getVersion() {
+ // TODO Completar
+ return null;
+ }
+
+ public void setVersion(String version) {
+ }
+
+ public String getContent() {
+ // TODO Completar
+ return null;
+ }
+
+ public void setContent(String content) {
+ }
+
+ public Map getParameters() {
+ // TODO Completar
+ return null;
+ }
+
+ public String putParameter(String name, String value) {
+ // TODO Completar
+ return null;
+ }
+
+ public boolean containsParameter(String name) {
+ // TODO Completar
+ return false;
+ }
+
+ public String removeParameter(String name) {
+ // TODO Completar
+ return null;
+ }
+
+ public void clearParameters() {
+ }
+
+ public List listParameters() {
+ // TODO Completar
+ return null;
+ }
+
+ public void print(Writer writer) throws IOException {
+ // TODO Completar
+ }
+
+ @Override
+ public String toString() {
+ try (final StringWriter writer = new StringWriter()) {
+ this.print(writer);
+
+ return writer.toString();
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected I/O exception", e);
+ }
+ }
+}
diff --git a/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPResponseStatus.java b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPResponseStatus.java
new file mode 100644
index 0000000000000000000000000000000000000000..e20ab1fe1ee3614e0f10b390ccdbcbee5ae983ca
--- /dev/null
+++ b/src/main/java/es/uvigo/esei/dai/hybridserver/http/HTTPResponseStatus.java
@@ -0,0 +1,81 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.http;
+
+public enum HTTPResponseStatus {
+ S100("Continue"),
+ S101("Switching Protocols"),
+ S200("OK"),
+ S201("Created"),
+ S202("Accepted"),
+ S203("Non-Authoritative Information"),
+ S204("No Content"),
+ S205("Reset Content"),
+ S206("Partial Content"),
+ S300("Multiple Choices"),
+ S301("Moved Permanently"),
+ S302("Found"),
+ S303("See Other"),
+ S304("Not Modified"),
+ S305("Use Proxy"),
+ S307("Temporary Redirect"),
+ S400("Bad Request"),
+ S401("Unauthorized"),
+ S402("Payment Required"),
+ S403("Forbidden"),
+ S404("Not Found"),
+ S405("Method Not Allowed"),
+ S406("Not Acceptable"),
+ S407("Proxy Authentication Required"),
+ S408("Request Time-out"),
+ S409("Conflict"),
+ S410("Gone"),
+ S411("Length Required"),
+ S412("Precondition Failed"),
+ S413("Request Entity Too Large"),
+ S414("Request-URI Too Large"),
+ S415("Unsupported Media Type"),
+ S416("Requested range not satisfiable"),
+ S417("Expectation Failed"),
+ S500("Internal Server Error"),
+ S501("Not Implemented"),
+ S502("Bad Gateway"),
+ S503("Service Unavailable"),
+ S504("Gateway Time-out"),
+ S505("HTTP Version not supported");
+
+ private final int code;
+ private final String status;
+
+ private HTTPResponseStatus(String status) {
+ this.code = Integer.parseInt(this.name().substring(1));
+ this.status = status;
+ }
+
+ public int getCode() {
+ return this.code;
+ }
+
+ public String getStatus() {
+ return this.status;
+ }
+
+ public static HTTPResponseStatus forCode(int code) {
+ return HTTPResponseStatus.valueOf("S" + code);
+ }
+}
diff --git a/src/main/java/es/uvigo/esei/dai/hybridserver/http/MIME.java b/src/main/java/es/uvigo/esei/dai/hybridserver/http/MIME.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e79d9d26742ecfa094d8acbcb6bbe14ac894fe0
--- /dev/null
+++ b/src/main/java/es/uvigo/esei/dai/hybridserver/http/MIME.java
@@ -0,0 +1,34 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.http;
+
+public enum MIME {
+ APPLICATION_XML("application/xml"),
+ FORM("application/x-www-form-urlencoded"),
+ TEXT_HTML("text/html");
+
+ private String mime;
+
+ private MIME(String mime) {
+ this.mime = mime;
+ }
+
+ public String getMime() {
+ return this.mime;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/HybridServerFirstReleaseTestSuite.java b/src/test/java/es/uvigo/esei/dai/hybridserver/HybridServerFirstReleaseTestSuite.java
new file mode 100644
index 0000000000000000000000000000000000000000..0736a6d652b309884770501a85b3250d8bcda8dc
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/HybridServerFirstReleaseTestSuite.java
@@ -0,0 +1,30 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver;
+
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
+@Suite
+@SelectClasses({
+ Week1TestSuite.class,
+ Week2TestSuite.class,
+ Week3TestSuite.class
+})
+public class HybridServerFirstReleaseTestSuite {
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/Week1TestSuite.java b/src/test/java/es/uvigo/esei/dai/hybridserver/Week1TestSuite.java
new file mode 100644
index 0000000000000000000000000000000000000000..dfb32df348704236622b75640a694ea7fe4685c9
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/Week1TestSuite.java
@@ -0,0 +1,33 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver;
+
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
+import es.uvigo.esei.dai.hybridserver.week1.HTTPRequestResponseSuite;
+import es.uvigo.esei.dai.hybridserver.week1.WelcomePageTest;
+
+@Suite
+@SelectClasses({
+ WelcomePageTest.class,
+ HTTPRequestResponseSuite.class
+})
+public class Week1TestSuite {
+
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/Week2TestSuite.java b/src/test/java/es/uvigo/esei/dai/hybridserver/Week2TestSuite.java
new file mode 100644
index 0000000000000000000000000000000000000000..b81b96d7ddd4d6150cc153f3b87e51f473c56b67
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/Week2TestSuite.java
@@ -0,0 +1,33 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver;
+
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
+import es.uvigo.esei.dai.hybridserver.week1.HTTPRequestResponseSuite;
+import es.uvigo.esei.dai.hybridserver.week2.ClientRequetsTest;
+
+@Suite
+@SelectClasses({
+ ClientRequetsTest.class,
+ HTTPRequestResponseSuite.class
+})
+public class Week2TestSuite {
+
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/Week3TestSuite.java b/src/test/java/es/uvigo/esei/dai/hybridserver/Week3TestSuite.java
new file mode 100644
index 0000000000000000000000000000000000000000..d99e12f1e12c41ce5e5ec400f838899b4795e2ee
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/Week3TestSuite.java
@@ -0,0 +1,33 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver;
+
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
+import es.uvigo.esei.dai.hybridserver.week3.ClientRequestsWithDatabaseTest;
+import es.uvigo.esei.dai.hybridserver.week3.CustomPortTest;
+
+@Suite
+@SelectClasses({
+ CustomPortTest.class,
+ ClientRequestsWithDatabaseTest.class
+})
+public class Week3TestSuite {
+
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/utils/HybridServerTestCase.java b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/HybridServerTestCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..18d9d5483d0aea34bf4f1226bb43f2ab9e2bdd1f
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/HybridServerTestCase.java
@@ -0,0 +1,47 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.utils;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Timeout;
+
+import es.uvigo.esei.dai.hybridserver.HybridServer;
+
+@Timeout(5L)
+public abstract class HybridServerTestCase {
+ protected HybridServer server;
+ protected String url;
+
+ @BeforeEach
+ public void startServer() {
+ this.server = createHybridServer();
+ this.url = String.format("http://localhost:%d/", this.server.getPort());
+
+ this.server.start();
+ }
+
+ @AfterEach
+ public void stopServer() {
+ this.server.close();
+ }
+
+ protected HybridServer createHybridServer() {
+ return new HybridServer();
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/utils/JdbcTestCase.java b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/JdbcTestCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..d3341759279da22d60cf94c6540e350314804071
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/JdbcTestCase.java
@@ -0,0 +1,86 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.utils;
+
+import java.sql.Connection;
+
+import org.dbunit.IDatabaseTester;
+import org.dbunit.database.IDatabaseConnection;
+import org.dbunit.dataset.IDataSet;
+import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+
+public abstract class JdbcTestCase {
+ private IDatabaseTester tester;
+ private IDatabaseConnection connection;
+
+ @BeforeEach
+ public void setUpJdbc() throws Exception {
+ this.tester = this.createDatabaseTester();
+
+ this.connection = this.tester.getConnection();
+
+ this.tester.setDataSet(getDataSet());
+
+ this.tester.onSetup();
+ }
+
+ @AfterEach
+ public void tearDownJdbc() throws Exception {
+ try {
+ this.tester.onTearDown();
+ this.connection.close();
+ } finally {
+ this.connection = null;
+ }
+ }
+
+ protected IDatabaseTester createDatabaseTester() throws ClassNotFoundException {
+ return new MySqlJdbcDatabaseTester(getConnectionUrl(), getUsername(), getPassword());
+ }
+
+ protected String getSchema() {
+ return "hstestdb";
+ }
+
+ protected String getConnectionUrl() {
+ // Esta base de datos debe existir con las tablas creadas
+ // y el usuario debe tener acceso.
+ return "jdbc:mysql://localhost/" + this.getSchema();
+ }
+
+ protected String getUsername() {
+ return "hsdb";
+ }
+
+ protected String getPassword() {
+ return "hsdbpass";
+ }
+
+ protected IDataSet getDataSet() throws Exception {
+ return new FlatXmlDataSetBuilder()
+ .setMetaDataSetFromDtd(getClass().getResourceAsStream("dataset.dtd"))
+ .setCaseSensitiveTableNames(false)
+ .setColumnSensing(true).build(getClass().getResourceAsStream("dataset.xml"));
+ }
+
+ protected Connection getConnection() throws Exception {
+ return this.connection.getConnection();
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/utils/MySqlJdbcDatabaseTester.java b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/MySqlJdbcDatabaseTester.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae782e3a8b125383b0e5d0e624e593c4ba1774cd
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/MySqlJdbcDatabaseTester.java
@@ -0,0 +1,53 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.utils;
+
+import org.dbunit.JdbcDatabaseTester;
+import org.dbunit.database.DatabaseConfig;
+import org.dbunit.database.IDatabaseConnection;
+import org.dbunit.ext.mysql.MySqlDataTypeFactory;
+import org.dbunit.ext.mysql.MySqlMetadataHandler;
+
+public class MySqlJdbcDatabaseTester extends JdbcDatabaseTester {
+ private final static String MYSQL_DRIVER = "com.mysql.cj.jdbc.Driver";
+
+ public MySqlJdbcDatabaseTester(String connectionUrl) throws ClassNotFoundException {
+ super(MYSQL_DRIVER, connectionUrl);
+ }
+
+ public MySqlJdbcDatabaseTester(String connectionUrl, String username, String password) throws ClassNotFoundException {
+ super(MYSQL_DRIVER, connectionUrl, username, password);
+ }
+
+ public MySqlJdbcDatabaseTester(String connectionUrl, String username, String password, String schema)
+ throws ClassNotFoundException {
+ super(MYSQL_DRIVER, connectionUrl, username, password, schema);
+ }
+
+ @Override
+ public IDatabaseConnection getConnection() throws Exception {
+ final IDatabaseConnection connection = super.getConnection();
+
+ connection.getConfig().setProperty(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, true);
+
+ connection.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory());
+ connection.getConfig().setProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER, new MySqlMetadataHandler());
+
+ return connection;
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/utils/TestUtils.java b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/TestUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..d789de8a6ed98d6ce605cc3a1c94cd011a41cf77
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/TestUtils.java
@@ -0,0 +1,256 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.utils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.fluent.Form;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.util.EntityUtils;
+
+public final class TestUtils {
+ private TestUtils() {
+ }
+
+ /**
+ * Devuelve un reader a un fichero en el classpath.
+ *
+ * @param filePath
+ * ruta absoluta del fichero, que debe estar en el classpath del sistema.
+ * @return reader asociado al fichero o {@code null} si el fichero no existe.
+ */
+ public static final Reader openReaderToFile(String filePath) {
+ return new InputStreamReader(TestUtils.class.getResourceAsStream(filePath));
+ }
+
+ /**
+ * Realiza una petición por GET y devuelve el contenido de la respuesta.
+ *
+ * La petición se hace en UTF-8 y solicitando el cierre de la conexión.
+ *
+ * Este método comprueba que el código de respuesta HTTP sea 200 OK.
+ *
+ * @param url
+ * URL de la página solicitada.
+ * @return contenido de la respuesta HTTP.
+ * @throws IOException
+ * en el caso de que se produzca un error de conexión.
+ */
+ public static String getContent(String url) throws IOException {
+ final HttpResponse response = Request.Get(url)
+ .addHeader("Connection", "close")
+ .addHeader("Content-encoding", "UTF-8")
+ .execute()
+ .returnResponse();
+
+ assertEquals(200, response.getStatusLine().getStatusCode());
+
+ return EntityUtils.toString(response.getEntity());
+ }
+
+ /**
+ * Realiza una petición por GET y devuelve el contenido de la respuesta.
+ *
+ * La petición se hace en UTF-8 y solicitando el cierre de la conexión.
+ *
+ * Este método comprueba que el código de respuesta HTTP sea 200 OK.
+ *
+ * @param url
+ * URL de la página solicitada.
+ * @return contenido de la respuesta HTTP.
+ * @throws IOException
+ * en el caso de que se produzca un error de conexión.
+ */
+ public static String getContentWithType(String url, String contentType) throws IOException {
+ final HttpResponse response = Request.Get(url)
+ .addHeader("Connection", "close")
+ .addHeader("Content-encoding", "UTF-8")
+ .execute()
+ .returnResponse();
+
+ assertEquals(200, response.getStatusLine().getStatusCode());
+ assertEquals(contentType, response.getEntity().getContentType().getValue());
+
+ return EntityUtils.toString(response.getEntity());
+ }
+
+ /**
+ * Realiza una petición por GET y devuelve el código de la respuesta HTTP.
+ *
+ * La petición se hace en UTF-8 y solicitando el cierre de la conexión.
+ *
+ * Este método comprueba que el código de respuesta HTTP sea 200 OK.
+ *
+ * @param url
+ * URL de la página solicitada.
+ * @return código de la respuesta HTTP.
+ * @throws IOException
+ * en el caso de que se produzca un error de conexión.
+ */
+ public static int getStatus(String url) throws IOException {
+ return Request.Get(url)
+ .addHeader("Connection", "close")
+ .addHeader("Content-encoding", "UTF-8")
+ .execute()
+ .returnResponse().getStatusLine().getStatusCode();
+ }
+
+ /**
+ * Realiza una petición por POST y devuelve el contenido de la respuesta.
+ *
+ * La petición se hace en UTF-8 y solicitando el cierre de la conexión.
+ *
+ * Este método comprueba que el código de respuesta HTTP sea 200 OK.
+ *
+ * @param url
+ * URL de la página solicitada.
+ * @param content
+ * parámetros que se incluirán en la petición HTTP.
+ * @return contenido de la respuesta HTTP.
+ * @throws IOException
+ * en el caso de que se produzca un error de conexión.
+ */
+ public static String postContent(String url, Map content) throws IOException {
+ final HttpResponse response = Request.Post(url)
+ .addHeader("Connection", "close")
+ .addHeader("Content-encoding", "UTF-8")
+ .bodyForm(mapToNameValuePair(content))
+ .execute().returnResponse();
+
+ assertEquals(200, response.getStatusLine().getStatusCode());
+
+ return EntityUtils.toString(response.getEntity());
+ }
+
+ /**
+ * Realiza una petición por POST y devuelve el código de la respuesta.
+ *
+ * La petición se hace en UTF-8 y solicitando el cierre de la conexión.
+ *
+ * Este método comprueba que el código de respuesta HTTP sea 200 OK.
+ *
+ * @param url
+ * URL de la página solicitada.
+ * @param content
+ * parámetros que se incluirán en la petición HTTP.
+ * @return código de la respuesta HTTP.
+ * @throws IOException
+ * en el caso de que se produzca un error de conexión.
+ */
+ public static int postStatus(String url, Map content) throws IOException {
+ return Request.Post(url)
+ .addHeader("Connection", "close")
+ .addHeader("Content-encoding", "UTF-8")
+ .bodyForm(mapToNameValuePair(content))
+ .execute().returnResponse().getStatusLine().getStatusCode();
+ }
+
+ /**
+ * Realiza una petición por DELETE y devuelve el código de la respuesta HTTP.
+ *
+ * La petición se hace en UTF-8 y solicitando el cierre de la conexión.
+ *
+ * Este método comprueba que el código de respuesta HTTP sea 200 OK.
+ *
+ * @param url
+ * URL de la página solicitada.
+ * @return código de la respuesta HTTP.
+ * @throws IOException
+ * en el caso de que se produzca un error de conexión.
+ */
+ public static int deleteStatus(String url) throws IOException {
+ return Request.Delete(url)
+ .addHeader("Connection", "close")
+ .addHeader("Content-encoding", "UTF-8")
+ .execute().returnResponse().getStatusLine().getStatusCode();
+ }
+
+ /**
+ * Extrae un UUID de un texto. En el caso de que existan varios UUID en el texto se devolverá, únicamente, el primero
+ * de ellos.
+ *
+ * @param text
+ * texto del cual se quiere extraer el UUID.
+ * @return UUID encontrado en el texto o null
en el caso de que no exista ninguno.
+ */
+ public static String extractUUIDFromText(String text) {
+ final String patternString = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
+
+ final Pattern pattern = Pattern.compile(patternString);
+ final Matcher matcher = pattern.matcher(text);
+
+ return matcher.find() ? matcher.group() : null;
+ }
+
+ /**
+ * Lee por completo un recurso en el classpath, cuya ruta es relativa a una clase.
+ *
+ * @param clazz
+ * clase de referencia para obtener el recurso.
+ * @param resourcePath
+ * ruta al recurso relativa a la clase.
+ * @return contenido del flujo de entrada.
+ */
+ public static String readResourceToString(Class> clazz, String resourcePath) {
+ return readToString(clazz.getResourceAsStream(resourcePath));
+ }
+
+ /**
+ * Lee por completo un InputStream y devuelve su contenido en forma de cadena de texto.
+ *
+ * @param is
+ * flujo de entrada del que se va a leer.
+ * @return contenido del flujo de entrada.
+ */
+ public static String readToString(InputStream is) {
+ try (final Reader reader = new InputStreamReader(is)) {
+ final char[] buffer = new char[4096];
+ final StringBuilder sb = new StringBuilder();
+
+ int read;
+ while ((read = reader.read(buffer, 0, buffer.length)) != -1) {
+ sb.append(buffer, 0, read);
+ }
+
+ return sb.toString();
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+
+ private static List mapToNameValuePair(Map map) {
+ final Form form = Form.form();
+
+ for (Map.Entry entry : map.entrySet()) {
+ form.add(entry.getKey(), entry.getValue());
+ }
+
+ return form.build();
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/Column.java b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/Column.java
new file mode 100644
index 0000000000000000000000000000000000000000..bce41129472b6991a681bbcb86d13d08dc97017a
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/Column.java
@@ -0,0 +1,51 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.utils.matchers;
+
+public class Column {
+ private final Table table;
+ private final String name;
+ private String value;
+
+ Column(Table table, String name) {
+ this.table = table;
+ this.name = name;
+ }
+
+ public TableHas withValue(String value) {
+ this.value = value;
+ this.table.addColumn(this);
+
+ return new TableHas(table);
+ }
+
+ public TableHasNot withoutValue(String value) {
+ this.value = value;
+ this.table.addColumn(this);
+
+ return new TableHasNot(table);
+ }
+
+ String getName() {
+ return name;
+ }
+
+ String getValue() {
+ return value;
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/EqualsToIgnoringSpacesAndCaseMatcher.java b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/EqualsToIgnoringSpacesAndCaseMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6f66d45cdb24bb9793bdcc0543caec4a708fc47
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/EqualsToIgnoringSpacesAndCaseMatcher.java
@@ -0,0 +1,52 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.utils.matchers;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+
+/**
+ * Comprueba que dos cadenas de texto son iguales ignorando los espacios en blanco y las mayúsculas y minúsculas.
+ */
+public class EqualsToIgnoringSpacesAndCaseMatcher extends BaseMatcher {
+ private final String expected;
+
+ public EqualsToIgnoringSpacesAndCaseMatcher(String expected) {
+ this.expected = expected;
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ if (item instanceof String) {
+ final String text = (String) item;
+
+ return expected.replaceAll("\\s", "").equalsIgnoreCase(text.replaceAll("\\s", ""));
+ }
+
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Strings are not equal ignoring whitespaces and case");
+ }
+
+ public static EqualsToIgnoringSpacesAndCaseMatcher equalsToIgnoringSpacesAndCase(String expected) {
+ return new EqualsToIgnoringSpacesAndCaseMatcher(expected);
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/EqualsToIgnoringSpacesMatcher.java b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/EqualsToIgnoringSpacesMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb4a68836c5d89d0582df40c5653de35f8d3cb6d
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/EqualsToIgnoringSpacesMatcher.java
@@ -0,0 +1,52 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.utils.matchers;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+
+/**
+ * Comprueba que dos cadenas de texto son iguales ignorando los espacios en blanco.
+ */
+public class EqualsToIgnoringSpacesMatcher extends BaseMatcher {
+ private final String expected;
+
+ public EqualsToIgnoringSpacesMatcher(String expected) {
+ this.expected = expected;
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ if (item instanceof String) {
+ final String text = (String) item;
+
+ return expected.replaceAll("\\s", "").equals(text.replaceAll("\\s", ""));
+ }
+
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Strings are not equal ignoring whitespaces");
+ }
+
+ public static EqualsToIgnoringSpacesMatcher equalsToIgnoringSpaces(String expected) {
+ return new EqualsToIgnoringSpacesMatcher(expected);
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/Table.java b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/Table.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac89c0b8f8bf25e9a4393d6d57059f82e1cf7aee
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/Table.java
@@ -0,0 +1,57 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.utils.matchers;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class Table {
+ private final String schema;
+ private final String name;
+ private final List columns;
+
+ Table(String schema, String name) {
+ this.schema = schema;
+ this.name = name;
+ this.columns = new LinkedList<>();
+ }
+
+ public Column withColumn(String name) {
+ return new Column(this, name);
+ }
+
+ void addColumn(Column column) {
+ this.columns.add(column);
+ }
+
+ String getSchema() {
+ return schema;
+ }
+
+ String getName() {
+ return name;
+ }
+
+ String getQualifiedName() {
+ return this.schema + "." + this.getName();
+ }
+
+ List getColumns() {
+ return columns;
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/TableHas.java b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/TableHas.java
new file mode 100644
index 0000000000000000000000000000000000000000..a980338e07e9966c873c9e3a50b8809346695ea7
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/TableHas.java
@@ -0,0 +1,57 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.utils.matchers;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+import org.hamcrest.Description;
+
+public class TableHas extends TableMatcher {
+ TableHas(Table table) {
+ super(table);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("No row matching query: " + this.toSQL());
+ }
+
+ @Override
+ protected String toSQL() {
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append("SELECT * FROM ").append(table.getQualifiedName());
+
+ final List columns = table.getColumns();
+ if (!columns.isEmpty()) {
+ sb.append(" WHERE 1=1");
+ for (Column column : columns) {
+ sb.append(" AND ").append(column.getName()).append(" = \"").append(column.getValue()).append('"');
+ }
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ protected boolean checkResults(ResultSet results) throws SQLException {
+ return results.next();
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/TableHasNot.java b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/TableHasNot.java
new file mode 100644
index 0000000000000000000000000000000000000000..68d67532cb19f84c70047603703d6966f21b4dde
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/TableHasNot.java
@@ -0,0 +1,57 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.utils.matchers;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+import org.hamcrest.Description;
+
+public class TableHasNot extends TableMatcher {
+ TableHasNot(Table table) {
+ super(table);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("There is a row matching query: " + this.toSQL());
+ }
+
+ @Override
+ protected String toSQL() {
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append("SELECT * FROM ").append(table.getQualifiedName());
+
+ final List columns = table.getColumns();
+ if (!columns.isEmpty()) {
+ sb.append(" WHERE 1=1");
+ for (Column column : columns) {
+ sb.append(" AND ").append(column.getName()).append(" = \"").append(column.getValue()).append('"');
+ }
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ protected boolean checkResults(ResultSet results) throws SQLException {
+ return !results.next();
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/TableMatcher.java b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/TableMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..eca6224e234f8476f7cf96723839bdc710dfea35
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/utils/matchers/TableMatcher.java
@@ -0,0 +1,56 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.utils.matchers;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.hamcrest.TypeSafeMatcher;
+
+public abstract class TableMatcher extends TypeSafeMatcher {
+ protected final Table table;
+
+ TableMatcher(Table table) {
+ this.table = table;
+ }
+
+ @Override
+ protected boolean matchesSafely(Connection connection) {
+ try (Statement statement = connection.createStatement()) {
+ try (ResultSet results = statement.executeQuery(this.toSQL())) {
+ return checkResults(results);
+ }
+ } catch (SQLException e) {
+ return false;
+ }
+ }
+
+ protected abstract String toSQL();
+
+ protected abstract boolean checkResults(ResultSet results) throws SQLException;
+
+ public static Table hasTable(String schema, String name) {
+ return new Table(schema, name);
+ }
+
+ public Column andColumn(String name) {
+ return new Column(this.table, name);
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPBadRequestsTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPBadRequestsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7063cd4308c91de2327747932c121fb85c1a1e7
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPBadRequestsTest.java
@@ -0,0 +1,86 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Named.named;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+import java.io.StringReader;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPParseException;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequest;
+
+@Tag("request")
+public class HTTPBadRequestsTest {
+ public static Stream badRequests() {
+ return Stream
+ .of(
+ arguments(
+ named(
+ "Missing method",
+ "/hello HTTP/1.1\r\n" + "Host: localhost\r\n" + "Accept: text/html\r\n"
+ + "Accept-Encoding: gzip,deflate\r\n"
+ )
+ ),
+ arguments(
+ named(
+ "Missing resource",
+ "GET HTTP/1.1\r\n" + "Host: localhost\r\n" + "Accept: text/html\r\n" + "Accept-Encoding: gzip,deflate\r\n"
+ )
+ ),
+ arguments(
+ named(
+ "Missing version",
+ "GET /hello\r\n" + "Host: localhost\r\n" + "Accept: text/html\r\n" + "Accept-Encoding: gzip,deflate\r\n"
+ )
+ ),
+ arguments(
+ named(
+ "Missing first line", "Host: localhost\r\n" + "Accept: text/html\r\n" + "Accept-Encoding: gzip,deflate\r\n"
+ )
+ ),
+ arguments(
+ named(
+ "Invalid header",
+ "GET /hello/world.html?country=Spain&province=Ourense&city=Ourense HTTP/1.1\r\n" + "Host\r\n"
+ + "Accept: text/html\r\n" + "Accept-Encoding: gzip,deflate\r\n"
+ )
+ ),
+ arguments(
+ named(
+ "Missing new line after header",
+ "GET /hello/world.html?country=Spain&province=Ourense&city=Ourense HTTP/1.1" + "Host: localhost\r\n"
+ + "Accept: text/html\r\n" + "Accept-Encoding: gzip,deflate\r\n"
+ )
+ )
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("badRequests")
+ public void testThatThrowsHTTPParseException(final String requestText) {
+ assertThrows(HTTPParseException.class, () -> new HTTPRequest(new StringReader(requestText)));
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETParametersTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETParametersTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..436ba2e166aaae285e5c55727a6493e3704ffbfb
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETParametersTest.java
@@ -0,0 +1,96 @@
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.ArrayMatching.arrayContaining;
+import static org.hamcrest.collection.IsMapContaining.hasEntry;
+import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
+
+import java.io.StringReader;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPHeaders;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequest;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequestMethod;
+
+@Tag("request")
+public class HTTPRequestGETParametersTest {
+ private String requestText;
+ private HTTPRequest request;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ requestText = "GET /hello/world.html?country=Spain&province=Ourense&city=Ourense HTTP/1.1\r\n"
+ + "Host: localhost\r\n" + "Accept: text/html\r\n"
+ + "Accept-Encoding: gzip,deflate\r\n";
+
+ request = new HTTPRequest(new StringReader(requestText + "\r\n"));
+ }
+
+ @Test
+ public final void testGetMethod() {
+ assertThat(request.getMethod(), is(equalTo(HTTPRequestMethod.GET)));
+ }
+
+ @Test
+ public final void testGetResourceChain() {
+ assertThat(
+ request.getResourceChain(), is(equalTo("/hello/world.html?country=Spain&province=Ourense&city=Ourense"))
+ );
+ }
+
+ @Test
+ public final void testGetResourcePath() {
+ assertThat(request.getResourcePath(), is(arrayContaining("hello", "world.html")));
+ }
+
+ @Test
+ public final void testGetResourceName() {
+ assertThat(request.getResourceName(), is(equalTo("hello/world.html")));
+ }
+
+ @Test
+ public final void testGetHttpVersion() {
+ assertThat(request.getHttpVersion(), is(equalTo(HTTPHeaders.HTTP_1_1.getHeader())));
+ }
+
+ @Test
+ public final void testGetResourceParameters() {
+ assertThat(
+ request.getResourceParameters(),
+ allOf(hasEntry("country", "Spain"), hasEntry("province", "Ourense"), hasEntry("city", "Ourense"))
+ );
+ assertThat(request.getResourceParameters(), is(aMapWithSize(3)));
+ }
+
+ @Test
+ public final void testGetHeaderParameters() {
+ assertThat(
+ request.getHeaderParameters(),
+ allOf(hasEntry("Host", "localhost"), hasEntry("Accept", "text/html"), hasEntry("Accept-Encoding", "gzip,deflate"))
+ );
+ assertThat(request.getHeaderParameters(), is(aMapWithSize(3)));
+ }
+
+ @Test
+ public final void testGetContent() {
+ assertThat(request.getContent(), is(nullValue()));
+ }
+
+ @Test
+ public final void testGetContentLength() {
+ assertThat(request.getContentLength(), is(equalTo(0)));
+ }
+
+ @Test
+ public final void testToString() {
+ assertThat(request.toString(), is(equalTo(requestText)));
+ }
+
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETResourceTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETResourceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1d083e779571e6e363c7603dbbfb1aff6645412
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETResourceTest.java
@@ -0,0 +1,92 @@
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.ArrayMatching.arrayContaining;
+import static org.hamcrest.collection.IsMapContaining.hasEntry;
+import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
+import static org.hamcrest.collection.IsMapWithSize.anEmptyMap;
+
+import java.io.StringReader;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPHeaders;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequest;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequestMethod;
+
+@Tag("request")
+public class HTTPRequestGETResourceTest {
+ private String requestText;
+ private HTTPRequest request;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ this.requestText = "GET /hello HTTP/1.1\r\n"
+ + "Host: localhost\r\n"
+ + "Accept: text/html\r\n"
+ + "Accept-Encoding: gzip,deflate\r\n";
+
+ this.request = new HTTPRequest(new StringReader(this.requestText + "\r\n"));
+ }
+
+ @Test
+ public final void testGetMethod() {
+ assertThat(request.getMethod(), is(equalTo(HTTPRequestMethod.GET)));
+ }
+
+ @Test
+ public final void testGetResourceChain() {
+ assertThat(request.getResourceChain(), is(equalTo("/hello")));
+ }
+
+ @Test
+ public final void testGetResourcePath() {
+ assertThat(request.getResourcePath(), is(arrayContaining("hello")));
+ }
+
+ @Test
+ public final void testGetResourceName() {
+ assertThat(request.getResourceName(), is(equalTo("hello")));
+ }
+
+ @Test
+ public final void testGetHttpVersion() {
+ assertThat(request.getHttpVersion(), is(equalTo(HTTPHeaders.HTTP_1_1.getHeader())));
+ }
+
+ @Test
+ public final void testGetResourceParameters() {
+ assertThat(this.request.getResourceParameters(), is(anEmptyMap()));
+ }
+
+ @Test
+ public final void testGetHeaderParameters() {
+ assertThat(
+ request.getHeaderParameters(),
+ allOf(hasEntry("Host", "localhost"), hasEntry("Accept", "text/html"), hasEntry("Accept-Encoding", "gzip,deflate"))
+ );
+ assertThat(request.getHeaderParameters(), is(aMapWithSize(3)));
+ }
+
+ @Test
+ public final void testGetContent() {
+ assertThat(request.getContent(), is(nullValue()));
+ }
+
+ @Test
+ public final void testGetContentLength() {
+ assertThat(request.getContentLength(), is(equalTo(0)));
+ }
+
+ @Test
+ public final void testToString() {
+ assertThat(request.toString(), is(equalTo(requestText)));
+ }
+
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETResourcesTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETResourcesTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..232c5d1421183f9b2ad9361e19a6fb01d3268790
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETResourcesTest.java
@@ -0,0 +1,92 @@
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.ArrayMatching.arrayContaining;
+import static org.hamcrest.collection.IsMapContaining.hasEntry;
+import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
+import static org.hamcrest.collection.IsMapWithSize.anEmptyMap;
+
+import java.io.StringReader;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPHeaders;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequest;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequestMethod;
+
+@Tag("request")
+public class HTTPRequestGETResourcesTest {
+ private String requestText;
+ private HTTPRequest request;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ this.requestText = "GET /hello/world.html HTTP/1.1\r\n"
+ + "Host: localhost\r\n"
+ + "Accept: text/html\r\n"
+ + "Accept-Encoding: gzip,deflate\r\n";
+
+ this.request = new HTTPRequest(new StringReader(this.requestText + "\r\n"));
+ }
+
+ @Test
+ public final void testGetMethod() {
+ assertThat(request.getMethod(), is(equalTo(HTTPRequestMethod.GET)));
+ }
+
+ @Test
+ public final void testGetResourceChain() {
+ assertThat(request.getResourceChain(), is(equalTo("/hello/world.html")));
+ }
+
+ @Test
+ public final void testGetResourcePath() {
+ assertThat(request.getResourcePath(), is(arrayContaining("hello", "world.html")));
+ }
+
+ @Test
+ public final void testGetResourceName() {
+ assertThat(request.getResourceName(), is(equalTo("hello/world.html")));
+ }
+
+ @Test
+ public final void testGetHttpVersion() {
+ assertThat(request.getHttpVersion(), is(equalTo(HTTPHeaders.HTTP_1_1.getHeader())));
+ }
+
+ @Test
+ public final void testGetResourceParameters() {
+ assertThat(this.request.getResourceParameters(), is(anEmptyMap()));
+ }
+
+ @Test
+ public final void testGetHeaderParameters() {
+ assertThat(
+ request.getHeaderParameters(),
+ allOf(hasEntry("Host", "localhost"), hasEntry("Accept", "text/html"), hasEntry("Accept-Encoding", "gzip,deflate"))
+ );
+ assertThat(request.getHeaderParameters(), is(aMapWithSize(3)));
+ }
+
+ @Test
+ public final void testGetContent() {
+ assertThat(request.getContent(), is(nullValue()));
+ }
+
+ @Test
+ public final void testGetContentLength() {
+ assertThat(request.getContentLength(), is(equalTo(0)));
+ }
+
+ @Test
+ public final void testToString() {
+ assertThat(request.toString(), is(equalTo(requestText)));
+ }
+
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETRootTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETRootTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..250bb0ef77fcb53f87f8f88c453da3bf9745d972
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestGETRootTest.java
@@ -0,0 +1,87 @@
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsArrayWithSize.emptyArray;
+import static org.hamcrest.collection.IsMapContaining.hasEntry;
+import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
+import static org.hamcrest.collection.IsMapWithSize.anEmptyMap;
+import static org.hamcrest.text.IsEmptyString.emptyString;
+
+import java.io.StringReader;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPHeaders;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequest;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequestMethod;
+
+@Tag("request")
+public class HTTPRequestGETRootTest {
+ private String requestText;
+ private HTTPRequest request;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ this.requestText = "GET / HTTP/1.1\r\n"
+ + "Host: localhost\r\n";
+
+ this.request = new HTTPRequest(new StringReader(this.requestText + "\r\n"));
+ }
+
+ @Test
+ public final void testGetMethod() {
+ assertThat(request.getMethod(), is(equalTo(HTTPRequestMethod.GET)));
+ }
+
+ @Test
+ public final void testGetResourceChain() {
+ assertThat(request.getResourceChain(), is(equalTo("/")));
+ }
+
+ @Test
+ public final void testGetResourcePath() {
+ assertThat(request.getResourcePath(), is(emptyArray()));
+ }
+
+ @Test
+ public final void testGetResourceName() {
+ assertThat(request.getResourceName(), is(emptyString()));
+ }
+
+ @Test
+ public final void testGetHttpVersion() {
+ assertThat(request.getHttpVersion(), is(equalTo(HTTPHeaders.HTTP_1_1.getHeader())));
+ }
+
+ @Test
+ public final void testGetResourceParameters() {
+ assertThat(this.request.getResourceParameters(), is(anEmptyMap()));
+ }
+
+ @Test
+ public final void testGetHeaderParameters() {
+ assertThat(request.getHeaderParameters(), hasEntry("Host", "localhost"));
+ assertThat(request.getHeaderParameters(), is(aMapWithSize(1)));
+ }
+
+ @Test
+ public final void testGetContent() {
+ assertThat(request.getContent(), is(nullValue()));
+ }
+
+ @Test
+ public final void testGetContentLength() {
+ assertThat(request.getContentLength(), is(equalTo(0)));
+ }
+
+ @Test
+ public final void testToString() {
+ assertThat(request.toString(), is(equalTo(requestText)));
+ }
+
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestPOSTEncodedTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestPOSTEncodedTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2ac5b23c96ac8f5e5ba16faa55cda94af4bcc1c
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestPOSTEncodedTest.java
@@ -0,0 +1,112 @@
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsArrayWithSize.emptyArray;
+import static org.hamcrest.collection.IsMapContaining.hasEntry;
+import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
+import static org.hamcrest.text.IsEmptyString.emptyString;
+
+import java.io.StringReader;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPHeaders;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequest;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequestMethod;
+
+@Tag("request")
+public class HTTPRequestPOSTEncodedTest {
+ private String requestText;
+ private String encodedRequestText;
+ private HTTPRequest request;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ this.requestText = "POST / HTTP/1.1\r\n"
+ + "Host: localhost\r\n"
+ + "Content-Type: application/x-www-form-urlencoded\r\n"
+ + "Content-Length: 116\r\n\r\n"
+ + "message=Hello world!!&mensaje=¡¡Hola mundo!!&mensaxe=Ola mundo!!&mensagem=Olá mundo!!";
+
+ this.encodedRequestText = "POST / HTTP/1.1\r\n"
+ + "Host: localhost\r\n"
+ + "Content-Type: application/x-www-form-urlencoded\r\n"
+ + "Content-Length: 116\r\n\r\n"
+ + "message=Hello+world%21%21&mensaje=%C2%A1%C2%A1Hola+mundo%21%21&mensaxe=Ola+mundo%21%21&mensagem=Ol%C3%A1+mundo%21%21";
+
+ this.request = new HTTPRequest(new StringReader(this.encodedRequestText));
+ }
+
+ @Test
+ public final void testGetMethod() {
+ assertThat(request.getMethod(), is(equalTo(HTTPRequestMethod.POST)));
+ }
+
+ @Test
+ public final void testGetResourceChain() {
+ assertThat(request.getResourceChain(), is(equalTo("/")));
+ }
+
+ @Test
+ public final void testGetResourcePath() {
+ assertThat(request.getResourcePath(), is(emptyArray()));
+ }
+
+ @Test
+ public final void testGetResourceName() {
+ assertThat(request.getResourceName(), is(emptyString()));
+ }
+
+ @Test
+ public final void testGetHttpVersion() {
+ assertThat(request.getHttpVersion(), is(equalTo(HTTPHeaders.HTTP_1_1.getHeader())));
+ }
+
+ @Test
+ public final void testGetResourceParameters() {
+ assertThat(
+ request.getResourceParameters(),
+ allOf(
+ hasEntry("message", "Hello world!!"), hasEntry("mensaje", "¡¡Hola mundo!!"), hasEntry("mensaxe", "Ola mundo!!"),
+ hasEntry("mensagem", "Olá mundo!!")
+ )
+ );
+ assertThat(request.getResourceParameters(), is(aMapWithSize(4)));
+ }
+
+ @Test
+ public final void testGetHeaderParameters() {
+ assertThat(
+ request.getHeaderParameters(),
+ allOf(
+ hasEntry("Host", "localhost"), hasEntry("Content-Type", "application/x-www-form-urlencoded"),
+ hasEntry("Content-Length", "116")
+ )
+ );
+ assertThat(request.getHeaderParameters(), is(aMapWithSize(3)));
+ }
+
+ @Test
+ public final void testGetContent() {
+ assertThat(
+ request.getContent(),
+ is(equalTo("message=Hello world!!&mensaje=¡¡Hola mundo!!&mensaxe=Ola mundo!!&mensagem=Olá mundo!!"))
+ );
+ }
+
+ @Test
+ public final void testGetContentLength() {
+ assertThat(request.getContentLength(), is(equalTo(116)));
+ }
+
+ @Test
+ public final void testToString() {
+ assertThat(request.toString(), is(equalTo(requestText)));
+ }
+
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestPOSTMultipleParametersTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestPOSTMultipleParametersTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4383b9ff4e4756c86ab0f940db3ea0ff20fddfc6
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestPOSTMultipleParametersTest.java
@@ -0,0 +1,97 @@
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.ArrayMatching.arrayContaining;
+import static org.hamcrest.collection.IsMapContaining.hasEntry;
+import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
+
+import java.io.StringReader;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPHeaders;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequest;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequestMethod;
+
+@Tag("request")
+public class HTTPRequestPOSTMultipleParametersTest {
+ private String requestText;
+ private HTTPRequest request;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ this.requestText = "POST /resource HTTP/1.1\r\n"
+ + "Host: localhost\r\n"
+ + "Content-Length: 85\r\n\r\n"
+ + "message=Hello world!!&mensaje=¡¡Hola mundo!!&mensaxe=Ola mundo!!&mensagem=Olá mundo!!";
+
+ this.request = new HTTPRequest(new StringReader(this.requestText));
+ }
+
+ @Test
+ public final void testGetMethod() {
+ assertThat(request.getMethod(), is(equalTo(HTTPRequestMethod.POST)));
+ }
+
+ @Test
+ public final void testGetResourceChain() {
+ assertThat(request.getResourceChain(), is(equalTo("/resource")));
+ }
+
+ @Test
+ public final void testGetResourcePath() {
+ assertThat(request.getResourcePath(), is(arrayContaining("resource")));
+ }
+
+ @Test
+ public final void testGetResourceName() {
+ assertThat(request.getResourceName(), is(equalTo("resource")));
+ }
+
+ @Test
+ public final void testGetHttpVersion() {
+ assertThat(request.getHttpVersion(), is(equalTo(HTTPHeaders.HTTP_1_1.getHeader())));
+ }
+
+ @Test
+ public final void testGetResourceParameters() {
+ assertThat(
+ request.getResourceParameters(),
+ allOf(
+ hasEntry("message", "Hello world!!"), hasEntry("mensaje", "¡¡Hola mundo!!"), hasEntry("mensaxe", "Ola mundo!!"),
+ hasEntry("mensagem", "Olá mundo!!")
+ )
+ );
+ assertThat(request.getResourceParameters(), is(aMapWithSize(4)));
+ }
+
+ @Test
+ public final void testGetHeaderParameters() {
+ assertThat(request.getHeaderParameters(), allOf(hasEntry("Host", "localhost"), hasEntry("Content-Length", "85")));
+ assertThat(request.getHeaderParameters(), is(aMapWithSize(2)));
+ }
+
+ @Test
+ public final void testGetContent() {
+ assertThat(
+ request.getContent(),
+ is(equalTo("message=Hello world!!&mensaje=¡¡Hola mundo!!&mensaxe=Ola mundo!!&mensagem=Olá mundo!!"))
+ );
+ }
+
+ @Test
+ public final void testGetContentLength() {
+ assertThat(request.getContentLength(), is(equalTo(85)));
+ }
+
+ @Test
+ public final void testToString() {
+ assertThat(request.toString(), is(equalTo(requestText)));
+ }
+
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestPOSTOneParameterTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestPOSTOneParameterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a32a042ba2775f8c03e51e6cbc2a2c7852469d6
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestPOSTOneParameterTest.java
@@ -0,0 +1,89 @@
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsArrayWithSize.emptyArray;
+import static org.hamcrest.collection.IsMapContaining.hasEntry;
+import static org.hamcrest.collection.IsMapWithSize.aMapWithSize;
+import static org.hamcrest.text.IsEmptyString.emptyString;
+
+import java.io.StringReader;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPHeaders;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequest;
+import es.uvigo.esei.dai.hybridserver.http.HTTPRequestMethod;
+
+@Tag("request")
+public class HTTPRequestPOSTOneParameterTest {
+ private String requestText;
+ private HTTPRequest request;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ this.requestText = "POST / HTTP/1.1\r\n"
+ + "Host: localhost\r\n"
+ + "Content-Length: 21\r\n\r\n"
+ + "message=Hello world!!";
+
+ this.request = new HTTPRequest(new StringReader(this.requestText));
+ }
+
+ @Test
+ public final void testGetMethod() {
+ assertThat(request.getMethod(), is(equalTo(HTTPRequestMethod.POST)));
+ }
+
+ @Test
+ public final void testGetResourceChain() {
+ assertThat(request.getResourceChain(), is(equalTo("/")));
+ }
+
+ @Test
+ public final void testGetResourcePath() {
+ assertThat(request.getResourcePath(), is(emptyArray()));
+ }
+
+ @Test
+ public final void testGetResourceName() {
+ assertThat(request.getResourceName(), is(emptyString()));
+ }
+
+ @Test
+ public final void testGetHttpVersion() {
+ assertThat(request.getHttpVersion(), is(equalTo(HTTPHeaders.HTTP_1_1.getHeader())));
+ }
+
+ @Test
+ public final void testGetResourceParameters() {
+ assertThat(request.getResourceParameters(), hasEntry("message", "Hello world!!"));
+ assertThat(request.getResourceParameters(), is(aMapWithSize(1)));
+ }
+
+ @Test
+ public final void testGetHeaderParameters() {
+ assertThat(request.getHeaderParameters(), allOf(hasEntry("Host", "localhost"), hasEntry("Content-Length", "21")));
+ assertThat(request.getHeaderParameters(), is(aMapWithSize(2)));
+ }
+
+ @Test
+ public final void testGetContent() {
+ assertThat(request.getContent(), is(equalTo("message=Hello world!!")));
+ }
+
+ @Test
+ public final void testGetContentLength() {
+ assertThat(request.getContentLength(), is(equalTo(21)));
+ }
+
+ @Test
+ public final void testToString() {
+ assertThat(request.toString(), is(equalTo(requestText)));
+ }
+
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestResponseSuite.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestResponseSuite.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f17f7b3b20ce504bda2f9e957a9220357393ec5
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestResponseSuite.java
@@ -0,0 +1,28 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import org.junit.platform.suite.api.IncludeTags;
+import org.junit.platform.suite.api.SelectPackages;
+import org.junit.platform.suite.api.Suite;
+
+@Suite
+@SelectPackages("es.uvigo.esei.dai.hybridserver.week1")
+@IncludeTags({ "request", "response" })
+public class HTTPRequestResponseSuite {
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestSuite.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestSuite.java
new file mode 100644
index 0000000000000000000000000000000000000000..360778c37e53d3797f114c535950785c04cd865b
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPRequestSuite.java
@@ -0,0 +1,28 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import org.junit.platform.suite.api.IncludeTags;
+import org.junit.platform.suite.api.SelectPackages;
+import org.junit.platform.suite.api.Suite;
+
+@Suite
+@SelectPackages("es.uvigo.esei.dai.hybridserver.week1")
+@IncludeTags("response")
+public class HTTPRequestSuite {
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseNoContentTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseNoContentTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..953144cabd33c473e63c170d168f217d7829a352
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseNoContentTest.java
@@ -0,0 +1,38 @@
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPHeaders;
+import es.uvigo.esei.dai.hybridserver.http.HTTPResponse;
+import es.uvigo.esei.dai.hybridserver.http.HTTPResponseStatus;
+
+@Tag("response")
+public class HTTPResponseNoContentTest {
+ private HTTPResponse response;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ this.response = new HTTPResponse();
+
+ this.response.setStatus(HTTPResponseStatus.S200);
+ this.response.setVersion(HTTPHeaders.HTTP_1_1.getHeader());
+ }
+
+ @Test
+ public final void testPrint() throws IOException {
+ try (final StringWriter writer = new StringWriter()) {
+ this.response.print(writer);
+
+ assertThat(writer.toString(), is(equalTo("HTTP/1.1 200 OK\r\n\r\n")));
+ }
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseNoContentWithHeadersTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseNoContentWithHeadersTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..df38ae1ce3df5e7f3e2ec6791feb32814aaa2a86
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseNoContentWithHeadersTest.java
@@ -0,0 +1,47 @@
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPHeaders;
+import es.uvigo.esei.dai.hybridserver.http.HTTPResponse;
+import es.uvigo.esei.dai.hybridserver.http.HTTPResponseStatus;
+
+@Tag("response")
+public class HTTPResponseNoContentWithHeadersTest {
+ private HTTPResponse response;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ this.response = new HTTPResponse();
+
+ this.response.setStatus(HTTPResponseStatus.S200);
+ this.response.setVersion(HTTPHeaders.HTTP_1_1.getHeader());
+ this.response.putParameter("Content-Type", "text/html");
+ this.response.putParameter("Content-Encoding", "deflate");
+ this.response.putParameter("Content-Language", "en");
+ }
+
+ @Test
+ public final void testPrint() throws IOException {
+ try (final StringWriter writer = new StringWriter()) {
+ this.response.print(writer);
+
+ final String responseText = writer.toString();
+ assertThat(responseText, startsWith("HTTP/1.1 200 OK"));
+ assertThat(responseText, containsString("Content-Type: text/html"));
+ assertThat(responseText, containsString("Content-Encoding: deflate"));
+ assertThat(responseText, containsString("Content-Language: en"));
+ assertThat(responseText, endsWith("\r\n\r\n"));
+ }
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseSuite.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseSuite.java
new file mode 100644
index 0000000000000000000000000000000000000000..79f5705c0979c8a8784b172a7f7a8707bbe8ac27
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseSuite.java
@@ -0,0 +1,28 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import org.junit.platform.suite.api.IncludeTags;
+import org.junit.platform.suite.api.SelectPackages;
+import org.junit.platform.suite.api.Suite;
+
+@Suite
+@SelectPackages("es.uvigo.esei.dai.hybridserver.week1")
+@IncludeTags("request")
+public class HTTPResponseSuite {
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..626020e14a72dea18aa036f06d31cd7fc65dcb2b
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseTest.java
@@ -0,0 +1,39 @@
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPHeaders;
+import es.uvigo.esei.dai.hybridserver.http.HTTPResponse;
+import es.uvigo.esei.dai.hybridserver.http.HTTPResponseStatus;
+
+@Tag("response")
+public class HTTPResponseTest {
+ private HTTPResponse response;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ this.response = new HTTPResponse();
+
+ this.response.setContent("Hello World");
+ this.response.setStatus(HTTPResponseStatus.S200);
+ this.response.setVersion(HTTPHeaders.HTTP_1_1.getHeader());
+ }
+
+ @Test
+ public final void testPrint() throws IOException {
+ try (final StringWriter writer = new StringWriter()) {
+ this.response.print(writer);
+
+ assertThat(writer.toString(), is(equalTo("HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World")));
+ }
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseWithHeadersTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseWithHeadersTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..42a4879e501e37e49b086f8d4e3597e44e940127
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/HTTPResponseWithHeadersTest.java
@@ -0,0 +1,49 @@
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.http.HTTPHeaders;
+import es.uvigo.esei.dai.hybridserver.http.HTTPResponse;
+import es.uvigo.esei.dai.hybridserver.http.HTTPResponseStatus;
+
+@Tag("response")
+public class HTTPResponseWithHeadersTest {
+ private HTTPResponse response;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ this.response = new HTTPResponse();
+
+ this.response.setContent("Hello World");
+ this.response.setStatus(HTTPResponseStatus.S200);
+ this.response.setVersion(HTTPHeaders.HTTP_1_1.getHeader());
+ this.response.putParameter("Content-Type", "text/html");
+ this.response.putParameter("Content-Encoding", "deflate");
+ this.response.putParameter("Content-Language", "en");
+ }
+
+ @Test
+ public final void testPrint() throws IOException {
+ try (final StringWriter writer = new StringWriter()) {
+ this.response.print(writer);
+
+ final String responseText = writer.toString();
+ assertThat(responseText, startsWith("HTTP/1.1 200 OK"));
+ assertThat(responseText, containsString("Content-Length: 11"));
+ assertThat(responseText, containsString("Content-Type: text/html"));
+ assertThat(responseText, containsString("Content-Encoding: deflate"));
+ assertThat(responseText, containsString("Content-Language: en"));
+ assertThat(responseText, endsWith("\r\n\r\nHello World"));
+ }
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week1/WelcomePageTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/WelcomePageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5d5a5b050f0502ff97dc2a85b8a291e188ee6b5b
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week1/WelcomePageTest.java
@@ -0,0 +1,36 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.week1;
+
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.getContentWithType;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.IOException;
+
+import org.junit.jupiter.api.RepeatedTest;
+
+import es.uvigo.esei.dai.hybridserver.utils.HybridServerTestCase;
+
+public class WelcomePageTest extends HybridServerTestCase {
+
+ @RepeatedTest(10)
+ public void testWelcome() throws IOException {
+ assertThat(getContentWithType(url, "text/html"), containsString("Hybrid Server"));
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week2/ClientRequetsTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week2/ClientRequetsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa2bb48f893fbc5442a24a87c7be0fbda59decd2
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week2/ClientRequetsTest.java
@@ -0,0 +1,159 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.week2;
+
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.deleteStatus;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.extractUUIDFromText;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.getContent;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.getContentWithType;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.getStatus;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.postContent;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.postStatus;
+import static java.util.Collections.singletonMap;
+import static java.util.stream.Collectors.toMap;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.HybridServer;
+import es.uvigo.esei.dai.hybridserver.utils.HybridServerTestCase;
+
+public class ClientRequetsTest extends HybridServerTestCase {
+ private String invalidUUID;
+ private String[][] pages;
+
+ @BeforeEach
+ public void setup() {
+ this.invalidUUID = "12345678-abcd-1234-ab12-9876543210ab";
+ }
+
+ @Override
+ protected HybridServer createHybridServer() {
+ // El servidor debe tener las siguientes páginas en memoria
+ this.pages = new String[][] {
+ // { "uuid", "texto contenido por la página" }
+ { "6df1047e-cf19-4a83-8cf3-38f5e53f7725", "This is the html page 6df1047e-cf19-4a83-8cf3-38f5e53f7725." },
+ { "79e01232-5ea4-41c8-9331-1c1880a1d3c2", "This is the html page 79e01232-5ea4-41c8-9331-1c1880a1d3c2." },
+ { "a35b6c5e-22d6-4707-98b4-462482e26c9e", "This is the html page a35b6c5e-22d6-4707-98b4-462482e26c9e." },
+ { "3aff2f9c-0c7f-4630-99ad-27a0cf1af137", "This is the html page 3aff2f9c-0c7f-4630-99ad-27a0cf1af137." },
+ { "77ec1d68-84e1-40f4-be8e-066e02f4e373", "This is the html page 77ec1d68-84e1-40f4-be8e-066e02f4e373." },
+ { "8f824126-0bd1-4074-b88e-c0b59d3e67a3", "This is the html page 8f824126-0bd1-4074-b88e-c0b59d3e67a3." },
+ { "c6c80c75-b335-4f68-b7a7-59434413ce6c", "This is the html page c6c80c75-b335-4f68-b7a7-59434413ce6c." },
+ { "f959ecb3-6382-4ae5-9325-8fcbc068e446", "This is the html page f959ecb3-6382-4ae5-9325-8fcbc068e446." },
+ { "2471caa8-e8df-44d6-94f2-7752a74f6819", "This is the html page 2471caa8-e8df-44d6-94f2-7752a74f6819." },
+ { "fa0979ca-2734-41f7-84c5-e40e0886e408", "This is the html page fa0979ca-2734-41f7-84c5-e40e0886e408." } };
+
+ // Creación del servidor con las páginas ya en memoria.
+ final Map pages = Stream.of(this.pages).collect(toMap(entry -> entry[0], entry -> entry[1]));
+
+ return new HybridServer(pages);
+ }
+
+ // Ejercicio 2
+ @Test
+ public void testGetHtmlPage() throws IOException {
+ for (String[] page : pages) {
+ final String uuid = page[0];
+ final String content = page[1];
+
+ final String pageURL = url + "html?uuid=" + uuid;
+
+ assertThat(getContentWithType(pageURL, "text/html"), containsString(content));
+ }
+ }
+
+ // Ejercicio 3
+ @Test
+ public void testGetHtmlList() throws IOException {
+ final String pageURL = url + "html";
+ final String content = getContentWithType(pageURL, "text/html");
+
+ for (String[] page : pages) {
+ final String uuid = page[0];
+
+ assertThat(content, containsString(uuid));
+ }
+ }
+
+ // Ejercicio 4
+ @Test
+ public void testPost() throws IOException {
+ final String content = "Testing POST";
+
+ // Envío de la página y extracción del uuid de la nueva página
+ final String responseContent = postContent(url + "html", singletonMap("html", content));
+ final String uuid = extractUUIDFromText(responseContent);
+ assertThat(uuid, is(notNullValue()));
+
+ // Verificación de que la página de respuesta contiene un enlace a la nueva página
+ final String uuidHyperlink = "" + uuid + "";
+ assertThat(responseContent, containsString(uuidHyperlink));
+
+ // Recuperación de la nueva página
+ final String url = this.url + "html?uuid=" + uuid;
+ assertThat("The new page couldn't be retrieved", getContent(url), is(equalTo(content)));
+ }
+
+ @Test
+ public void testDelete() throws IOException {
+ final String uuid = pages[4][0];
+ final String url = this.url + "html?uuid=" + uuid;
+
+ assertThat("The page couldn't be deleted", deleteStatus(url), is(equalTo(200)));
+
+ assertThat("The page already exists", getStatus(url), is(equalTo(404)));
+ }
+
+ // Ejercicio 5
+ @Test
+ public void testGetInvalidHtmlPage() throws IOException {
+ final String pageURL = url + "html?uuid=" + invalidUUID;
+
+ assertThat(getStatus(pageURL), is(equalTo(404)));
+ }
+
+ @Test
+ public void testGetInvalidResource() throws IOException {
+ final String pageURL = url + "xxx?uuid=" + pages[0];
+
+ assertThat(getStatus(pageURL), is(equalTo(400)));
+ }
+
+ @Test
+ public void testDeleteNonexistentPage() throws IOException {
+ final String pageURL = this.url + "html?uuid=" + invalidUUID;
+
+ assertThat(deleteStatus(pageURL), is(equalTo(404)));
+ }
+
+ @Test
+ public void testPostInvalidContent() throws IOException {
+ final String content = "Testing POST";
+
+ assertThat(postStatus(url + "html", singletonMap("xxx", content)), is(equalTo(400)));
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week3/ClientRequestsWithDatabaseTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week3/ClientRequestsWithDatabaseTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2a9c9b66d9dedac3ee2e8cffac785dc9da07c54
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week3/ClientRequestsWithDatabaseTest.java
@@ -0,0 +1,166 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.week3;
+
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.deleteStatus;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.extractUUIDFromText;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.getContent;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.getContentWithType;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.getStatus;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.postContent;
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.postStatus;
+import static es.uvigo.esei.dai.hybridserver.utils.matchers.TableMatcher.hasTable;
+import static java.util.Collections.singletonMap;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import es.uvigo.esei.dai.hybridserver.HybridServer;
+import es.uvigo.esei.dai.hybridserver.utils.JdbcTestCase;
+
+public class ClientRequestsWithDatabaseTest extends JdbcTestCase {
+ private HybridServer server;
+ private String url;
+ private String invalidUUID;
+ private String[][] pages;
+
+ @BeforeEach
+ public void initAttributes() {
+ this.invalidUUID = "12345678-abcd-1234-ab12-9876543210ab";
+
+ // Estas páginas se insertan en la base de datos al inicio del test.
+ this.pages = new String[][] {
+ // { "uuid", "texto contenido por la página" }
+ { "6df1047e-cf19-4a83-8cf3-38f5e53f7725", "This is the html page 6df1047e-cf19-4a83-8cf3-38f5e53f7725." },
+ { "79e01232-5ea4-41c8-9331-1c1880a1d3c2", "This is the html page 79e01232-5ea4-41c8-9331-1c1880a1d3c2." },
+ { "a35b6c5e-22d6-4707-98b4-462482e26c9e", "This is the html page a35b6c5e-22d6-4707-98b4-462482e26c9e." },
+ { "3aff2f9c-0c7f-4630-99ad-27a0cf1af137", "This is the html page 3aff2f9c-0c7f-4630-99ad-27a0cf1af137." },
+ { "77ec1d68-84e1-40f4-be8e-066e02f4e373", "This is the html page 77ec1d68-84e1-40f4-be8e-066e02f4e373." },
+ { "8f824126-0bd1-4074-b88e-c0b59d3e67a3", "This is the html page 8f824126-0bd1-4074-b88e-c0b59d3e67a3." },
+ { "c6c80c75-b335-4f68-b7a7-59434413ce6c", "This is the html page c6c80c75-b335-4f68-b7a7-59434413ce6c." },
+ { "f959ecb3-6382-4ae5-9325-8fcbc068e446", "This is the html page f959ecb3-6382-4ae5-9325-8fcbc068e446." },
+ { "2471caa8-e8df-44d6-94f2-7752a74f6819", "This is the html page 2471caa8-e8df-44d6-94f2-7752a74f6819." },
+ { "fa0979ca-2734-41f7-84c5-e40e0886e408", "This is the html page fa0979ca-2734-41f7-84c5-e40e0886e408." } };
+ }
+
+ @BeforeEach
+ public void startServer() throws Exception {
+ final Properties properties = new Properties();
+ properties.setProperty("port", Integer.toString(8888));
+ properties.setProperty("numClients", "50");
+ properties.setProperty("db.url", getConnectionUrl());
+ properties.setProperty("db.user", getUsername());
+ properties.setProperty("db.password", getPassword());
+
+ this.server = new HybridServer(properties);
+ this.url = String.format("http://localhost:%d/", this.server.getPort());
+
+ this.server.start();
+ }
+
+ @AfterEach
+ public void stopServer() {
+ this.server.close();
+ }
+
+ @Test
+ public void testGetHtmlList() throws IOException {
+ final String pageURL = url + "html";
+ final String content = getContentWithType(pageURL, "text/html");
+
+ for (String[] page : pages) {
+ final String uuid = page[0];
+
+ assertThat(content, containsString(uuid));
+ }
+ }
+
+ @Test
+ public void testPost() throws Exception {
+ final String content = "Testing POST";
+
+ // Envío de la página y extracción del uuid de la nueva página
+ final String responseContent = postContent(url + "html", singletonMap("html", content));
+ final String uuid = extractUUIDFromText(responseContent);
+ assertThat(uuid, is(notNullValue()));
+
+ // Verificación de que la página de respuesta contiene un enlace a la nueva página
+ final String uuidHyperlink = "" + uuid + "";
+ assertThat(responseContent, containsString(uuidHyperlink));
+
+ // Recuperación de la nueva página
+ final String url = this.url + "html?uuid=" + uuid;
+ assertThat("The new page couldn't be retrieved", getContent(url), is(equalTo(content)));
+
+ // Comprobación de la inserción en la base de datos
+ assertThat(getConnection(), hasTable(getSchema(), "HTML").withColumn("uuid").withValue(uuid));
+ }
+
+ @Test
+ public void testDelete() throws Exception {
+ final String uuid = pages[4][0];
+ final String url = this.url + "html?uuid=" + uuid;
+
+ // Eliminación de la página
+ assertThat("The page couldn't be deleted", deleteStatus(url), is(equalTo(200)));
+
+ // Comprobación de la eliminación en la base de datos
+ assertThat(getConnection(), hasTable(getSchema(), "HTML").withColumn("uuid").withoutValue(uuid));
+
+ // Comprobación de la eliminación vía web
+ assertThat("The page already exists", getStatus(url), is(equalTo(404)));
+ }
+
+ // Ejercicio 5
+ @Test
+ public void testGetInvalidHtmlPage() throws IOException {
+ final String pageURL = url + "html?uuid=" + invalidUUID;
+
+ assertThat(getStatus(pageURL), is(equalTo(404)));
+ }
+
+ @Test
+ public void testGetInvalidResource() throws IOException {
+ final String pageURL = url + "xxx?uuid=" + pages[0];
+
+ assertThat(getStatus(pageURL), is(equalTo(400)));
+ }
+
+ @Test
+ public void testDeleteNonexistentPage() throws IOException {
+ final String pageURL = this.url + "html?uuid=" + invalidUUID;
+
+ assertThat(deleteStatus(pageURL), is(equalTo(404)));
+ }
+
+ @Test
+ public void testPostInvalidContent() throws IOException {
+ final String content = "Testing POST";
+
+ assertThat(postStatus(url + "html", singletonMap("xxx", content)), is(equalTo(400)));
+ }
+}
diff --git a/src/test/java/es/uvigo/esei/dai/hybridserver/week3/CustomPortTest.java b/src/test/java/es/uvigo/esei/dai/hybridserver/week3/CustomPortTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2fba0575d7130740b21536290a8e71130728f3f2
--- /dev/null
+++ b/src/test/java/es/uvigo/esei/dai/hybridserver/week3/CustomPortTest.java
@@ -0,0 +1,83 @@
+/**
+ * HybridServer
+ * Copyright (C) 2023 Miguel Reboiro-Jato
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package es.uvigo.esei.dai.hybridserver.week3;
+
+import static es.uvigo.esei.dai.hybridserver.utils.TestUtils.getContentWithType;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import es.uvigo.esei.dai.hybridserver.HybridServer;
+
+@Timeout(5L)
+public class CustomPortTest {
+ public HybridServer startServer() {
+ final HybridServer server = new HybridServer();
+ server.start();
+
+ return server;
+ }
+
+ public HybridServer startServer(int port) {
+ final Properties properties = new Properties();
+ properties.setProperty("port", Integer.toString(port));
+ properties.setProperty("numClients", "50");
+ properties.setProperty("db.url", "jdbc:mysql://localhost/hstestdb");
+ properties.setProperty("db.user", "dai");
+ properties.setProperty("db.password", "daipassword");
+
+ final HybridServer server = new HybridServer(properties);
+ server.start();
+
+ return server;
+ }
+
+ @Test
+ public void testWelcome() throws IOException {
+ try (HybridServer server = startServer()) {
+ final String url = getUrlForHome(server);
+
+ for (int i = 0; i < 10; i++) {
+ assertThat(getContentWithType(url, "text/html"), containsString("Hybrid Server"));
+ }
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = { 1234, 4242, 7315, 8833, 10201, 21386, 33217, 45450, 55881, 60000 })
+ public void testMultipleWelcome(int port) throws IOException {
+ try (HybridServer server = startServer(port)) {
+ final String url = getUrlForHome(server);
+
+ for (int i = 0; i < 10; i++) {
+ assertThat(getContentWithType(url, "text/html"), containsString("Hybrid Server"));
+ }
+ }
+ }
+
+ private String getUrlForHome(HybridServer server) {
+ return String.format("http://localhost:%d/", server.getPort());
+ }
+}
diff --git a/src/test/resources/es/uvigo/esei/dai/hybridserver/week3/dataset.dtd b/src/test/resources/es/uvigo/esei/dai/hybridserver/week3/dataset.dtd
new file mode 100644
index 0000000000000000000000000000000000000000..8cf9b83ae7648276801aab4249632ae6543a0213
--- /dev/null
+++ b/src/test/resources/es/uvigo/esei/dai/hybridserver/week3/dataset.dtd
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/src/test/resources/es/uvigo/esei/dai/hybridserver/week3/dataset.xml b/src/test/resources/es/uvigo/esei/dai/hybridserver/week3/dataset.xml
new file mode 100644
index 0000000000000000000000000000000000000000..256e19838f1158c1cef1b0bea7487cd9b9c8bdfb
--- /dev/null
+++ b/src/test/resources/es/uvigo/esei/dai/hybridserver/week3/dataset.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+