Skip to content

Commit

Permalink
Add integration tests (#38)
Browse files Browse the repository at this point in the history
* Make runs output a generic

* Add test for sleep

* Add test for a multi step function

* Add Java overloads for FunctionTrigger

* Add function and test for waitForEvent

* Return events ids from sendEvent

* Add test for sendEvent

* Use gradle test for integration tests

* Fix formatting

* Split tests into separate files
  • Loading branch information
KiKoS0 authored Feb 27, 2024
1 parent f9f993d commit 741ca45
Show file tree
Hide file tree
Showing 18 changed files with 418 additions and 98 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ test: test-core test-ktor test-springboot-demo

.PHONY: itest
itest:
gradle inngest-spring-boot-demo:integrationTest
gradle test $(TEST_ARGS) -p inngest-spring-boot-demo integrationTest

.PHONY: test-core
test-core:
Expand Down
12 changes: 7 additions & 5 deletions inngest-core/src/main/kotlin/com/inngest/Function.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ data class FunctionOptions(
val triggers: Array<FunctionTrigger>,
)

data class FunctionTrigger(
@Json(serializeNull = false) val event: String? = null,
@Json(serializeNull = false) val `if`: String? = null,
@Json(serializeNull = false) val cron: String? = null,
)
data class FunctionTrigger
@JvmOverloads
constructor(
@Json(serializeNull = false) val event: String? = null,
@Json(serializeNull = false) val `if`: String? = null,
@Json(serializeNull = false) val cron: String? = null,
)

// TODO - Add an abstraction layer between the Function call response and the comm handler response
enum class OpCode {
Expand Down
11 changes: 8 additions & 3 deletions inngest-core/src/main/kotlin/com/inngest/State.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,22 @@ class State(val payloadJson: String) {
return sb.toString()
}

inline fun <reified T> getState(hashedId: String): T? = getState(hashedId, T::class.java)
inline fun <reified T> getState(
hashedId: String,
fieldName: String = "data",
): T? = getState(hashedId, T::class.java, fieldName)

fun <T> getState(
hashedId: String,
type: Class<T>,
fieldName: String = "data",
): T? {
val mapper = ObjectMapper()
val node: JsonNode = mapper.readTree(payloadJson)
val stepResult = node.path("steps").get(hashedId) ?: throw StateNotFound()
if (stepResult.has("data")) {
val dataNode = stepResult.get("data")

if (stepResult.has(fieldName)) {
val dataNode = stepResult.get(fieldName)
return mapper.treeToValue(dataNode, type)
} else if (stepResult.has("error")) {
// TODO - Parse the error and throw it
Expand Down
14 changes: 6 additions & 8 deletions inngest-core/src/main/kotlin/com/inngest/Step.kt
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class Step(val state: State, val client: Inngest) {
}

/**
* Sends multiple event to Inngest
* Sends multiple events to Inngest
*
* @param id Unique step id for memoization.
* @param event An event payload object.
Expand All @@ -117,18 +117,16 @@ class Step(val state: State, val client: Inngest) {
fun sendEvent(
id: String,
events: Array<InngestEvent>,
) {
): SendEventsResponse {
val hashedId = state.getHashFromId(id)

try {
// If this doesn't throw an error, it's null and that's what is expected
// TODO - Get the event_ids that were sent and return it from this method
// event_ids are not in the data field but it's actually a separate one.
val stepState = state.getState<Any?>(hashedId)
val stepState = state.getState<Array<String>>(hashedId, "event_ids")

if (stepState != null) {
throw Exception("step state expected sendEvent, got something else")
return SendEventsResponse(stepState)
}
return
throw Exception("step state expected sendEvent, got something else")
} catch (e: StateNotFound) {
val response = client.send<SendEventsResponse>(events)
throw StepInterruptSendEventException(id, hashedId, response!!.ids)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class DemoConfiguration extends InngestConfiguration {
@Override
public HashMap<String, InngestFunction> functions() {
String followUpEvent = "user.signup.completed";
FunctionTrigger fnTrigger = new FunctionTrigger("user-signup", null, null);
FunctionTrigger fnTrigger = new FunctionTrigger("user-signup");
FunctionTrigger[] triggers = {fnTrigger};
FunctionOptions fnConfig = new FunctionOptions("fn-id-slug", "My function!", triggers);

Expand Down Expand Up @@ -55,7 +55,7 @@ public HashMap<String, InngestFunction> functions() {
}};
};

FunctionTrigger followupFnTrigger = new FunctionTrigger(followUpEvent, null, null);
FunctionTrigger followupFnTrigger = new FunctionTrigger(followUpEvent);
FunctionOptions followupFnConfig = new FunctionOptions(
"fn-follow-up",
"Follow up function!",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.inngest.springbootdemo;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.Request;

Expand Down Expand Up @@ -40,23 +41,39 @@ private void waitForStartup() throws Exception {
}
}

RunIdsResponse runIds(String runId) throws Exception {
// TODO: Figure out how to make this generic.
// The issue is that deserialization will fail on the `output` generic
// type and return either `LinkedHashMap` or `ArrayList`.
EventRunsResponse<Object> runsByEvent(String eventId) throws Exception {
Request request = new Request.Builder()
.url(String.format("%s/v1/events/%s/runs", baseUrl, runId))
.url(String.format("%s/v1/events/%s/runs", baseUrl, eventId))
.build();
return makeRequest(request, new TypeReference<EventRunsResponse<Object>>() {
});
}

<T> RunResponse<T> runById(String eventId) throws Exception {
Request request = new Request.Builder()
.url(String.format("%s/v1/runs/%S", baseUrl, eventId))
.build();
return makeRequest(request, new TypeReference<RunResponse<T>>() {
});
}

private <T> T makeRequest(Request request, TypeReference<T> typeReference) throws Exception {
try (Response response = httpClient.newCall(request).execute()) {
if (response.code() == 200) {
assert response.body() != null;

String strResponse = response.body().string();
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(strResponse, RunIdsResponse.class);
return mapper.readValue(strResponse, typeReference);
}
}
return null;
}


@PreDestroy
public void stop() throws Exception {
Runtime rt = Runtime.getRuntime();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.inngest.springbootdemo;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
class EventRunsResponse<T> {
RunEntry<T>[] data;

RunEntry<T> first() {
return data[0];
}
}

@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
class RunResponse<T> {
RunEntry<T> data;
}

@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
class RunEntry<T> {
String run_id;
String event_id;

String run_started_at;
String ended_at;

String function_id;
String function_version;

T output;
String status;
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@
class SendEventResponse {
String status;
String[] ids;

String first() {
return ids[0];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public class DemoTestConfiguration extends InngestConfiguration {
protected HashMap<String, InngestFunction> functions() {
HashMap<String, InngestFunction> functions = new HashMap<>();
functions.put("no-step-fn", InngestFunctionTestHelpers.emptyStepFunction());
functions.put("sleep-fn", InngestFunctionTestHelpers.sleepStepFunction());
functions.put("two-steps-fn", InngestFunctionTestHelpers.twoStepsFunction());
functions.put("wait-for-event-fn", InngestFunctionTestHelpers.waitForEventFunction());
functions.put("send-fn", InngestFunctionTestHelpers.sendEventFunction());

return functions;
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,92 @@

import com.inngest.*;

import java.time.Duration;
import java.util.HashMap;
import java.util.Objects;
import java.util.function.BiFunction;

public class InngestFunctionTestHelpers {

static SendEventResponse sendEvent(Inngest inngest, String eventName) {
InngestEvent event = new InngestEvent(eventName, new HashMap<String, String>());
return inngest.send(event, SendEventResponse.class);
SendEventResponse response = inngest.send(event, SendEventResponse.class);

assert Objects.requireNonNull(response).ids.length > 0;
return response;
}

static InngestFunction emptyStepFunction() {
FunctionTrigger fnTrigger = new FunctionTrigger("test/no-step", null, null);
FunctionTrigger fnTrigger = new FunctionTrigger("test/no-step");
FunctionTrigger[] triggers = {fnTrigger};
FunctionOptions fnConfig = new FunctionOptions("no-step-fn", "No Step Function", triggers);

BiFunction<FunctionContext, Step, String> handler = (ctx, step) -> "hello world";

return new InngestFunction(fnConfig, handler);
}

static InngestFunction sleepStepFunction() {
FunctionTrigger fnTrigger = new FunctionTrigger("test/sleep");
FunctionTrigger[] triggers = {fnTrigger};
FunctionOptions fnConfig = new FunctionOptions("sleep-fn", "Sleep Function", triggers);

BiFunction<FunctionContext, Step, Integer> handler = (ctx, step) -> {
int result = step.run("num", () -> 42, Integer.class);
step.sleep("wait-one-sec", Duration.ofSeconds(9));

return result;
};

return new InngestFunction(fnConfig, handler);
}

static InngestFunction twoStepsFunction() {
FunctionTrigger fnTrigger = new FunctionTrigger("test/two.steps");
FunctionTrigger[] triggers = {fnTrigger};
FunctionOptions fnConfig = new FunctionOptions("two-steps-fn", "Two Steps Function", triggers);

int count = 0;

BiFunction<FunctionContext, Step, Integer> handler = (ctx, step) -> {
int step1 = step.run("step1", () -> count + 1, Integer.class);
int tmp1 = step1 + 1;

int step2 = step.run("step2", () -> tmp1 + 1, Integer.class);
int tmp2 = step2 + 1;

return tmp2 + 1;
};

return new InngestFunction(fnConfig, handler);
}

static InngestFunction waitForEventFunction() {
FunctionTrigger fnTrigger = new FunctionTrigger("test/wait-for-event");
FunctionTrigger[] triggers = {fnTrigger};
FunctionOptions fnConfig = new FunctionOptions("wait-for-event-fn", "Wait for Event Function", triggers);

BiFunction<FunctionContext, Step, String> handler = (ctx, step) -> {
Object event = step.waitForEvent("wait-test", "test/yolo.wait", "8s", null);

return event == null ? "empty" : "fullfilled";
};

return new InngestFunction(fnConfig, handler);
}

static InngestFunction sendEventFunction() {
FunctionTrigger fnTrigger = new FunctionTrigger("test/send");
FunctionTrigger[] triggers = {fnTrigger};
FunctionOptions fnConfig = new FunctionOptions("send-fn", "Send Function", triggers);

BiFunction<FunctionContext, Step, SendEventsResponse> handler = (ctx, step) ->
step.sendEvent("send-test", new InngestEvent(
"test/no-match",
new HashMap<String, String>()));

return new InngestFunction(fnConfig, handler);
}


}
Loading

0 comments on commit 741ca45

Please sign in to comment.