@voidhash/mimic-effect 1.0.0-beta.1 → 1.0.0-beta.10
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/.turbo/turbo-build.log +116 -74
- package/dist/ColdStorage.cjs +9 -5
- package/dist/ColdStorage.d.cts.map +1 -1
- package/dist/ColdStorage.d.mts.map +1 -1
- package/dist/ColdStorage.mjs +9 -5
- package/dist/ColdStorage.mjs.map +1 -1
- package/dist/DocumentInstance.cjs +263 -0
- package/dist/DocumentInstance.d.cts +78 -0
- package/dist/DocumentInstance.d.cts.map +1 -0
- package/dist/DocumentInstance.d.mts +78 -0
- package/dist/DocumentInstance.d.mts.map +1 -0
- package/dist/DocumentInstance.mjs +264 -0
- package/dist/DocumentInstance.mjs.map +1 -0
- package/dist/Errors.cjs +10 -1
- package/dist/Errors.d.cts +18 -3
- package/dist/Errors.d.cts.map +1 -1
- package/dist/Errors.d.mts +18 -3
- package/dist/Errors.d.mts.map +1 -1
- package/dist/Errors.mjs +9 -1
- package/dist/Errors.mjs.map +1 -1
- package/dist/HotStorage.cjs +39 -12
- package/dist/HotStorage.d.cts +17 -1
- package/dist/HotStorage.d.cts.map +1 -1
- package/dist/HotStorage.d.mts +17 -1
- package/dist/HotStorage.d.mts.map +1 -1
- package/dist/HotStorage.mjs +39 -12
- package/dist/HotStorage.mjs.map +1 -1
- package/dist/Metrics.cjs +29 -1
- package/dist/Metrics.d.cts +5 -0
- package/dist/Metrics.d.cts.map +1 -1
- package/dist/Metrics.d.mts +5 -0
- package/dist/Metrics.d.mts.map +1 -1
- package/dist/Metrics.mjs +26 -1
- package/dist/Metrics.mjs.map +1 -1
- package/dist/MimicClusterServerEngine.cjs +44 -139
- package/dist/MimicClusterServerEngine.d.cts.map +1 -1
- package/dist/MimicClusterServerEngine.d.mts +1 -1
- package/dist/MimicClusterServerEngine.d.mts.map +1 -1
- package/dist/MimicClusterServerEngine.mjs +46 -141
- package/dist/MimicClusterServerEngine.mjs.map +1 -1
- package/dist/MimicServer.cjs +20 -20
- package/dist/MimicServer.d.cts.map +1 -1
- package/dist/MimicServer.d.mts.map +1 -1
- package/dist/MimicServer.mjs +20 -20
- package/dist/MimicServer.mjs.map +1 -1
- package/dist/MimicServerEngine.cjs +92 -11
- package/dist/MimicServerEngine.d.cts +12 -4
- package/dist/MimicServerEngine.d.cts.map +1 -1
- package/dist/MimicServerEngine.d.mts +12 -4
- package/dist/MimicServerEngine.d.mts.map +1 -1
- package/dist/MimicServerEngine.mjs +94 -13
- package/dist/MimicServerEngine.mjs.map +1 -1
- package/dist/PresenceManager.cjs +5 -5
- package/dist/PresenceManager.d.cts.map +1 -1
- package/dist/PresenceManager.d.mts.map +1 -1
- package/dist/PresenceManager.mjs +5 -5
- package/dist/PresenceManager.mjs.map +1 -1
- package/dist/Protocol.d.cts +1 -1
- package/dist/Protocol.d.mts +1 -1
- package/dist/Types.d.cts +9 -2
- package/dist/Types.d.cts.map +1 -1
- package/dist/Types.d.mts +9 -2
- package/dist/Types.d.mts.map +1 -1
- package/dist/index.cjs +5 -6
- package/dist/index.d.cts +3 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +3 -3
- package/dist/testing/ColdStorageTestSuite.cjs +508 -0
- package/dist/testing/ColdStorageTestSuite.d.cts +36 -0
- package/dist/testing/ColdStorageTestSuite.d.cts.map +1 -0
- package/dist/testing/ColdStorageTestSuite.d.mts +36 -0
- package/dist/testing/ColdStorageTestSuite.d.mts.map +1 -0
- package/dist/testing/ColdStorageTestSuite.mjs +508 -0
- package/dist/testing/ColdStorageTestSuite.mjs.map +1 -0
- package/dist/testing/FailingStorage.cjs +162 -0
- package/dist/testing/FailingStorage.d.cts +43 -0
- package/dist/testing/FailingStorage.d.cts.map +1 -0
- package/dist/testing/FailingStorage.d.mts +43 -0
- package/dist/testing/FailingStorage.d.mts.map +1 -0
- package/dist/testing/FailingStorage.mjs +163 -0
- package/dist/testing/FailingStorage.mjs.map +1 -0
- package/dist/testing/HotStorageTestSuite.cjs +820 -0
- package/dist/testing/HotStorageTestSuite.d.cts +42 -0
- package/dist/testing/HotStorageTestSuite.d.cts.map +1 -0
- package/dist/testing/HotStorageTestSuite.d.mts +42 -0
- package/dist/testing/HotStorageTestSuite.d.mts.map +1 -0
- package/dist/testing/HotStorageTestSuite.mjs +820 -0
- package/dist/testing/HotStorageTestSuite.mjs.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.cjs +487 -0
- package/dist/testing/StorageIntegrationTestSuite.d.cts +37 -0
- package/dist/testing/StorageIntegrationTestSuite.d.cts.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.d.mts +37 -0
- package/dist/testing/StorageIntegrationTestSuite.d.mts.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.mjs +487 -0
- package/dist/testing/StorageIntegrationTestSuite.mjs.map +1 -0
- package/dist/testing/assertions.cjs +117 -0
- package/dist/testing/assertions.mjs +112 -0
- package/dist/testing/assertions.mjs.map +1 -0
- package/dist/testing/index.cjs +14 -0
- package/dist/testing/index.d.cts +6 -0
- package/dist/testing/index.d.mts +6 -0
- package/dist/testing/index.mjs +7 -0
- package/dist/testing/types.cjs +15 -0
- package/dist/testing/types.d.cts +90 -0
- package/dist/testing/types.d.cts.map +1 -0
- package/dist/testing/types.d.mts +90 -0
- package/dist/testing/types.d.mts.map +1 -0
- package/dist/testing/types.mjs +16 -0
- package/dist/testing/types.mjs.map +1 -0
- package/package.json +8 -3
- package/src/ColdStorage.ts +21 -12
- package/src/DocumentInstance.ts +527 -0
- package/src/Errors.ts +15 -1
- package/src/HotStorage.ts +115 -24
- package/src/Metrics.ts +30 -0
- package/src/MimicClusterServerEngine.ts +120 -275
- package/src/MimicServer.ts +83 -75
- package/src/MimicServerEngine.ts +230 -30
- package/src/PresenceManager.ts +44 -34
- package/src/Types.ts +9 -2
- package/src/index.ts +5 -35
- package/src/testing/ColdStorageTestSuite.ts +589 -0
- package/src/testing/FailingStorage.ts +338 -0
- package/src/testing/HotStorageTestSuite.ts +1105 -0
- package/src/testing/StorageIntegrationTestSuite.ts +736 -0
- package/src/testing/assertions.ts +188 -0
- package/src/testing/index.ts +83 -0
- package/src/testing/types.ts +100 -0
- package/tests/ColdStorage.test.ts +8 -120
- package/tests/DocumentInstance.test.ts +669 -0
- package/tests/HotStorage.test.ts +7 -126
- package/tests/StorageIntegration.test.ts +259 -0
- package/tsdown.config.ts +1 -1
- package/dist/DocumentManager.cjs +0 -229
- package/dist/DocumentManager.d.cts +0 -59
- package/dist/DocumentManager.d.cts.map +0 -1
- package/dist/DocumentManager.d.mts +0 -59
- package/dist/DocumentManager.d.mts.map +0 -1
- package/dist/DocumentManager.mjs +0 -227
- package/dist/DocumentManager.mjs.map +0 -1
- package/src/DocumentManager.ts +0 -506
- package/tests/DocumentManager.test.ts +0 -335
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @voidhash/mimic-effect/testing - Assertion Helpers
|
|
3
|
+
*
|
|
4
|
+
* Internal assertion helpers used by the test suites.
|
|
5
|
+
*/
|
|
6
|
+
import { Effect } from "effect";
|
|
7
|
+
import { TestError } from "./types";
|
|
8
|
+
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// Deep Equality
|
|
11
|
+
// =============================================================================
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Deep equality check that handles objects, arrays, and primitives.
|
|
15
|
+
* Skips function properties since reconstructed objects (like OperationPath)
|
|
16
|
+
* will have new function instances even when the underlying data is identical.
|
|
17
|
+
*/
|
|
18
|
+
export const isDeepEqual = (a: unknown, b: unknown): boolean => {
|
|
19
|
+
if (a === b) return true;
|
|
20
|
+
|
|
21
|
+
if (a === null || b === null) return a === b;
|
|
22
|
+
if (a === undefined || b === undefined) return a === b;
|
|
23
|
+
|
|
24
|
+
if (typeof a !== typeof b) return false;
|
|
25
|
+
|
|
26
|
+
// Skip function comparison - functions with same behavior but different references should be considered equal
|
|
27
|
+
if (typeof a === "function" && typeof b === "function") return true;
|
|
28
|
+
|
|
29
|
+
if (typeof a === "number" && typeof b === "number") {
|
|
30
|
+
if (Number.isNaN(a) && Number.isNaN(b)) return true;
|
|
31
|
+
return a === b;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
35
|
+
if (a.length !== b.length) return false;
|
|
36
|
+
for (let i = 0; i < a.length; i++) {
|
|
37
|
+
if (!isDeepEqual(a[i], b[i])) return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
43
|
+
const aObj = a as Record<string, unknown>;
|
|
44
|
+
const bObj = b as Record<string, unknown>;
|
|
45
|
+
|
|
46
|
+
// Filter out function properties for comparison
|
|
47
|
+
const aKeys = Object.keys(aObj).filter(k => typeof aObj[k] !== "function");
|
|
48
|
+
const bKeys = Object.keys(bObj).filter(k => typeof bObj[k] !== "function");
|
|
49
|
+
|
|
50
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
51
|
+
|
|
52
|
+
for (const key of aKeys) {
|
|
53
|
+
if (!Object.prototype.hasOwnProperty.call(bObj, key)) return false;
|
|
54
|
+
if (!isDeepEqual(aObj[key], bObj[key])) return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return false;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// =============================================================================
|
|
64
|
+
// Assertion Helpers
|
|
65
|
+
// =============================================================================
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Assert that two values are deeply equal.
|
|
69
|
+
*/
|
|
70
|
+
export const assertEqual = <T>(
|
|
71
|
+
actual: T,
|
|
72
|
+
expected: T,
|
|
73
|
+
message: string
|
|
74
|
+
): Effect.Effect<void, TestError> =>
|
|
75
|
+
Effect.gen(function* () {
|
|
76
|
+
if (!isDeepEqual(actual, expected)) {
|
|
77
|
+
yield* Effect.fail(new TestError({ message, expected, actual }));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Assert that a condition is true.
|
|
83
|
+
*/
|
|
84
|
+
export const assertTrue = (
|
|
85
|
+
condition: boolean,
|
|
86
|
+
message: string
|
|
87
|
+
): Effect.Effect<void, TestError> =>
|
|
88
|
+
Effect.gen(function* () {
|
|
89
|
+
if (!condition) {
|
|
90
|
+
yield* Effect.fail(new TestError({ message }));
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Assert that a condition is false.
|
|
96
|
+
*/
|
|
97
|
+
export const assertFalse = (
|
|
98
|
+
condition: boolean,
|
|
99
|
+
message: string
|
|
100
|
+
): Effect.Effect<void, TestError> =>
|
|
101
|
+
Effect.gen(function* () {
|
|
102
|
+
if (condition) {
|
|
103
|
+
yield* Effect.fail(new TestError({ message }));
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Assert that a value is undefined.
|
|
109
|
+
*/
|
|
110
|
+
export const assertUndefined = (
|
|
111
|
+
value: unknown,
|
|
112
|
+
message: string
|
|
113
|
+
): Effect.Effect<void, TestError> =>
|
|
114
|
+
Effect.gen(function* () {
|
|
115
|
+
if (value !== undefined) {
|
|
116
|
+
yield* Effect.fail(
|
|
117
|
+
new TestError({ message, expected: undefined, actual: value })
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Assert that a value is defined (not undefined).
|
|
124
|
+
*/
|
|
125
|
+
export const assertDefined = <T>(
|
|
126
|
+
value: T | undefined,
|
|
127
|
+
message: string
|
|
128
|
+
): Effect.Effect<T, TestError> =>
|
|
129
|
+
Effect.gen(function* () {
|
|
130
|
+
if (value === undefined) {
|
|
131
|
+
yield* Effect.fail(
|
|
132
|
+
new TestError({ message, expected: "defined value", actual: undefined })
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return value as T;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Assert that an array has the expected length.
|
|
140
|
+
*/
|
|
141
|
+
export const assertLength = <T>(
|
|
142
|
+
array: T[],
|
|
143
|
+
expectedLength: number,
|
|
144
|
+
message: string
|
|
145
|
+
): Effect.Effect<void, TestError> =>
|
|
146
|
+
Effect.gen(function* () {
|
|
147
|
+
if (array.length !== expectedLength) {
|
|
148
|
+
yield* Effect.fail(
|
|
149
|
+
new TestError({
|
|
150
|
+
message,
|
|
151
|
+
expected: expectedLength,
|
|
152
|
+
actual: array.length,
|
|
153
|
+
})
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Assert that an array is empty.
|
|
160
|
+
*/
|
|
161
|
+
export const assertEmpty = <T>(
|
|
162
|
+
array: T[],
|
|
163
|
+
message: string
|
|
164
|
+
): Effect.Effect<void, TestError> => assertLength(array, 0, message);
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Assert that an array is sorted by a key.
|
|
168
|
+
*/
|
|
169
|
+
export const assertSortedBy = <T>(
|
|
170
|
+
array: T[],
|
|
171
|
+
key: keyof T,
|
|
172
|
+
message: string
|
|
173
|
+
): Effect.Effect<void, TestError> =>
|
|
174
|
+
Effect.gen(function* () {
|
|
175
|
+
for (let i = 1; i < array.length; i++) {
|
|
176
|
+
const prev = array[i - 1]![key];
|
|
177
|
+
const curr = array[i]![key];
|
|
178
|
+
if (prev > curr) {
|
|
179
|
+
yield* Effect.fail(
|
|
180
|
+
new TestError({
|
|
181
|
+
message,
|
|
182
|
+
expected: `array sorted by ${String(key)}`,
|
|
183
|
+
actual: `element at index ${i - 1} (${prev}) > element at index ${i} (${curr})`,
|
|
184
|
+
})
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @voidhash/mimic-effect/testing
|
|
3
|
+
*
|
|
4
|
+
* Test utilities for verifying ColdStorage and HotStorage adapter implementations.
|
|
5
|
+
*
|
|
6
|
+
* These utilities help ensure that custom storage adapters correctly implement
|
|
7
|
+
* the required interfaces and can reliably persist/retrieve data without loss.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { ColdStorageTestSuite, HotStorageTestSuite } from "@voidhash/mimic-effect/testing";
|
|
12
|
+
* import { describe, it } from "vitest";
|
|
13
|
+
* import { Effect } from "effect";
|
|
14
|
+
*
|
|
15
|
+
* // Test your ColdStorage adapter
|
|
16
|
+
* describe("MyColdStorageAdapter", () => {
|
|
17
|
+
* const layer = MyColdStorageAdapter.make();
|
|
18
|
+
*
|
|
19
|
+
* for (const test of ColdStorageTestSuite.makeTests()) {
|
|
20
|
+
* it(test.name, () =>
|
|
21
|
+
* Effect.runPromise(test.run.pipe(Effect.provide(layer)))
|
|
22
|
+
* );
|
|
23
|
+
* }
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // Test your HotStorage adapter
|
|
27
|
+
* describe("MyHotStorageAdapter", () => {
|
|
28
|
+
* const layer = MyHotStorageAdapter.make();
|
|
29
|
+
*
|
|
30
|
+
* for (const test of HotStorageTestSuite.makeTests()) {
|
|
31
|
+
* it(test.name, () =>
|
|
32
|
+
* Effect.runPromise(test.run.pipe(Effect.provide(layer)))
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @since 1.0.0
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
// =============================================================================
|
|
42
|
+
// Types
|
|
43
|
+
// =============================================================================
|
|
44
|
+
|
|
45
|
+
export type {
|
|
46
|
+
StorageTestCase,
|
|
47
|
+
TestResults,
|
|
48
|
+
FailedTest,
|
|
49
|
+
} from "./types";
|
|
50
|
+
|
|
51
|
+
export { TestError } from "./types";
|
|
52
|
+
|
|
53
|
+
// =============================================================================
|
|
54
|
+
// Test Suites
|
|
55
|
+
// =============================================================================
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
ColdStorageTestSuite,
|
|
59
|
+
type ColdStorageTestError,
|
|
60
|
+
} from "./ColdStorageTestSuite";
|
|
61
|
+
export {
|
|
62
|
+
HotStorageTestSuite,
|
|
63
|
+
type HotStorageTestError,
|
|
64
|
+
} from "./HotStorageTestSuite";
|
|
65
|
+
export { StorageIntegrationTestSuite } from "./StorageIntegrationTestSuite";
|
|
66
|
+
|
|
67
|
+
// =============================================================================
|
|
68
|
+
// Test Utilities
|
|
69
|
+
// =============================================================================
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
FailingStorage,
|
|
73
|
+
type FailingColdStorageConfig,
|
|
74
|
+
type FailingHotStorageConfig,
|
|
75
|
+
} from "./FailingStorage";
|
|
76
|
+
|
|
77
|
+
// =============================================================================
|
|
78
|
+
// Re-export Categories for Convenience
|
|
79
|
+
// =============================================================================
|
|
80
|
+
|
|
81
|
+
export { Categories as ColdStorageCategories } from "./ColdStorageTestSuite";
|
|
82
|
+
export { Categories as HotStorageCategories } from "./HotStorageTestSuite";
|
|
83
|
+
export { Categories as IntegrationCategories } from "./StorageIntegrationTestSuite";
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @voidhash/mimic-effect/testing - Core Types
|
|
3
|
+
*
|
|
4
|
+
* Types used by the storage adapter test utilities.
|
|
5
|
+
*/
|
|
6
|
+
import { Data, Effect } from "effect";
|
|
7
|
+
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// Error Types
|
|
10
|
+
// =============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when a test assertion fails.
|
|
14
|
+
*/
|
|
15
|
+
export class TestError extends Data.TaggedError("TestError")<{
|
|
16
|
+
/** Description of what failed */
|
|
17
|
+
readonly message: string;
|
|
18
|
+
/** Expected value (if applicable) */
|
|
19
|
+
readonly expected?: unknown;
|
|
20
|
+
/** Actual value received (if applicable) */
|
|
21
|
+
readonly actual?: unknown;
|
|
22
|
+
}> {}
|
|
23
|
+
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// Test Case Types
|
|
26
|
+
// =============================================================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A single storage adapter test case.
|
|
30
|
+
*
|
|
31
|
+
* Test cases are framework-agnostic Effects that can be run with any test runner.
|
|
32
|
+
*
|
|
33
|
+
* @template E - The error type for this test case
|
|
34
|
+
* @template R - The Effect requirements (e.g., ColdStorageTag or HotStorageTag)
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* // Using with vitest
|
|
39
|
+
* const tests = ColdStorageTestSuite.makeTests();
|
|
40
|
+
*
|
|
41
|
+
* describe("MyAdapter", () => {
|
|
42
|
+
* for (const test of tests) {
|
|
43
|
+
* it(test.name, () =>
|
|
44
|
+
* Effect.runPromise(test.run.pipe(Effect.provide(myAdapterLayer)))
|
|
45
|
+
* );
|
|
46
|
+
* }
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export interface StorageTestCase<E, R> {
|
|
51
|
+
/** Human-readable test name */
|
|
52
|
+
readonly name: string;
|
|
53
|
+
/** Category for grouping (e.g., "Basic Operations", "Data Integrity") */
|
|
54
|
+
readonly category: string;
|
|
55
|
+
/** The test as an Effect - succeeds if test passes, fails with error if not */
|
|
56
|
+
readonly run: Effect.Effect<void, E, R>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// Test Results Types
|
|
61
|
+
// =============================================================================
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Result of a failed test.
|
|
65
|
+
*/
|
|
66
|
+
export interface FailedTest<E, R> {
|
|
67
|
+
/** The test case that failed */
|
|
68
|
+
readonly test: StorageTestCase<E, R>;
|
|
69
|
+
/** The error that caused the failure */
|
|
70
|
+
readonly error: E;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Results from running all tests in a suite.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* const results = await Effect.runPromise(
|
|
79
|
+
* ColdStorageTestSuite.runAll().pipe(Effect.provide(myAdapterLayer))
|
|
80
|
+
* );
|
|
81
|
+
*
|
|
82
|
+
* console.log(`Passed: ${results.passCount}/${results.total}`);
|
|
83
|
+
*
|
|
84
|
+
* for (const { test, error } of results.failed) {
|
|
85
|
+
* console.error(`FAIL: ${test.name} - ${error._tag}`);
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export interface TestResults<E, R> {
|
|
90
|
+
/** Tests that passed */
|
|
91
|
+
readonly passed: StorageTestCase<E, R>[];
|
|
92
|
+
/** Tests that failed with their errors */
|
|
93
|
+
readonly failed: FailedTest<E, R>[];
|
|
94
|
+
/** Total number of tests run */
|
|
95
|
+
readonly total: number;
|
|
96
|
+
/** Number of tests that passed */
|
|
97
|
+
readonly passCount: number;
|
|
98
|
+
/** Number of tests that failed */
|
|
99
|
+
readonly failCount: number;
|
|
100
|
+
}
|
|
@@ -1,131 +1,19 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { Effect
|
|
2
|
+
import { Effect } from "effect";
|
|
3
3
|
import { ColdStorage, ColdStorageTag } from "../src/ColdStorage";
|
|
4
|
-
import
|
|
4
|
+
import { ColdStorageTestSuite } from "../src/testing";
|
|
5
5
|
|
|
6
6
|
describe("ColdStorage", () => {
|
|
7
7
|
describe("InMemory", () => {
|
|
8
|
+
// Use the test suite utilities for comprehensive testing
|
|
8
9
|
const layer = ColdStorage.InMemory.make();
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return yield* storage.load("non-existent");
|
|
15
|
-
}).pipe(Effect.provide(layer))
|
|
11
|
+
// Run all test suite tests
|
|
12
|
+
for (const test of ColdStorageTestSuite.makeTests()) {
|
|
13
|
+
it(`[${test.category}] ${test.name}`, () =>
|
|
14
|
+
Effect.runPromise(test.run.pipe(Effect.provide(layer)))
|
|
16
15
|
);
|
|
17
|
-
|
|
18
|
-
expect(result).toBeUndefined();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("should save and load document", async () => {
|
|
22
|
-
const doc: StoredDocument = {
|
|
23
|
-
state: { title: "Test", count: 42 },
|
|
24
|
-
version: 5,
|
|
25
|
-
schemaVersion: 1,
|
|
26
|
-
savedAt: Date.now(),
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const result = await Effect.runPromise(
|
|
30
|
-
Effect.gen(function* () {
|
|
31
|
-
const storage = yield* ColdStorageTag;
|
|
32
|
-
yield* storage.save("doc-1", doc);
|
|
33
|
-
return yield* storage.load("doc-1");
|
|
34
|
-
}).pipe(Effect.provide(layer))
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
expect(result).toEqual(doc);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("should update existing document", async () => {
|
|
41
|
-
const doc1: StoredDocument = {
|
|
42
|
-
state: { title: "First" },
|
|
43
|
-
version: 1,
|
|
44
|
-
schemaVersion: 1,
|
|
45
|
-
savedAt: Date.now(),
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const doc2: StoredDocument = {
|
|
49
|
-
state: { title: "Second" },
|
|
50
|
-
version: 2,
|
|
51
|
-
schemaVersion: 1,
|
|
52
|
-
savedAt: Date.now(),
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const result = await Effect.runPromise(
|
|
56
|
-
Effect.gen(function* () {
|
|
57
|
-
const storage = yield* ColdStorageTag;
|
|
58
|
-
yield* storage.save("doc-1", doc1);
|
|
59
|
-
yield* storage.save("doc-1", doc2);
|
|
60
|
-
return yield* storage.load("doc-1");
|
|
61
|
-
}).pipe(Effect.provide(layer))
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
expect(result).toEqual(doc2);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("should delete document", async () => {
|
|
68
|
-
const doc: StoredDocument = {
|
|
69
|
-
state: { title: "To Delete" },
|
|
70
|
-
version: 1,
|
|
71
|
-
schemaVersion: 1,
|
|
72
|
-
savedAt: Date.now(),
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const result = await Effect.runPromise(
|
|
76
|
-
Effect.gen(function* () {
|
|
77
|
-
const storage = yield* ColdStorageTag;
|
|
78
|
-
yield* storage.save("doc-1", doc);
|
|
79
|
-
yield* storage.delete("doc-1");
|
|
80
|
-
return yield* storage.load("doc-1");
|
|
81
|
-
}).pipe(Effect.provide(layer))
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
expect(result).toBeUndefined();
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("should handle multiple documents independently", async () => {
|
|
88
|
-
const doc1: StoredDocument = {
|
|
89
|
-
state: { title: "Doc 1" },
|
|
90
|
-
version: 1,
|
|
91
|
-
schemaVersion: 1,
|
|
92
|
-
savedAt: Date.now(),
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const doc2: StoredDocument = {
|
|
96
|
-
state: { title: "Doc 2" },
|
|
97
|
-
version: 2,
|
|
98
|
-
schemaVersion: 1,
|
|
99
|
-
savedAt: Date.now(),
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const result = await Effect.runPromise(
|
|
103
|
-
Effect.gen(function* () {
|
|
104
|
-
const storage = yield* ColdStorageTag;
|
|
105
|
-
yield* storage.save("doc-1", doc1);
|
|
106
|
-
yield* storage.save("doc-2", doc2);
|
|
107
|
-
|
|
108
|
-
const loaded1 = yield* storage.load("doc-1");
|
|
109
|
-
const loaded2 = yield* storage.load("doc-2");
|
|
110
|
-
|
|
111
|
-
return { loaded1, loaded2 };
|
|
112
|
-
}).pipe(Effect.provide(layer))
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
expect(result.loaded1).toEqual(doc1);
|
|
116
|
-
expect(result.loaded2).toEqual(doc2);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("should not error when deleting non-existent document", async () => {
|
|
120
|
-
await expect(
|
|
121
|
-
Effect.runPromise(
|
|
122
|
-
Effect.gen(function* () {
|
|
123
|
-
const storage = yield* ColdStorageTag;
|
|
124
|
-
yield* storage.delete("non-existent");
|
|
125
|
-
}).pipe(Effect.provide(layer))
|
|
126
|
-
)
|
|
127
|
-
).resolves.toBeUndefined();
|
|
128
|
-
});
|
|
16
|
+
}
|
|
129
17
|
});
|
|
130
18
|
|
|
131
19
|
describe("Tag", () => {
|