@powerhousedao/reactor-api 6.0.0-dev.90 → 6.0.0-dev.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/graphql/document-model-subgraph.d.ts.map +1 -1
- package/dist/src/graphql/document-model-subgraph.js +17 -13
- package/dist/src/graphql/document-model-subgraph.js.map +1 -1
- package/dist/src/graphql/reactor/factory.d.ts.map +1 -1
- package/dist/src/graphql/reactor/gen/graphql.d.ts +17 -3
- package/dist/src/graphql/reactor/gen/graphql.d.ts.map +1 -1
- package/dist/src/graphql/reactor/gen/graphql.js +4 -1
- package/dist/src/graphql/reactor/gen/graphql.js.map +1 -1
- package/dist/src/graphql/reactor/operations.graphql +4 -1
- package/dist/src/graphql/reactor/resolvers.d.ts +11 -1
- package/dist/src/graphql/reactor/resolvers.d.ts.map +1 -1
- package/dist/src/graphql/reactor/resolvers.js +74 -3
- package/dist/src/graphql/reactor/resolvers.js.map +1 -1
- package/dist/src/graphql/reactor/schema.graphql +6 -1
- package/dist/src/utils/create-schema.d.ts.map +1 -1
- package/dist/src/utils/create-schema.js +165 -2
- package/dist/src/utils/create-schema.js.map +1 -1
- package/dist/test/fault-injection-sync.test.d.ts +2 -0
- package/dist/test/fault-injection-sync.test.d.ts.map +1 -0
- package/dist/test/fault-injection-sync.test.js +196 -0
- package/dist/test/fault-injection-sync.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -11
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { ConsoleLogger, DocumentModelRegistry, EventBus, GqlRequestChannel, GqlResponseChannelFactory, InMemoryQueue, NullDocumentModelResolver, ReactorBuilder, SyncBuilder, } from "@powerhousedao/reactor";
|
|
2
|
+
import { driveDocumentModelModule } from "document-drive";
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { ManualPollTimer, createMockCursorStorage, createMockLogger, createMockOperationIndex, } from "../../reactor/test/sync/channels/gql-req-channel/test-helpers.js";
|
|
5
|
+
import { createResolverBridge } from "./utils/gql-resolver-bridge.js";
|
|
6
|
+
class FaultInjector {
|
|
7
|
+
faults = new Map();
|
|
8
|
+
delegate;
|
|
9
|
+
constructor(delegate) {
|
|
10
|
+
this.delegate = delegate;
|
|
11
|
+
}
|
|
12
|
+
injectFault(op, rule) {
|
|
13
|
+
if (!this.faults.has(op)) {
|
|
14
|
+
this.faults.set(op, []);
|
|
15
|
+
}
|
|
16
|
+
this.faults.get(op).push(rule);
|
|
17
|
+
}
|
|
18
|
+
clearFaults() {
|
|
19
|
+
this.faults.clear();
|
|
20
|
+
}
|
|
21
|
+
get fetch() {
|
|
22
|
+
return async (input, init) => {
|
|
23
|
+
if (!init?.body) {
|
|
24
|
+
return this.delegate(input, init);
|
|
25
|
+
}
|
|
26
|
+
const body = JSON.parse(init.body);
|
|
27
|
+
let operationName;
|
|
28
|
+
if (body.query.includes("touchChannel")) {
|
|
29
|
+
operationName = "touchChannel";
|
|
30
|
+
}
|
|
31
|
+
else if (body.query.includes("pollSyncEnvelopes")) {
|
|
32
|
+
operationName = "pollSyncEnvelopes";
|
|
33
|
+
}
|
|
34
|
+
else if (body.query.includes("pushSyncEnvelopes")) {
|
|
35
|
+
operationName = "pushSyncEnvelopes";
|
|
36
|
+
}
|
|
37
|
+
if (operationName) {
|
|
38
|
+
const queue = this.faults.get(operationName);
|
|
39
|
+
if (queue && queue.length > 0) {
|
|
40
|
+
const rule = queue.shift();
|
|
41
|
+
if (rule.type === "http") {
|
|
42
|
+
return new Response(JSON.stringify({}), {
|
|
43
|
+
status: rule.statusCode,
|
|
44
|
+
statusText: rule.statusText,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (rule.type === "network") {
|
|
48
|
+
throw new Error(rule.message);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return this.delegate(input, init);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Tests
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
describe("Fault-Injection Sync", () => {
|
|
60
|
+
const CHANNEL_ID = "fault-test-channel";
|
|
61
|
+
let serverModule;
|
|
62
|
+
let serverSyncManager;
|
|
63
|
+
let channel;
|
|
64
|
+
let manualTimer;
|
|
65
|
+
let faultInjector;
|
|
66
|
+
beforeEach(async () => {
|
|
67
|
+
// Build server with real timers (buildModule may use async I/O)
|
|
68
|
+
const syncManagerRegistry = new Map();
|
|
69
|
+
const resolverBridge = createResolverBridge(syncManagerRegistry, {
|
|
70
|
+
log: false,
|
|
71
|
+
});
|
|
72
|
+
const serverEventBus = new EventBus();
|
|
73
|
+
const registry = new DocumentModelRegistry();
|
|
74
|
+
registry.registerModules(driveDocumentModelModule);
|
|
75
|
+
const resolver = new NullDocumentModelResolver(registry);
|
|
76
|
+
const serverQueue = new InMemoryQueue(serverEventBus, resolver);
|
|
77
|
+
serverModule = await new ReactorBuilder()
|
|
78
|
+
.withEventBus(serverEventBus)
|
|
79
|
+
.withQueue(serverQueue)
|
|
80
|
+
.withDocumentModels([
|
|
81
|
+
driveDocumentModelModule,
|
|
82
|
+
])
|
|
83
|
+
.withSync(new SyncBuilder().withChannelFactory(new GqlResponseChannelFactory(new ConsoleLogger(["test"]))))
|
|
84
|
+
.buildModule();
|
|
85
|
+
serverSyncManager = serverModule.syncModule.syncManager;
|
|
86
|
+
syncManagerRegistry.set("server", serverSyncManager);
|
|
87
|
+
// Switch to fake timers (recovery backoff uses setTimeout)
|
|
88
|
+
vi.useFakeTimers();
|
|
89
|
+
// Client setup
|
|
90
|
+
faultInjector = new FaultInjector(resolverBridge);
|
|
91
|
+
manualTimer = new ManualPollTimer();
|
|
92
|
+
const config = {
|
|
93
|
+
url: "http://server/graphql",
|
|
94
|
+
collectionId: "test-collection",
|
|
95
|
+
filter: { documentId: [], scope: [], branch: "main" },
|
|
96
|
+
retryBaseDelayMs: 100,
|
|
97
|
+
retryMaxDelayMs: 200,
|
|
98
|
+
};
|
|
99
|
+
channel = new GqlRequestChannel(createMockLogger(), CHANNEL_ID, "remote-1", createMockCursorStorage(), { ...config, fetchFn: faultInjector.fetch }, createMockOperationIndex(), manualTimer);
|
|
100
|
+
await channel.init();
|
|
101
|
+
expect(channel.getConnectionState().state).toBe("connected");
|
|
102
|
+
});
|
|
103
|
+
afterEach(async () => {
|
|
104
|
+
await channel.shutdown();
|
|
105
|
+
serverModule.reactor.kill();
|
|
106
|
+
vi.useRealTimers();
|
|
107
|
+
});
|
|
108
|
+
// -----------------------------------------------------------------------
|
|
109
|
+
// Test 1: Poll 401 -> stops polling (unrecoverable)
|
|
110
|
+
// -----------------------------------------------------------------------
|
|
111
|
+
it("poll 401 stops polling (unrecoverable)", async () => {
|
|
112
|
+
faultInjector.injectFault("pollSyncEnvelopes", {
|
|
113
|
+
type: "http",
|
|
114
|
+
statusCode: 401,
|
|
115
|
+
statusText: "Unauthorized",
|
|
116
|
+
});
|
|
117
|
+
await manualTimer.tick();
|
|
118
|
+
expect(channel.getConnectionState().state).toBe("error");
|
|
119
|
+
expect(manualTimer.isRunning()).toBe(false);
|
|
120
|
+
expect(channel.getConnectionState().failureCount).toBe(1);
|
|
121
|
+
});
|
|
122
|
+
// -----------------------------------------------------------------------
|
|
123
|
+
// Test 2: Poll 500 -> retries and recovers (recoverable)
|
|
124
|
+
// -----------------------------------------------------------------------
|
|
125
|
+
it("poll 500 retries and recovers on next tick", async () => {
|
|
126
|
+
faultInjector.injectFault("pollSyncEnvelopes", {
|
|
127
|
+
type: "http",
|
|
128
|
+
statusCode: 500,
|
|
129
|
+
statusText: "Internal Server Error",
|
|
130
|
+
});
|
|
131
|
+
// First poll: 500 error (recoverable) - rethrown by handlePollError
|
|
132
|
+
await manualTimer.tick().catch(() => { });
|
|
133
|
+
expect(channel.getConnectionState().state).toBe("error");
|
|
134
|
+
expect(manualTimer.isRunning()).toBe(true);
|
|
135
|
+
// Second poll: no faults, real resolver responds
|
|
136
|
+
await manualTimer.tick();
|
|
137
|
+
expect(channel.getConnectionState().state).toBe("connected");
|
|
138
|
+
expect(channel.getConnectionState().failureCount).toBe(0);
|
|
139
|
+
});
|
|
140
|
+
// -----------------------------------------------------------------------
|
|
141
|
+
// Test 3: Channel deleted -> recovery -> reconnect
|
|
142
|
+
// -----------------------------------------------------------------------
|
|
143
|
+
it("channel deleted triggers recovery and reconnects", async () => {
|
|
144
|
+
// Delete the channel on the server
|
|
145
|
+
await serverSyncManager.remove(CHANNEL_ID);
|
|
146
|
+
// Poll -> resolver throws "Channel not found"
|
|
147
|
+
await manualTimer.tick();
|
|
148
|
+
expect(channel.getConnectionState().state).toBe("reconnecting");
|
|
149
|
+
expect(manualTimer.isRunning()).toBe(false);
|
|
150
|
+
// Recovery touchChannel -> real resolver recreates the channel
|
|
151
|
+
await vi.advanceTimersByTimeAsync(500);
|
|
152
|
+
expect(channel.getConnectionState().state).toBe("connected");
|
|
153
|
+
expect(manualTimer.isRunning()).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
// -----------------------------------------------------------------------
|
|
156
|
+
// Test 4: Channel deleted + auth error on recovery -> stops
|
|
157
|
+
// -----------------------------------------------------------------------
|
|
158
|
+
it("channel deleted with auth error on recovery stops", async () => {
|
|
159
|
+
await serverSyncManager.remove(CHANNEL_ID);
|
|
160
|
+
// Queue a 403 for the recovery touchChannel
|
|
161
|
+
faultInjector.injectFault("touchChannel", {
|
|
162
|
+
type: "http",
|
|
163
|
+
statusCode: 403,
|
|
164
|
+
statusText: "Forbidden",
|
|
165
|
+
});
|
|
166
|
+
// Poll -> "Channel not found" -> recovery triggered
|
|
167
|
+
await manualTimer.tick();
|
|
168
|
+
expect(channel.getConnectionState().state).toBe("reconnecting");
|
|
169
|
+
// Recovery touchChannel -> 403 (unrecoverable)
|
|
170
|
+
await vi.advanceTimersByTimeAsync(500);
|
|
171
|
+
expect(channel.getConnectionState().state).toBe("error");
|
|
172
|
+
expect(manualTimer.isRunning()).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
// -----------------------------------------------------------------------
|
|
175
|
+
// Test 5: Channel deleted + transient error on recovery -> retries
|
|
176
|
+
// -----------------------------------------------------------------------
|
|
177
|
+
it("channel deleted with transient recovery error retries and succeeds", async () => {
|
|
178
|
+
await serverSyncManager.remove(CHANNEL_ID);
|
|
179
|
+
// Queue a network error for the first recovery touchChannel attempt
|
|
180
|
+
faultInjector.injectFault("touchChannel", {
|
|
181
|
+
type: "network",
|
|
182
|
+
message: "Network timeout",
|
|
183
|
+
});
|
|
184
|
+
// Poll -> "Channel not found" -> recovery triggered
|
|
185
|
+
await manualTimer.tick();
|
|
186
|
+
expect(channel.getConnectionState().state).toBe("reconnecting");
|
|
187
|
+
// First recovery attempt -> network error (recoverable), still reconnecting
|
|
188
|
+
await vi.advanceTimersByTimeAsync(50);
|
|
189
|
+
expect(channel.getConnectionState().state).toBe("reconnecting");
|
|
190
|
+
// Advance past backoff delay - second recovery attempt -> real resolver succeeds
|
|
191
|
+
await vi.advanceTimersByTimeAsync(500);
|
|
192
|
+
expect(channel.getConnectionState().state).toBe("connected");
|
|
193
|
+
expect(manualTimer.isRunning()).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
//# sourceMappingURL=fault-injection-sync.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fault-injection-sync.test.js","sourceRoot":"","sources":["../../test/fault-injection-sync.test.ts"],"names":[],"mappings":"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"}
|