diff --git a/docs/design/sdk-api.md b/docs/design/sdk-api.md index e0d1467..2cceeeb 100644 --- a/docs/design/sdk-api.md +++ b/docs/design/sdk-api.md @@ -407,7 +407,7 @@ public interface Recording extends AutoCloseable { public interface StubRef { int index(); Optional id(); Stub definition(); - void replace(StubSpec spec); void delete(); + void replace(StubSpec spec); void delete(); // from rift 0.23.0 a by-index replace restarts the response cycle; by id keeps it (rift#1515) void replace(JsonValue stub); // escape hatch } diff --git a/docs/recording.md b/docs/recording.md index 46aff8a..4abd4df 100644 --- a/docs/recording.md +++ b/docs/recording.md @@ -46,6 +46,11 @@ RecordSpec spec = RecordSpec.builder() | `void persist(Path file)` | `stop()`, then write the replayable imposter JSON to `file`. | | `void close()` | `stop()`. | +From rift 0.23.0, an `ALWAYS` (`proxyAlways`) recording's responses also carry `_proxyResponseTime`, +the origin's response time (rift#1519). The model keeps it through `IsResponse.extra`, so it survives +`persist` and a replay. `Imposter.clearProxyResponses()` then also strips the responses `proxyAlways` +appended to stubs you declared, and restarts every remaining stub's response cycle. + `persist(file)` writes the engine's **replayable imposter definition** — the same format loadable by `rift --configfile`, rift-node, and rift-scala — so a captured golden file is portable across SDKs. diff --git a/rift-java-core/src/main/java/io/github/achirdlabs/rift/Imposter.java b/rift-java-core/src/main/java/io/github/achirdlabs/rift/Imposter.java index dcc5a0c..c33e7ca 100644 --- a/rift-java-core/src/main/java/io/github/achirdlabs/rift/Imposter.java +++ b/rift-java-core/src/main/java/io/github/achirdlabs/rift/Imposter.java @@ -159,6 +159,10 @@ public interface Imposter { *

From rift 0.19.0 this also deletes every stub a proxy recorded (one carrying {@code * recordedFrom}, e.g. from {@code predicateGenerators}), matching Mountebank; stubs you declared * are kept. Through 0.18.x the recorded stubs survived the call. + * + *

From rift 0.23.0 (rift#1519) it also strips the responses a {@code proxyAlways} proxy + * appended to stubs you declared, leaving those stubs as you wrote them, and restarts the + * response cycle of every stub that remains, as Mountebank does. */ void clearProxyResponses(); diff --git a/rift-java-core/src/main/java/io/github/achirdlabs/rift/StubRef.java b/rift-java-core/src/main/java/io/github/achirdlabs/rift/StubRef.java index 88987df..c24b0eb 100644 --- a/rift-java-core/src/main/java/io/github/achirdlabs/rift/StubRef.java +++ b/rift-java-core/src/main/java/io/github/achirdlabs/rift/StubRef.java @@ -15,6 +15,12 @@ public interface StubRef { Stub definition(); + /** + * Replaces this reference's stub. From rift 0.23.0 (rift#1515), replacing a stub addressed by + * index (the reference {@code addStub} returns) restarts its response cycle at the first + * response, as Mountebank does, even when the content is unchanged; replacing one addressed by + * id ({@link Imposter#stub(String)}) keeps the cycle where it was. + */ void replace(StubSpec spec); /** diff --git a/rift-java-core/src/main/java/io/github/achirdlabs/rift/dsl/RequestField.java b/rift-java-core/src/main/java/io/github/achirdlabs/rift/dsl/RequestField.java index 00f8f7f..112e8af 100644 --- a/rift-java-core/src/main/java/io/github/achirdlabs/rift/dsl/RequestField.java +++ b/rift-java-core/src/main/java/io/github/achirdlabs/rift/dsl/RequestField.java @@ -11,9 +11,18 @@ public enum RequestField { QUERY("query"), HEADERS("headers"), BODY("body"), - /** The client's address and port, so in effect it matches only that one connection. */ + /** + * The client's address and port, so in effect it matches only that one connection. See {@link + * #IP} for how an IPv4 client is written on a dual-stack imposter. + */ REQUEST_FROM("requestFrom"), - /** The client's IP address. */ + /** + * The client's IP address. From rift 0.23.0 (rift#1514) a host-less imposter on an embedded + * engine or in a {@code RiftContainer} listens dual-stack and records an IPv4 client in + * IPv4-mapped form, {@code ::ffff:127.0.0.1}, so a predicate written as {@code 127.0.0.1} no + * longer matches there; give the imposter {@code host("127.0.0.1")} to keep it IPv4. A spawned + * engine's imposters bind its {@code --host} and record plain IPv4. + */ IP("ip"), /** The urlencoded form body, recorded like {@link #QUERY}; nothing is recorded for a request with no form. */ FORM("form");