diff --git a/bukkit/pom.xml b/bukkit/pom.xml index 682c789b..8646be53 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -353,5 +353,40 @@ true provided + + + org.testcontainers + testcontainers + 1.16.3 + test + + + + org.testcontainers + mockserver + 1.16.3 + test + + + + org.mock-server + mockserver-client-java + 5.11.2 + test + + + + com.github.steveice10 + mcprotocollib + 1.18-2 + test + + + + ch.qos.logback + logback-core + 1.2.10 + test + diff --git a/bukkit/src/test/java/integration/LoginIT.java b/bukkit/src/test/java/integration/LoginIT.java new file mode 100644 index 00000000..96ff74d3 --- /dev/null +++ b/bukkit/src/test/java/integration/LoginIT.java @@ -0,0 +1,162 @@ +package integration; + +import com.github.steveice10.mc.protocol.MinecraftProtocol; +import com.github.steveice10.packetlib.Session; +import com.github.steveice10.packetlib.event.session.DisconnectedEvent; +import com.github.steveice10.packetlib.event.session.SessionAdapter; +import com.github.steveice10.packetlib.packet.Packet; +import com.github.steveice10.packetlib.tcp.TcpClientSession; +import com.google.common.io.CharStreams; + +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.junit.Rule; +import org.junit.Test; +import org.mockserver.client.MockServerClient; +import org.mockserver.model.HttpRequest; +import org.mockserver.verify.VerificationTimes; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.MockServerContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +// Warning name is sensitive to the surefire plugin +public class LoginIT { + + private static final String API_TAG = "mockserver-5.11.2"; + private static final String API_IMAGE_NAME = "mockserver/mockserver"; + private static final String API_IMAGE = API_IMAGE_NAME + ':' + API_TAG; + + // @Rule + public MockServerContainer mockServer = new MockServerContainer(DockerImageName.parse(API_IMAGE)) + .withReuse(true); + + private static final String SERVER_TAG = "1.18.1"; + private static final String SERVER_IMAGE_NAME = "ghcr.io/games647/paperclip"; + private static final String SERVER_IMAGE = SERVER_IMAGE_NAME + ':' + SERVER_TAG; + + @Rule + public GenericContainer minecraftServer = new GenericContainer(DockerImageName.parse(SERVER_IMAGE)) + .withEnv("JDK_JAVA_OPTIONS", "-Dcom.mojang.eula.agree=true") + .withExposedPorts(25565) + // Done (XXXXs)! For help, type "help" + .waitingFor( + Wait.forLogMessage(".*For help, type \"help\"*\\n", 1) + ) + .withReuse(true); + + @Test + public void checkRunning() throws Exception { + assertThat(minecraftServer.isRunning(), is(true)); + + String host = minecraftServer.getHost(); + int port = minecraftServer.getMappedPort(25565); + Session clientSession = new TcpClientSession(host, port, new MinecraftProtocol()); + try { + CompletableFuture connectionResult = new CompletableFuture<>(); + clientSession.addListener(new SessionAdapter() { + @Override + public void packetReceived(Session session, Packet packet) { + System.out.println("Received: " + packet.getClass()); + connectionResult.complete(true); + } + + @Override + public void disconnected(DisconnectedEvent event) { + connectionResult.complete(false); + } + }); + + clientSession.connect(); + assertThat(connectionResult.get(2, TimeUnit.SECONDS), is(true)); + } finally { + clientSession.disconnect("Status test complete."); + } + } + + @Test + public void autoRegisterNewUser() throws Exception { + assertThat(mockServer.isRunning(), is(true)); + + try (MockServerClient client = new MockServerClient(mockServer.getHost(), mockServer.getServerPort())) { + HttpRequest profileReq = request("/users/profiles/minecraft/" + "username"); + HttpRequest hasJoinedReq = request() + .withPath("/session/minecraft/hasJoined") + .withQueryStringParameter("username", "") + .withQueryStringParameter("serverId", "") + .withQueryStringParameter("ip", ""); + + // check call network request times + client.verify(profileReq, VerificationTimes.once()); + client.verify(hasJoinedReq, VerificationTimes.once()); + + // Verify order + client.verify(profileReq, hasJoinedReq); + + client + .when(request() + .withPath("/users/profiles/minecraft/" + "username")) + .respond(response() + .withBody("bla")); + + client + .when(hasJoinedReq) + .respond(response() + .withBody("Test")); + + URLConnection urlConnection = new URL(mockServer.getEndpoint() + "/users/profiles/minecraft/username").openConnection(); + String out = CharStreams.toString(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8)); + System.out.println("OUTPUT: " + out); + } + } + + @Test + public void failedJoinedVerification() { + // has joined fails + } + + @Test + public void offlineLoginNewUserDisabledRegister() { + // auto register disabled, always offline login for new users + } + + @Test + public void offlineLoginNewUser() { + // auto register enabled, but no paid account + } + + @Test + public void autoLoginRegistered() { + // registered premium user and paid account login in + } + + @Test + public void failedLoginPremiumRegistered() { + // registered premium, but tried offline login + } + + @Test + public void offlineLoginRegistered() { + // assume registered user marked as offline - tried to login + } + + @Test + public void alreadyOnlineDuplicateOwner() { + + } + + @Test + public void alreadyOnlineDuplicateCracked() { + + } +}