Skip to content

Commit

Permalink
Add some tests & update libs (#27)
Browse files Browse the repository at this point in the history
* Add some tests & update libs

* remove silly copy/paste comment

* Test fixes + async response fixes
  • Loading branch information
modmuss50 authored Dec 16, 2023
1 parent ab22403 commit df67979
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 33 deletions.
27 changes: 18 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,26 @@ repositories {
}

dependencies {
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.8'
implementation group: 'io.javalin', name: 'javalin', version: '3.13.11'
implementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.32'
implementation group: 'commons-io', name: 'commons-io', version: '2.8.0'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'io.javalin:javalin:5.6.3'
implementation 'org.slf4j:slf4j-simple:2.0.9'
implementation 'commons-io:commons-io:2.15.1'
implementation 'org.jetbrains:annotations:24.1.0'

testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testImplementation "org.junit.jupiter:junit-jupiter-params:5.9.2"
testImplementation 'io.javalin:javalin-testtools:5.6.3'

testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

java {
sourceCompatibility = 1.8
targetCompatibility = 1.8
sourceCompatibility = 21
targetCompatibility = 21
}

test {
useJUnitPlatform()
}

jar {
Expand All @@ -43,9 +54,7 @@ jar {
tasks.withType(JavaCompile).configureEach {
it.options.encoding = "UTF-8"

if (JavaVersion.current().isJava9Compatible()) {
it.options.release = 8
}
it.options.release = 21
}

spotless {
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/net/fabricmc/meta/FabricMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.concurrent.TimeUnit;

import com.google.gson.stream.JsonReader;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -92,6 +93,16 @@ private static void update() {
}
}

@VisibleForTesting
public static void setupForTesting() {
if (configInitialized) {
return;
}

configInitialized = true;
update();
}

private static void updateHeartbeat() {
if (heartbeatUrl == null) return;

Expand Down
8 changes: 4 additions & 4 deletions src/main/java/net/fabricmc/meta/web/EndpointsV1.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ public static void setup() {
WebServer.jsonGet("/v1/versions", () -> FabricMeta.database);

WebServer.jsonGet("/v1/versions/game", () -> FabricMeta.database.game);
WebServer.jsonGet("/v1/versions/game/:game_version", context -> filter(context, FabricMeta.database.game));
WebServer.jsonGet("/v1/versions/game/{game_version}", context -> filter(context, FabricMeta.database.game));

WebServer.jsonGet("/v1/versions/mappings", () -> FabricMeta.database.mappings);
WebServer.jsonGet("/v1/versions/mappings/:game_version", context -> filter(context, FabricMeta.database.mappings));
WebServer.jsonGet("/v1/versions/mappings/{game_version}", context -> filter(context, FabricMeta.database.mappings));

WebServer.jsonGet("/v1/versions/loader", () -> FabricMeta.database.getLoader());
WebServer.jsonGet("/v1/versions/loader/:game_version", EndpointsV1::getLoaderInfoAll);
WebServer.jsonGet("/v1/versions/loader/:game_version/:loader_version", EndpointsV1::getLoaderInfo);
WebServer.jsonGet("/v1/versions/loader/{game_version}", EndpointsV1::getLoaderInfoAll);
WebServer.jsonGet("/v1/versions/loader/{game_version}/{loader_version}", EndpointsV1::getLoaderInfo);
}

private static <T extends Predicate<String>> List filter(Context context, List<T> versionList) {
Expand Down
20 changes: 9 additions & 11 deletions src/main/java/net/fabricmc/meta/web/EndpointsV2.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import io.javalin.core.util.Header;
import io.javalin.http.Context;
import io.javalin.http.Header;

import net.fabricmc.meta.FabricMeta;
import net.fabricmc.meta.web.models.BaseVersion;
Expand All @@ -46,14 +46,14 @@ public static void setup() {
WebServer.jsonGet("/v2/versions/game/intermediary", () -> compatibleGameVersions(FabricMeta.database.intermediary, BaseVersion::getVersion, v -> new BaseVersion(v.getVersion(), v.isStable())));

WebServer.jsonGet("/v2/versions/yarn", context -> withLimitSkip(context, FabricMeta.database.mappings));
WebServer.jsonGet("/v2/versions/yarn/:game_version", context -> withLimitSkip(context, filter(context, FabricMeta.database.mappings)));
WebServer.jsonGet("/v2/versions/yarn/{game_version}", context -> withLimitSkip(context, filter(context, FabricMeta.database.mappings)));

WebServer.jsonGet("/v2/versions/intermediary", () -> FabricMeta.database.intermediary);
WebServer.jsonGet("/v2/versions/intermediary/:game_version", context -> filter(context, FabricMeta.database.intermediary));
WebServer.jsonGet("/v2/versions/intermediary/{game_version}", context -> filter(context, FabricMeta.database.intermediary));

WebServer.jsonGet("/v2/versions/loader", context -> withLimitSkip(context, FabricMeta.database.getLoader()));
WebServer.jsonGet("/v2/versions/loader/:game_version", context -> withLimitSkip(context, EndpointsV2.getLoaderInfoAll(context)));
WebServer.jsonGet("/v2/versions/loader/:game_version/:loader_version", EndpointsV2::getLoaderInfo);
WebServer.jsonGet("/v2/versions/loader/{game_version}", context -> withLimitSkip(context, EndpointsV2.getLoaderInfoAll(context)));
WebServer.jsonGet("/v2/versions/loader/{game_version}/{loader_version}", EndpointsV2::getLoaderInfo);

WebServer.jsonGet("/v2/versions/installer", context -> withLimitSkip(context, FabricMeta.database.installer));

Expand All @@ -66,8 +66,8 @@ private static <T> List<T> withLimitSkip(Context context, List<T> list) {
return Collections.emptyList();
}

int limit = context.queryParam("limit", Integer.class, "0").check(i -> i >= 0).get();
int skip = context.queryParam("skip", Integer.class, "0").check(i -> i >= 0).get();
int limit = context.queryParamAsClass("limit", Integer.class).check(i -> i >= 0, "limit must be larger than one").getOrDefault(0);
int skip = context.queryParamAsClass("skip", Integer.class).check(i -> i >= 0, "skip must be larger than one").getOrDefault(0);

Stream<T> listStream = list.stream().skip(skip);

Expand Down Expand Up @@ -157,16 +157,14 @@ private static <T extends BaseVersion> List<BaseVersion> compatibleGameVersions(
}

public static void fileDownload(String path, String ext, Function<LoaderInfoV2, String> fileNameFunction, Function<LoaderInfoV2, CompletableFuture<InputStream>> streamSupplier) {
WebServer.javalin.get("/v2/versions/loader/:game_version/:loader_version/" + path + "/" + ext, ctx -> {
WebServer.javalin.get("/v2/versions/loader/{game_version}/{loader_version}/" + path + "/" + ext, ctx -> {
Object obj = getLoaderInfo(ctx);

if (obj instanceof String) {
ctx.result((String) obj);
} else if (obj instanceof LoaderInfoV2) {
LoaderInfoV2 versionInfo = (LoaderInfoV2) obj;

CompletableFuture<InputStream> streamFuture = streamSupplier.apply(versionInfo);

if (ext.equals("zip")) {
//Set the filename to download
ctx.header(Header.CONTENT_DISPOSITION, String.format("attachment; filename=\"%s\"", fileNameFunction.apply(versionInfo)));
Expand All @@ -179,7 +177,7 @@ public static void fileDownload(String path, String ext, Function<LoaderInfoV2,
//Cache for a day
ctx.header(Header.CACHE_CONTROL, "public, max-age=86400");

ctx.result(streamFuture);
ctx.future(() -> streamSupplier.apply(versionInfo).thenApply(ctx::result));
} else {
ctx.result("An internal error occurred");
}
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/net/fabricmc/meta/web/ServerBootstrap.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import io.javalin.core.util.Header;
import io.javalin.http.BadRequestResponse;
import io.javalin.http.Context;
import io.javalin.http.Handler;
import io.javalin.http.Header;
import io.javalin.http.InternalServerErrorResponse;
import org.apache.commons.io.FileUtils;

Expand All @@ -50,7 +50,7 @@ public class ServerBootstrap {

public static void setup() {
// http://localhost:5555/v2/versions/loader/1.17.1/0.12.0/0.8.0/server/jar
WebServer.javalin.get("/v2/versions/loader/:game_version/:loader_version/:installer_version/server/jar", boostrapHandler());
WebServer.javalin.get("/v2/versions/loader/{game_version}/{loader_version}/{installer_version}/server/jar", boostrapHandler());
}

private static Handler boostrapHandler() {
Expand All @@ -74,7 +74,7 @@ private static Handler boostrapHandler() {
ctx.header(Header.CACHE_CONTROL, cacheControl);
ctx.contentType("application/java-archive");

ctx.result(getResultStream(installerVersion, gameVersion, loaderVersion));
ctx.future(() -> getResultStream(installerVersion, gameVersion, loaderVersion).thenApply(ctx::result));
};
}

Expand Down
23 changes: 17 additions & 6 deletions src/main/java/net/fabricmc/meta/web/WebServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,34 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.javalin.Javalin;
import io.javalin.core.util.Header;
import io.javalin.core.util.RouteOverviewPlugin;
import io.javalin.http.Context;
import io.javalin.http.Header;
import io.javalin.plugin.bundled.CorsPluginConfig;

public class WebServer {
public static Javalin javalin;
public static Gson GSON = new GsonBuilder().setPrettyPrinting().create();

public static void start() {
public static Javalin create() {
if (javalin != null) {
javalin.stop();
}

javalin = Javalin.create(config -> {
config.registerPlugin(new RouteOverviewPlugin("/"));
config.plugins.enableRouteOverview("/");
config.showJavalinBanner = false;
config.enableCorsForAllOrigins();
}).start(5555);
config.plugins.enableCors(cors -> cors.add(CorsPluginConfig::anyHost));
});

EndpointsV1.setup();
EndpointsV2.setup();

return javalin;
}

public static void start() {
assert javalin == null;
create().start(5555);
}

public static <T> void jsonGet(String route, Supplier<T> supplier) {
Expand Down
106 changes: 106 additions & 0 deletions src/test/java/net/fabricmc/meta/test/integration/ComparisonTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2023 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.meta.test.integration;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.net.URI;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.stream.Stream;

import io.javalin.testtools.HttpClient;
import io.javalin.testtools.JavalinTest;
import okhttp3.Response;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import net.fabricmc.meta.FabricMeta;
import net.fabricmc.meta.web.WebServer;

// Tests that the local response matches the remote response of the version in prod
public class ComparisonTests {
private static final String REMOTE_FABRIC_META_URL = "https://meta.fabricmc.net";

@BeforeAll
static void beforeAll() {
FabricMeta.setupForTesting();
}

public static Stream<Arguments> provideEndpoints() {
return Stream.of(
// V1
"/v1/versions",
"/v1/versions/game",
"/v1/versions/game/1.14.4",
"/v1/versions/mappings",
"/v1/versions/mappings/1.16.5",
"/v1/versions/loader",
"/v1/versions/loader/1.20.4",
"/v1/versions/loader/1.20.4/0.15.2",

// V2
"/v2/versions",
"/v2/versions/game",
"/v2/versions/game/yarn",
"/v2/versions/game/intermediary",
"/v2/versions/yarn",
"/v2/versions/yarn/1.20.4",
"/v2/versions/intermediary",
"/v2/versions/intermediary/1.20.4",
"/v2/versions/loader",
"/v2/versions/loader?limit=5",
"/v2/versions/loader?limit=5&skip=5",
// Disabled as this forces all the load metadata to be downloaded, timing out the test.
//"/v2/versions/loader/1.20.4",
"/v2/versions/loader/1.20.4/0.15.2",
"/v2/versions/installer"
// Disabled as this includes the release time, and is not stable
//"/v2/versions/loader/1.20.4/0.15.2/profile/json"
).map(Arguments::of);
}

@ParameterizedTest
@MethodSource("provideEndpoints")
void compareEndpoint(String endpoint) {
JavalinTest.test(WebServer.create(), (server, client) -> {
compareEndpoint(endpoint, client);
});
}

private static void compareEndpoint(String endpoint, HttpClient client) throws Exception {
Response response = client.get(endpoint);
assertEquals(200, response.code());
String localResponse = response.body().string();

String remoteResponse = getRemoteEndpoint(endpoint);
assertEquals(remoteResponse, localResponse);
}

private static String getRemoteEndpoint(String endpoint) throws Exception {
try (var httpClient = java.net.http.HttpClient.newHttpClient()) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(REMOTE_FABRIC_META_URL + endpoint))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
return response.body();
}
}
}
44 changes: 44 additions & 0 deletions src/test/java/net/fabricmc/meta/test/unit/EndpointsV2Tests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2023 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.meta.test.unit;

import static org.junit.jupiter.api.Assertions.assertEquals;

import io.javalin.testtools.JavalinTest;
import okhttp3.Response;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import net.fabricmc.meta.FabricMeta;
import net.fabricmc.meta.web.WebServer;

public class EndpointsV2Tests {
@BeforeAll
static void beforeAll() {
// TODO provide a way to pass in dummy data for constant test results
FabricMeta.setupForTesting();
}

@Test
void versions() {
JavalinTest.test(WebServer.create(), (server, client) -> {
Response response = client.get("/v2/versions");
assertEquals(200, response.code());
String body = response.body().string();
});
}
}
Loading

0 comments on commit df67979

Please sign in to comment.