convex
Version:
Client for the Convex Cloud
162 lines (141 loc) • 4.46 kB
text/typescript
import { test, expect, beforeEach } from "@jest/globals";
import { RequestManager } from "./request_manager";
import { Long } from "../long.js";
import { MutationRequest } from "./protocol.js";
let requestManager: RequestManager;
beforeEach(() => {
requestManager = new RequestManager();
});
test("hasIncompleteRequests", () => {
// Starts false.
expect(requestManager.hasIncompleteRequests()).toBe(false);
// When we request a mutation it becomes true.
let message: MutationRequest = {
type: "Mutation",
requestId: 0,
udfPath: "myMutation",
args: [],
};
let _ = requestManager.request(message);
expect(requestManager.hasIncompleteRequests()).toBe(true);
// Request a second mutation and receive the first's response.
// Should still have an outstanding mutation.
message = {
type: "Mutation",
requestId: 1,
udfPath: "myMutation",
args: [],
};
_ = requestManager.request(message);
requestManager.onResponse({
type: "MutationResponse",
requestId: 0,
success: true,
result: null,
ts: Long.fromNumber(0),
logLines: [],
});
expect(requestManager.hasIncompleteRequests()).toBe(true);
// When the second response comes back we no longer have any outstanding mutations.
requestManager.onResponse({
type: "MutationResponse",
requestId: 1,
success: true,
result: null,
ts: Long.fromNumber(0),
logLines: [],
});
expect(requestManager.hasIncompleteRequests()).toBe(false);
});
/**
* A test that simulates a WebSocket disconnection half-way through the mutation
* flow.
*/
test("mutation retries", async () => {
// Request a mutation.
const message: MutationRequest = {
type: "Mutation",
requestId: 0,
udfPath: "myMutation",
args: [],
};
const mutationRequest = requestManager.request(message);
// Receive the response.
requestManager.onResponse({
type: "MutationResponse",
requestId: 0,
success: true,
result: null,
ts: Long.fromNumber(0),
logLines: [],
});
// Pretend that we become disconnected and reconnect.
// We should request the mutation because we haven't transitioned past the
// timestamp the mutation was committed at yet.
expect(requestManager.restart()).toEqual([message]);
// Receive another response (because we restarted and requested it again)
requestManager.onResponse({
type: "MutationResponse",
requestId: 0,
success: true,
result: null,
ts: Long.fromNumber(0),
logLines: [],
});
// Transition to ts=1
requestManager.removeCompleted(Long.fromNumber(1));
// Return the result of the mutation now that we've transitioned past the
// mutation timestamp.
const result = await mutationRequest;
expect(result).toBe(null);
// The if we restart now, the mutation should no longer be re-requested.
expect(requestManager.restart()).toEqual([]);
});
/**
* Another disconnection test
*
* This time upon reconnect we transition past the mutation immediately when we
* reconnect (before getting the second response).
*/
test("mutation retries with transition", async () => {
// Request a mutation.
const message: MutationRequest = {
type: "Mutation",
requestId: 0,
udfPath: "myMutation",
args: [],
};
const mutationRequest = requestManager.request(message);
// Receive the response.
requestManager.onResponse({
type: "MutationResponse",
requestId: 0,
success: true,
result: null,
ts: Long.fromNumber(0),
logLines: [],
});
// Pretend that we become disconnected and reconnect.
// We should request the mutation because we haven't transitioned past the
// timestamp the mutation was committed at yet.
expect(requestManager.restart()).toEqual([message]);
// Transition to ts=1
requestManager.removeCompleted(Long.fromNumber(1));
// Return the result of the mutation now that we've transitioned past the
// mutation timestamp.
const result = await mutationRequest;
expect(result).toBe(null);
// The if we restart now, the mutation should no longer be re-requested because
// we've already observed it.
expect(requestManager.restart()).toEqual([]);
// Receive another response (because we requested it again).
// This response just needs to not crash the client.
requestManager.onResponse({
type: "MutationResponse",
requestId: 0,
success: true,
result: null,
ts: Long.fromNumber(0),
logLines: [],
});
});