Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions tests/integration/provider-maven/example/Pulumi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ resources:
type: javap:index:HelloWorld
properties:
length: 12
hello2:
type: javap:index:HelloWorld
sampleAsync:
type: javap:index:SampleAsync
properties:
length: 22
outputs:
value: ${hello.value}
sampleAsyncValue: ${sampleAsync.value}
5 changes: 5 additions & 0 deletions tests/integration/provider-maven/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
<groupId>com.pulumi</groupId>
<artifactId>random</artifactId>
<version>4.14.0</version>
</dependency>
<dependency>
<groupId>com.pulumi</groupId>
<artifactId>std</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.pulumi.example.provider;

import com.pulumi.core.annotations.Import;
import com.pulumi.core.annotations.Export;
import com.pulumi.core.Output;
import com.pulumi.random.RandomString;
import com.pulumi.random.RandomStringArgs;
import com.pulumi.resources.ComponentResource;
import com.pulumi.resources.ComponentResourceOptions;
import com.pulumi.resources.CustomResourceOptions;
import com.pulumi.resources.ResourceArgs;
import com.pulumi.std.inputs.AbsArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.AbsPlainArgs;
import java.util.concurrent.CompletableFuture;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;

class SampleAsyncArgs extends ResourceArgs {
@Import(name="length", required=true)
private Output<Integer> length;

public Output<Integer> length() {
return this.length;
}

private SampleAsyncArgs() {}

public SampleAsyncArgs(Output<Integer> length) {
this.length = length;
}
}

// This component is a test case for a number of async operations to make sure they work
// correctly for our context-aware completable future implementation.
class SampleAsync extends ComponentResource {
@Export(name="value", refs={String.class}, tree="[0]")
public final Output<String> value;

public SampleAsync(String name, SampleAsyncArgs args, ComponentResourceOptions opts) {
super("javap:index:SampleAsync", name, null, opts);

var resOpts = CustomResourceOptions.builder()
.parent(this)
.build();

// First async operation for length
var asyncLength = Output.of(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
return -5.0;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Async operation interrupted", e);
}
}));

// Invoke a remote function.
var absLength = StdFunctions.abs(AbsArgs.builder().input(asyncLength).build());
var absLengthInt = absLength.applyValue(d -> d.result().intValue());

// Invoke a remote function with a plain value.
var absPlainLength = StdFunctions.absPlain(AbsPlainArgs.builder().input(-1.0).build());
var absPlainLengthInt = Output.of(absPlainLength).applyValue(d -> d.result().intValue());

var totalLength = Output.tuple(args.length(), absLengthInt, absPlainLengthInt).applyValue(values -> values.t1 + values.t2 + values.t3);

// Create the random string with modified length.
var randomString = new RandomString(name + "1",
RandomStringArgs.builder()
.length(totalLength)
.special(false)
.build(), resOpts);


// Make HTTP request based on the input length
this.value = Output.tuple(randomString.result(), totalLength)
.applyValue(values -> {
String randomStr = values.t1;
Integer todoId = (values.t2 % 10) + 1;

var randomString2 = new RandomString(name + "2",
RandomStringArgs.builder()
.length(totalLength)
.special(false)
.build(), resOpts);

try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://jsonplaceholder.typicode.com/todos/" + todoId))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Java have something like Go's test HTTP server? Or is there another way to simulate this asyncronicity? It's not ideal to make an actual HTTP request in our tests, especially to a site we don't control.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could do a Java http server but I don't think I have the right lifecycle to start/stop it...

HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/test", exchange -> {
    String response = "{\"status\":\"ok\"}";
    exchange.sendResponseHeaders(200, response.length());
    try (var os = exchange.getResponseBody()) {
        os.write(response.getBytes());
    }
});
server.start();

We could do something fake like

URI uri = URI.create("data:text/plain,Hello%20World");
HttpRequest request = HttpRequest.newBuilder().uri(uri).build();

but I'm not sure it catches all the right problems?

The TODO thing is specifically designed for this case: "Free fake and reliable API for testing and prototyping". I think we are relatively safe and get a real test with some networking and latency, which may affect the asynchronous behavior?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are relatively safe and get a real test with some networking and latency

Every network call in the tests are a potential source for flakes/CI breakages, so I'd really rather not introduce the dependency. Even the most stable services tend to break sometimes, in which case CI is gonna be red.

.build();

HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
return randomStr + " - Todo #" + todoId + ": " + response.body();
} catch (Exception e) {
throw new RuntimeException("HTTP request failed", e);
}
});
}
}
Loading