@voidhash/mimic-effect 1.0.0-beta.16 → 1.0.0-beta.18
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/ColdStorage.cjs +1 -1
- package/dist/ColdStorage.d.cts +2 -2
- package/dist/ColdStorage.d.cts.map +1 -1
- package/dist/ColdStorage.d.mts +2 -2
- package/dist/ColdStorage.d.mts.map +1 -1
- package/dist/ColdStorage.mjs +2 -2
- package/dist/ColdStorage.mjs.map +1 -1
- package/dist/DocumentInstance.cjs +13 -13
- package/dist/DocumentInstance.mjs +13 -13
- package/dist/DocumentInstance.mjs.map +1 -1
- package/dist/Errors.d.cts +8 -8
- package/dist/Errors.d.cts.map +1 -1
- package/dist/Errors.d.mts +8 -8
- package/dist/Errors.d.mts.map +1 -1
- package/dist/HotStorage.cjs +1 -1
- package/dist/HotStorage.d.cts +2 -2
- package/dist/HotStorage.d.mts +2 -2
- package/dist/HotStorage.mjs +2 -2
- package/dist/HotStorage.mjs.map +1 -1
- package/dist/Metrics.cjs +6 -6
- package/dist/Metrics.d.cts +21 -23
- package/dist/Metrics.d.cts.map +1 -1
- package/dist/Metrics.d.mts +21 -23
- package/dist/Metrics.d.mts.map +1 -1
- package/dist/Metrics.mjs +7 -7
- package/dist/Metrics.mjs.map +1 -1
- package/dist/MimicAuthService.cjs +1 -1
- package/dist/MimicAuthService.d.cts +2 -2
- package/dist/MimicAuthService.d.cts.map +1 -1
- package/dist/MimicAuthService.d.mts +2 -2
- package/dist/MimicAuthService.d.mts.map +1 -1
- package/dist/MimicAuthService.mjs +2 -2
- package/dist/MimicAuthService.mjs.map +1 -1
- package/dist/MimicClusterServerEngine.cjs +38 -41
- package/dist/MimicClusterServerEngine.d.cts +1 -1
- package/dist/MimicClusterServerEngine.d.mts +1 -1
- package/dist/MimicClusterServerEngine.mjs +31 -34
- package/dist/MimicClusterServerEngine.mjs.map +1 -1
- package/dist/MimicServer.cjs +23 -23
- package/dist/MimicServer.d.cts +3 -3
- package/dist/MimicServer.d.cts.map +1 -1
- package/dist/MimicServer.d.mts +3 -3
- package/dist/MimicServer.d.mts.map +1 -1
- package/dist/MimicServer.mjs +22 -22
- package/dist/MimicServer.mjs.map +1 -1
- package/dist/MimicServerEngine.cjs +13 -13
- package/dist/MimicServerEngine.d.cts +2 -2
- package/dist/MimicServerEngine.d.mts +2 -2
- package/dist/MimicServerEngine.mjs +14 -14
- package/dist/MimicServerEngine.mjs.map +1 -1
- package/dist/PresenceManager.cjs +4 -4
- package/dist/PresenceManager.d.cts +2 -2
- package/dist/PresenceManager.d.mts +2 -2
- package/dist/PresenceManager.mjs +5 -5
- package/dist/PresenceManager.mjs.map +1 -1
- package/dist/Types.d.cts +1 -1
- package/dist/Types.d.mts +1 -1
- package/dist/testing/ColdStorageTestSuite.cjs +3 -3
- package/dist/testing/ColdStorageTestSuite.mjs +3 -3
- package/dist/testing/ColdStorageTestSuite.mjs.map +1 -1
- package/dist/testing/HotStorageTestSuite.cjs +13 -13
- package/dist/testing/HotStorageTestSuite.mjs +13 -13
- package/dist/testing/HotStorageTestSuite.mjs.map +1 -1
- package/dist/testing/StorageIntegrationTestSuite.cjs +3 -3
- package/dist/testing/StorageIntegrationTestSuite.mjs +3 -3
- package/dist/testing/StorageIntegrationTestSuite.mjs.map +1 -1
- package/dist/testing/types.d.cts +3 -3
- package/dist/testing/types.d.cts.map +1 -1
- package/dist/testing/types.d.mts +1 -1
- package/dist/testing/types.d.mts.map +1 -1
- package/package.json +17 -21
- package/src/ColdStorage.ts +4 -5
- package/src/DocumentInstance.ts +13 -13
- package/src/HotStorage.ts +3 -3
- package/src/Metrics.ts +22 -16
- package/src/MimicAuthService.ts +3 -3
- package/src/MimicClusterServerEngine.ts +35 -35
- package/src/MimicServer.ts +26 -30
- package/src/MimicServerEngine.ts +15 -15
- package/src/PresenceManager.ts +6 -6
- package/src/Types.ts +1 -1
- package/src/testing/ColdStorageTestSuite.ts +3 -3
- package/src/testing/HotStorageTestSuite.ts +17 -17
- package/src/testing/StorageIntegrationTestSuite.ts +3 -3
- package/.turbo/turbo-build.log +0 -154
- package/tests/ColdStorage.test.ts +0 -24
- package/tests/DocumentInstance.test.ts +0 -669
- package/tests/HotStorage.test.ts +0 -24
- package/tests/MimicAuthService.test.ts +0 -153
- package/tests/MimicClusterServerEngine.test.ts +0 -587
- package/tests/MimicServer.test.ts +0 -142
- package/tests/MimicServerEngine.test.ts +0 -547
- package/tests/PresenceManager.test.ts +0 -380
- package/tests/Protocol.test.ts +0 -190
- package/tests/StorageIntegration.test.ts +0 -259
- package/tsconfig.build.json +0 -24
- package/tsconfig.json +0 -8
- package/tsdown.config.ts +0 -18
- package/vitest.mts +0 -11
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { Effect, Layer } from "effect";
|
|
3
|
-
import { Schema } from "effect";
|
|
4
|
-
import { Primitive, Presence } from "@voidhash/mimic";
|
|
5
|
-
import { MimicServer } from "../src/MimicServer";
|
|
6
|
-
import { MimicServerEngine } from "../src/MimicServerEngine";
|
|
7
|
-
import { ColdStorage } from "../src/ColdStorage";
|
|
8
|
-
import { HotStorage } from "../src/HotStorage";
|
|
9
|
-
import { MimicAuthService } from "../src/MimicAuthService";
|
|
10
|
-
|
|
11
|
-
// =============================================================================
|
|
12
|
-
// Test Schema
|
|
13
|
-
// =============================================================================
|
|
14
|
-
|
|
15
|
-
const TestSchema = Primitive.Struct({
|
|
16
|
-
title: Primitive.String().default(""),
|
|
17
|
-
count: Primitive.Number().default(0),
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const CursorPresence = Presence.make({
|
|
21
|
-
schema: Schema.Struct({
|
|
22
|
-
x: Schema.Number,
|
|
23
|
-
y: Schema.Number,
|
|
24
|
-
name: Schema.optional(Schema.String),
|
|
25
|
-
}),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// =============================================================================
|
|
29
|
-
// MimicServer Tests
|
|
30
|
-
// =============================================================================
|
|
31
|
-
|
|
32
|
-
describe("MimicServer", () => {
|
|
33
|
-
describe("layerHttpLayerRouter", () => {
|
|
34
|
-
it("should create route layer with default config", () => {
|
|
35
|
-
const route = MimicServer.layerHttpLayerRouter();
|
|
36
|
-
|
|
37
|
-
// Should return a Layer
|
|
38
|
-
expect(route).toBeDefined();
|
|
39
|
-
// Layer should have the proper structure
|
|
40
|
-
expect(typeof route).toBe("object");
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("should create route layer with custom path", () => {
|
|
44
|
-
const route = MimicServer.layerHttpLayerRouter({
|
|
45
|
-
path: "/custom-mimic",
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
expect(route).toBeDefined();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("should create route layer with custom heartbeat config", () => {
|
|
52
|
-
const route = MimicServer.layerHttpLayerRouter({
|
|
53
|
-
path: "/mimic",
|
|
54
|
-
heartbeatInterval: "15 seconds",
|
|
55
|
-
heartbeatTimeout: "5 seconds",
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
expect(route).toBeDefined();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("should compose with engine layer", () => {
|
|
62
|
-
// Create the engine
|
|
63
|
-
const Engine = MimicServerEngine.make({
|
|
64
|
-
schema: TestSchema,
|
|
65
|
-
initial: { title: "Untitled" },
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// Create the route layer
|
|
69
|
-
const MimicRoute = MimicServer.layerHttpLayerRouter({
|
|
70
|
-
path: "/mimic",
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Wire together - this should type-check
|
|
74
|
-
const MimicLive = MimicRoute.pipe(
|
|
75
|
-
Layer.provide(Engine),
|
|
76
|
-
Layer.provide(ColdStorage.InMemory.make()),
|
|
77
|
-
Layer.provide(HotStorage.InMemory.make()),
|
|
78
|
-
Layer.provide(MimicAuthService.NoAuth.make())
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
expect(MimicLive).toBeDefined();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("should compose with presence-enabled engine", () => {
|
|
85
|
-
// Create the engine with presence
|
|
86
|
-
const Engine = MimicServerEngine.make({
|
|
87
|
-
schema: TestSchema,
|
|
88
|
-
initial: { title: "Untitled" },
|
|
89
|
-
presence: CursorPresence,
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// Create the route layer
|
|
93
|
-
const MimicRoute = MimicServer.layerHttpLayerRouter({
|
|
94
|
-
path: "/mimic",
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Wire together
|
|
98
|
-
const MimicLive = MimicRoute.pipe(
|
|
99
|
-
Layer.provide(Engine),
|
|
100
|
-
Layer.provide(ColdStorage.InMemory.make()),
|
|
101
|
-
Layer.provide(HotStorage.InMemory.make()),
|
|
102
|
-
Layer.provide(MimicAuthService.NoAuth.make())
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
expect(MimicLive).toBeDefined();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("should compose with static auth", () => {
|
|
109
|
-
// Create the engine
|
|
110
|
-
const Engine = MimicServerEngine.make({
|
|
111
|
-
schema: TestSchema,
|
|
112
|
-
initial: { title: "Untitled" },
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Create the route layer
|
|
116
|
-
const MimicRoute = MimicServer.layerHttpLayerRouter({
|
|
117
|
-
path: "/mimic",
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// Wire together with static auth
|
|
121
|
-
const MimicLive = MimicRoute.pipe(
|
|
122
|
-
Layer.provide(Engine),
|
|
123
|
-
Layer.provide(ColdStorage.InMemory.make()),
|
|
124
|
-
Layer.provide(HotStorage.InMemory.make()),
|
|
125
|
-
Layer.provide(
|
|
126
|
-
MimicAuthService.Static.make({
|
|
127
|
-
permissions: { admin: "write", user: "read" },
|
|
128
|
-
defaultPermission: "read",
|
|
129
|
-
})
|
|
130
|
-
)
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
expect(MimicLive).toBeDefined();
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
describe("namespace", () => {
|
|
138
|
-
it("should export layerHttpLayerRouter function", () => {
|
|
139
|
-
expect(typeof MimicServer.layerHttpLayerRouter).toBe("function");
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
});
|
|
@@ -1,547 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { Effect, Layer, Stream } from "effect";
|
|
3
|
-
import { Schema } from "effect";
|
|
4
|
-
import { Primitive, Presence, Document, Transaction } from "@voidhash/mimic";
|
|
5
|
-
import {
|
|
6
|
-
MimicServerEngine,
|
|
7
|
-
MimicServerEngineTag,
|
|
8
|
-
} from "../src/MimicServerEngine";
|
|
9
|
-
import { ColdStorage } from "../src/ColdStorage";
|
|
10
|
-
import { HotStorage } from "../src/HotStorage";
|
|
11
|
-
import { MimicAuthService } from "../src/MimicAuthService";
|
|
12
|
-
import * as Protocol from "../src/Protocol";
|
|
13
|
-
|
|
14
|
-
// =============================================================================
|
|
15
|
-
// Test Schema
|
|
16
|
-
// =============================================================================
|
|
17
|
-
|
|
18
|
-
const TestSchema = Primitive.Struct({
|
|
19
|
-
title: Primitive.String().default(""),
|
|
20
|
-
count: Primitive.Number().default(0),
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const CursorPresence = Presence.make({
|
|
24
|
-
schema: Schema.Struct({
|
|
25
|
-
x: Schema.Number,
|
|
26
|
-
y: Schema.Number,
|
|
27
|
-
name: Schema.optional(Schema.String),
|
|
28
|
-
}),
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// =============================================================================
|
|
32
|
-
// Helper Functions
|
|
33
|
-
// =============================================================================
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Create a valid transaction using the Document API
|
|
37
|
-
*/
|
|
38
|
-
const createValidTransaction = (title: string): Transaction.Transaction => {
|
|
39
|
-
const doc = Document.make(TestSchema);
|
|
40
|
-
doc.transaction((root) => {
|
|
41
|
-
root.title.set(title);
|
|
42
|
-
});
|
|
43
|
-
return doc.flush();
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// =============================================================================
|
|
47
|
-
// Test Layer Factory
|
|
48
|
-
// =============================================================================
|
|
49
|
-
|
|
50
|
-
const makeTestLayer = (options?: {
|
|
51
|
-
withPresence?: boolean;
|
|
52
|
-
authPermissions?: Record<string, "read" | "write">;
|
|
53
|
-
initial?: { title: string; count?: number };
|
|
54
|
-
}) => {
|
|
55
|
-
const Engine = MimicServerEngine.make({
|
|
56
|
-
schema: TestSchema,
|
|
57
|
-
initial: options?.initial,
|
|
58
|
-
presence: options?.withPresence ? CursorPresence : undefined,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
const authLayer = options?.authPermissions
|
|
62
|
-
? MimicAuthService.Static.make({
|
|
63
|
-
permissions: options.authPermissions,
|
|
64
|
-
defaultPermission: undefined,
|
|
65
|
-
})
|
|
66
|
-
: MimicAuthService.NoAuth.make();
|
|
67
|
-
|
|
68
|
-
return Engine.pipe(
|
|
69
|
-
Layer.provide(ColdStorage.InMemory.make()),
|
|
70
|
-
Layer.provide(HotStorage.InMemory.make()),
|
|
71
|
-
Layer.provide(authLayer)
|
|
72
|
-
);
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// =============================================================================
|
|
76
|
-
// MimicServerEngine Tests
|
|
77
|
-
// =============================================================================
|
|
78
|
-
|
|
79
|
-
describe("MimicServerEngine", () => {
|
|
80
|
-
describe("make", () => {
|
|
81
|
-
it("should create engine with minimal config", async () => {
|
|
82
|
-
const Engine = MimicServerEngine.make({
|
|
83
|
-
schema: TestSchema,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
const layer = Engine.pipe(
|
|
87
|
-
Layer.provide(ColdStorage.InMemory.make()),
|
|
88
|
-
Layer.provide(HotStorage.InMemory.make()),
|
|
89
|
-
Layer.provide(MimicAuthService.NoAuth.make())
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
const result = await Effect.runPromise(
|
|
93
|
-
Effect.scoped(
|
|
94
|
-
Effect.gen(function* () {
|
|
95
|
-
const engine = yield* MimicServerEngineTag;
|
|
96
|
-
// Engine should have document management methods
|
|
97
|
-
return (
|
|
98
|
-
typeof engine.submit === "function" &&
|
|
99
|
-
typeof engine.getSnapshot === "function" &&
|
|
100
|
-
typeof engine.subscribe === "function" &&
|
|
101
|
-
typeof engine.touch === "function" &&
|
|
102
|
-
engine.config !== undefined
|
|
103
|
-
);
|
|
104
|
-
})
|
|
105
|
-
).pipe(Effect.provide(layer))
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
expect(result).toBe(true);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("should create engine with full config", async () => {
|
|
112
|
-
const Engine = MimicServerEngine.make({
|
|
113
|
-
schema: TestSchema,
|
|
114
|
-
initial: { title: "Default Title" },
|
|
115
|
-
maxIdleTime: "10 minutes",
|
|
116
|
-
maxTransactionHistory: 500,
|
|
117
|
-
snapshot: {
|
|
118
|
-
interval: "1 minute",
|
|
119
|
-
transactionThreshold: 50,
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
const layer = Engine.pipe(
|
|
124
|
-
Layer.provide(ColdStorage.InMemory.make()),
|
|
125
|
-
Layer.provide(HotStorage.InMemory.make()),
|
|
126
|
-
Layer.provide(MimicAuthService.NoAuth.make())
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const result = await Effect.runPromise(
|
|
130
|
-
Effect.scoped(
|
|
131
|
-
Effect.gen(function* () {
|
|
132
|
-
const engine = yield* MimicServerEngineTag;
|
|
133
|
-
return engine.config !== undefined;
|
|
134
|
-
})
|
|
135
|
-
).pipe(Effect.provide(layer))
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
expect(result).toBe(true);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it("should work with static auth", async () => {
|
|
142
|
-
const Engine = MimicServerEngine.make({
|
|
143
|
-
schema: TestSchema,
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
const layer = Engine.pipe(
|
|
147
|
-
Layer.provide(ColdStorage.InMemory.make()),
|
|
148
|
-
Layer.provide(HotStorage.InMemory.make()),
|
|
149
|
-
Layer.provide(
|
|
150
|
-
MimicAuthService.Static.make({
|
|
151
|
-
permissions: { admin: "write", user: "read" },
|
|
152
|
-
})
|
|
153
|
-
)
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
const result = await Effect.runPromise(
|
|
157
|
-
Effect.scoped(
|
|
158
|
-
Effect.gen(function* () {
|
|
159
|
-
const engine = yield* MimicServerEngineTag;
|
|
160
|
-
return engine.config !== undefined;
|
|
161
|
-
})
|
|
162
|
-
).pipe(Effect.provide(layer))
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
expect(result).toBe(true);
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe("document management", () => {
|
|
170
|
-
it("should get snapshot for new document", async () => {
|
|
171
|
-
const result = await Effect.runPromise(
|
|
172
|
-
Effect.scoped(
|
|
173
|
-
Effect.gen(function* () {
|
|
174
|
-
const engine = yield* MimicServerEngineTag;
|
|
175
|
-
const snapshot = yield* engine.getSnapshot("test-doc-1");
|
|
176
|
-
return snapshot;
|
|
177
|
-
})
|
|
178
|
-
).pipe(Effect.provide(makeTestLayer({ initial: { title: "Initial" } })))
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
// Note: state only includes properties that were explicitly set (not defaults)
|
|
182
|
-
expect(result.state).toEqual({ title: "Initial" });
|
|
183
|
-
expect(result.version).toBe(0);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it("should submit transaction and update state", async () => {
|
|
187
|
-
const tx = createValidTransaction("Updated Title");
|
|
188
|
-
|
|
189
|
-
const result = await Effect.runPromise(
|
|
190
|
-
Effect.scoped(
|
|
191
|
-
Effect.gen(function* () {
|
|
192
|
-
const engine = yield* MimicServerEngineTag;
|
|
193
|
-
|
|
194
|
-
// Submit transaction
|
|
195
|
-
const submitResult = yield* engine.submit("test-doc-2", tx);
|
|
196
|
-
|
|
197
|
-
// Get snapshot
|
|
198
|
-
const snapshot = yield* engine.getSnapshot("test-doc-2");
|
|
199
|
-
|
|
200
|
-
return { submitResult, snapshot };
|
|
201
|
-
})
|
|
202
|
-
).pipe(Effect.provide(makeTestLayer({ initial: { title: "Initial" } })))
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
expect(result.submitResult.success).toBe(true);
|
|
206
|
-
if (result.submitResult.success) {
|
|
207
|
-
expect(result.submitResult.version).toBe(1);
|
|
208
|
-
}
|
|
209
|
-
// Note: state only includes properties that were explicitly set
|
|
210
|
-
expect(result.snapshot.state).toEqual({ title: "Updated Title" });
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("should get tree snapshot for rendering", async () => {
|
|
214
|
-
const tx = createValidTransaction("Tree Snapshot Test");
|
|
215
|
-
|
|
216
|
-
const result = await Effect.runPromise(
|
|
217
|
-
Effect.scoped(
|
|
218
|
-
Effect.gen(function* () {
|
|
219
|
-
const engine = yield* MimicServerEngineTag;
|
|
220
|
-
|
|
221
|
-
// Submit transaction
|
|
222
|
-
yield* engine.submit("test-doc-tree", tx);
|
|
223
|
-
|
|
224
|
-
// Get tree snapshot (for rendering)
|
|
225
|
-
const treeSnapshot = yield* engine.getTreeSnapshot("test-doc-tree");
|
|
226
|
-
|
|
227
|
-
return treeSnapshot;
|
|
228
|
-
})
|
|
229
|
-
).pipe(Effect.provide(makeTestLayer({ initial: { title: "Initial" } })))
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
// Tree snapshot should have the expected structure with defaults resolved
|
|
233
|
-
expect(result).toEqual({
|
|
234
|
-
title: "Tree Snapshot Test",
|
|
235
|
-
count: 0, // Default value
|
|
236
|
-
});
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
it("should touch document to update activity time", async () => {
|
|
240
|
-
const result = await Effect.runPromise(
|
|
241
|
-
Effect.scoped(
|
|
242
|
-
Effect.gen(function* () {
|
|
243
|
-
const engine = yield* MimicServerEngineTag;
|
|
244
|
-
|
|
245
|
-
// Get document first (creates it)
|
|
246
|
-
yield* engine.getSnapshot("test-doc-3");
|
|
247
|
-
|
|
248
|
-
// Touch should not throw
|
|
249
|
-
yield* engine.touch("test-doc-3");
|
|
250
|
-
|
|
251
|
-
return true;
|
|
252
|
-
})
|
|
253
|
-
).pipe(Effect.provide(makeTestLayer()))
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
expect(result).toBe(true);
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
describe("presence management", () => {
|
|
261
|
-
it("should set and get presence", async () => {
|
|
262
|
-
const result = await Effect.runPromise(
|
|
263
|
-
Effect.scoped(
|
|
264
|
-
Effect.gen(function* () {
|
|
265
|
-
const engine = yield* MimicServerEngineTag;
|
|
266
|
-
|
|
267
|
-
// Set presence
|
|
268
|
-
yield* engine.setPresence("doc-1", "conn-1", {
|
|
269
|
-
data: { x: 10, y: 20 },
|
|
270
|
-
userId: "user-1",
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
// Get snapshot
|
|
274
|
-
const snapshot = yield* engine.getPresenceSnapshot("doc-1");
|
|
275
|
-
|
|
276
|
-
return snapshot;
|
|
277
|
-
})
|
|
278
|
-
).pipe(Effect.provide(makeTestLayer({ withPresence: true })))
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
expect(result.presences["conn-1"]).toEqual({
|
|
282
|
-
data: { x: 10, y: 20 },
|
|
283
|
-
userId: "user-1",
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it("should remove presence", async () => {
|
|
288
|
-
const result = await Effect.runPromise(
|
|
289
|
-
Effect.scoped(
|
|
290
|
-
Effect.gen(function* () {
|
|
291
|
-
const engine = yield* MimicServerEngineTag;
|
|
292
|
-
|
|
293
|
-
// Set presence
|
|
294
|
-
yield* engine.setPresence("doc-2", "conn-2", {
|
|
295
|
-
data: { x: 10, y: 20 },
|
|
296
|
-
userId: "user-1",
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
// Remove presence
|
|
300
|
-
yield* engine.removePresence("doc-2", "conn-2");
|
|
301
|
-
|
|
302
|
-
// Get snapshot
|
|
303
|
-
const snapshot = yield* engine.getPresenceSnapshot("doc-2");
|
|
304
|
-
|
|
305
|
-
return snapshot;
|
|
306
|
-
})
|
|
307
|
-
).pipe(Effect.provide(makeTestLayer({ withPresence: true })))
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
expect(result.presences["conn-2"]).toBeUndefined();
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
describe("Tag", () => {
|
|
315
|
-
it("should have correct identifier", () => {
|
|
316
|
-
expect(MimicServerEngineTag.key).toBe(
|
|
317
|
-
"@voidhash/mimic-effect/MimicServerEngine"
|
|
318
|
-
);
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
describe("presence validation", () => {
|
|
323
|
-
it("should validate presence data against schema using Presence module", () => {
|
|
324
|
-
const validData = { x: 100, y: 200 };
|
|
325
|
-
const invalidData = { x: "invalid", y: 200 };
|
|
326
|
-
|
|
327
|
-
// Valid data should pass validation
|
|
328
|
-
const validated = Presence.validateSafe(CursorPresence, validData);
|
|
329
|
-
expect(validated).toEqual({ x: 100, y: 200 });
|
|
330
|
-
|
|
331
|
-
// Invalid data should return undefined
|
|
332
|
-
const invalidResult = Presence.validateSafe(CursorPresence, invalidData);
|
|
333
|
-
expect(invalidResult).toBeUndefined();
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
it("should handle optional fields in presence schema", () => {
|
|
337
|
-
// Without optional field
|
|
338
|
-
const withoutName = Presence.validateSafe(CursorPresence, {
|
|
339
|
-
x: 10,
|
|
340
|
-
y: 20,
|
|
341
|
-
});
|
|
342
|
-
expect(withoutName).toEqual({ x: 10, y: 20 });
|
|
343
|
-
|
|
344
|
-
// With optional field
|
|
345
|
-
const withName = Presence.validateSafe(CursorPresence, {
|
|
346
|
-
x: 10,
|
|
347
|
-
y: 20,
|
|
348
|
-
name: "Alice",
|
|
349
|
-
});
|
|
350
|
-
expect(withName).toEqual({ x: 10, y: 20, name: "Alice" });
|
|
351
|
-
});
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
describe("protocol message types", () => {
|
|
355
|
-
it("should create auth_result success message", () => {
|
|
356
|
-
const message = Protocol.authResultSuccess("user-123", "write");
|
|
357
|
-
expect(message).toEqual({
|
|
358
|
-
type: "auth_result",
|
|
359
|
-
success: true,
|
|
360
|
-
userId: "user-123",
|
|
361
|
-
permission: "write",
|
|
362
|
-
});
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
it("should create auth_result failure message", () => {
|
|
366
|
-
const message = Protocol.authResultFailure("Invalid token");
|
|
367
|
-
expect(message).toEqual({
|
|
368
|
-
type: "auth_result",
|
|
369
|
-
success: false,
|
|
370
|
-
error: "Invalid token",
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
it("should create presence_snapshot message", () => {
|
|
375
|
-
const message = Protocol.presenceSnapshotMessage("conn-123", {
|
|
376
|
-
"conn-456": { data: { x: 10, y: 20 }, userId: "user-1" },
|
|
377
|
-
});
|
|
378
|
-
expect(message).toEqual({
|
|
379
|
-
type: "presence_snapshot",
|
|
380
|
-
selfId: "conn-123",
|
|
381
|
-
presences: {
|
|
382
|
-
"conn-456": { data: { x: 10, y: 20 }, userId: "user-1" },
|
|
383
|
-
},
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
it("should create presence_update message", () => {
|
|
388
|
-
const message = Protocol.presenceUpdateMessage("conn-789", { x: 50, y: 75 }, "user-2");
|
|
389
|
-
expect(message).toEqual({
|
|
390
|
-
type: "presence_update",
|
|
391
|
-
id: "conn-789",
|
|
392
|
-
data: { x: 50, y: 75 },
|
|
393
|
-
userId: "user-2",
|
|
394
|
-
});
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
it("should create presence_remove message", () => {
|
|
398
|
-
const message = Protocol.presenceRemoveMessage("conn-disconnected");
|
|
399
|
-
expect(message).toEqual({
|
|
400
|
-
type: "presence_remove",
|
|
401
|
-
id: "conn-disconnected",
|
|
402
|
-
});
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
it("should create pong message", () => {
|
|
406
|
-
const message = Protocol.pong();
|
|
407
|
-
expect(message).toEqual({ type: "pong" });
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
it("should create snapshot message", () => {
|
|
411
|
-
const message = Protocol.snapshotMessage({ title: "Test", count: 5 }, 42);
|
|
412
|
-
expect(message).toEqual({
|
|
413
|
-
type: "snapshot",
|
|
414
|
-
state: { title: "Test", count: 5 },
|
|
415
|
-
version: 42,
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
it("should create error message", () => {
|
|
420
|
-
const message = Protocol.errorMessage("tx-123", "Duplicate transaction");
|
|
421
|
-
expect(message).toEqual({
|
|
422
|
-
type: "error",
|
|
423
|
-
transactionId: "tx-123",
|
|
424
|
-
reason: "Duplicate transaction",
|
|
425
|
-
});
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it("should create transaction message", () => {
|
|
429
|
-
const tx = createValidTransaction("Test");
|
|
430
|
-
const message = Protocol.transactionMessage(tx, 5);
|
|
431
|
-
expect(message.type).toBe("transaction");
|
|
432
|
-
expect(message.version).toBe(5);
|
|
433
|
-
expect(message.transaction.id).toBe(tx.id);
|
|
434
|
-
});
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
describe("protocol encoding/decoding", () => {
|
|
438
|
-
it("should parse auth message", async () => {
|
|
439
|
-
const result = await Effect.runPromise(
|
|
440
|
-
Protocol.parseClientMessage(JSON.stringify({ type: "auth", token: "test-token" }))
|
|
441
|
-
);
|
|
442
|
-
expect(result).toEqual({ type: "auth", token: "test-token" });
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
it("should parse ping message", async () => {
|
|
446
|
-
const result = await Effect.runPromise(
|
|
447
|
-
Protocol.parseClientMessage(JSON.stringify({ type: "ping" }))
|
|
448
|
-
);
|
|
449
|
-
expect(result).toEqual({ type: "ping" });
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
it("should parse request_snapshot message", async () => {
|
|
453
|
-
const result = await Effect.runPromise(
|
|
454
|
-
Protocol.parseClientMessage(JSON.stringify({ type: "request_snapshot" }))
|
|
455
|
-
);
|
|
456
|
-
expect(result).toEqual({ type: "request_snapshot" });
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
it("should parse presence_set message", async () => {
|
|
460
|
-
const result = await Effect.runPromise(
|
|
461
|
-
Protocol.parseClientMessage(
|
|
462
|
-
JSON.stringify({ type: "presence_set", data: { x: 10, y: 20 } })
|
|
463
|
-
)
|
|
464
|
-
);
|
|
465
|
-
expect(result).toEqual({ type: "presence_set", data: { x: 10, y: 20 } });
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
it("should parse presence_clear message", async () => {
|
|
469
|
-
const result = await Effect.runPromise(
|
|
470
|
-
Protocol.parseClientMessage(JSON.stringify({ type: "presence_clear" }))
|
|
471
|
-
);
|
|
472
|
-
expect(result).toEqual({ type: "presence_clear" });
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
it("should encode and decode transaction message correctly", () => {
|
|
476
|
-
const tx = createValidTransaction("Hello");
|
|
477
|
-
const message = Protocol.transactionMessage(tx, 10);
|
|
478
|
-
const encoded = Protocol.encodeServerMessage(message);
|
|
479
|
-
const decoded = JSON.parse(encoded);
|
|
480
|
-
|
|
481
|
-
expect(decoded.type).toBe("transaction");
|
|
482
|
-
expect(decoded.version).toBe(10);
|
|
483
|
-
// Transaction should be encoded
|
|
484
|
-
expect(decoded.transaction).toBeDefined();
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
it("should encode snapshot message as JSON", () => {
|
|
488
|
-
const message = Protocol.snapshotMessage({ title: "Test" }, 5);
|
|
489
|
-
const encoded = Protocol.encodeServerMessage(message);
|
|
490
|
-
const decoded = JSON.parse(encoded);
|
|
491
|
-
|
|
492
|
-
expect(decoded).toEqual({
|
|
493
|
-
type: "snapshot",
|
|
494
|
-
state: { title: "Test" },
|
|
495
|
-
version: 5,
|
|
496
|
-
});
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
describe("auth integration", () => {
|
|
501
|
-
it("should authenticate with static auth service", async () => {
|
|
502
|
-
const result = await Effect.runPromise(
|
|
503
|
-
Effect.scoped(
|
|
504
|
-
Effect.gen(function* () {
|
|
505
|
-
const engine = yield* MimicServerEngineTag;
|
|
506
|
-
// Just verify the engine was created with auth layer
|
|
507
|
-
return engine.config !== undefined;
|
|
508
|
-
})
|
|
509
|
-
).pipe(
|
|
510
|
-
Effect.provide(
|
|
511
|
-
makeTestLayer({
|
|
512
|
-
authPermissions: { admin: "write", viewer: "read" },
|
|
513
|
-
})
|
|
514
|
-
)
|
|
515
|
-
)
|
|
516
|
-
);
|
|
517
|
-
|
|
518
|
-
expect(result).toBe(true);
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
it("should create engine with presence enabled", async () => {
|
|
522
|
-
const result = await Effect.runPromise(
|
|
523
|
-
Effect.scoped(
|
|
524
|
-
Effect.gen(function* () {
|
|
525
|
-
const engine = yield* MimicServerEngineTag;
|
|
526
|
-
return engine.config.presence !== undefined;
|
|
527
|
-
})
|
|
528
|
-
).pipe(Effect.provide(makeTestLayer({ withPresence: true })))
|
|
529
|
-
);
|
|
530
|
-
|
|
531
|
-
expect(result).toBe(true);
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
it("should create engine with initial state", async () => {
|
|
535
|
-
const result = await Effect.runPromise(
|
|
536
|
-
Effect.scoped(
|
|
537
|
-
Effect.gen(function* () {
|
|
538
|
-
const engine = yield* MimicServerEngineTag;
|
|
539
|
-
return engine.config.initial !== undefined;
|
|
540
|
-
})
|
|
541
|
-
).pipe(Effect.provide(makeTestLayer({ initial: { title: "Initial" } })))
|
|
542
|
-
);
|
|
543
|
-
|
|
544
|
-
expect(result).toBe(true);
|
|
545
|
-
});
|
|
546
|
-
});
|
|
547
|
-
});
|