@uploadista/server 0.0.13-beta.4 → 0.0.13
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/auth/index.d.cts +1 -1
- package/dist/auth/index.d.mts +1 -1
- package/dist/auth/index.mjs +1 -1
- package/dist/{auth-BqArZeGK.mjs → auth-D2lKhlzK.mjs} +1 -1
- package/dist/{auth-BqArZeGK.mjs.map → auth-D2lKhlzK.mjs.map} +1 -1
- package/dist/{index-50KlDIjc.d.cts → index-BXLtlr98.d.mts} +1 -1
- package/dist/{index-50KlDIjc.d.cts.map → index-BXLtlr98.d.mts.map} +1 -1
- package/dist/{index--Lny6VJP.d.mts → index-mMP18lsw.d.cts} +1 -1
- package/dist/{index--Lny6VJP.d.mts.map → index-mMP18lsw.d.cts.map} +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +13 -11
- package/src/core/plugin-types.ts +5 -3
- package/src/plugins-typing.ts +1 -0
- package/{src/__tests__ → tests}/backward-compatibility.test.ts +23 -23
- package/{src → tests}/cache.test.ts +2 -2
- package/tests/core/http-handlers/flow-handlers.test.ts +495 -0
- package/tests/core/http-handlers/upload-handlers.test.ts +657 -0
- package/{src/core/__tests__ → tests/core}/plugin-validation.test.ts +59 -26
- package/tests/core/websocket-handlers/websocket-handlers.test.ts +659 -0
- package/{src → tests}/service.test.ts +2 -2
- package/type-tests/plugin-types.test-d.ts +56 -25
- package/vitest.config.ts +39 -0
|
@@ -14,7 +14,7 @@ import { describe, expect, it } from "vitest";
|
|
|
14
14
|
describe("Deprecated plugins-typing module", () => {
|
|
15
15
|
it("should still be importable", async () => {
|
|
16
16
|
// Import from deprecated module - should not throw
|
|
17
|
-
const pluginsTyping = await import("../plugins-typing");
|
|
17
|
+
const pluginsTyping = await import("../src/plugins-typing");
|
|
18
18
|
|
|
19
19
|
// Module should exist
|
|
20
20
|
expect(pluginsTyping).toBeDefined();
|
|
@@ -40,21 +40,21 @@ describe("Deprecated plugins-typing module", () => {
|
|
|
40
40
|
|
|
41
41
|
describe("Deprecated createTypeSafeServer", () => {
|
|
42
42
|
it("should still be exported from core module", async () => {
|
|
43
|
-
const coreModule = await import("../core");
|
|
43
|
+
const coreModule = await import("../src/core");
|
|
44
44
|
|
|
45
45
|
expect(coreModule).toHaveProperty("createTypeSafeServer");
|
|
46
46
|
expect(typeof coreModule.createTypeSafeServer).toBe("function");
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
it("should be exported from create-type-safe-server module", async () => {
|
|
50
|
-
const module = await import("../core/create-type-safe-server");
|
|
50
|
+
const module = await import("../src/core/create-type-safe-server");
|
|
51
51
|
|
|
52
52
|
expect(module).toHaveProperty("createTypeSafeServer");
|
|
53
53
|
expect(typeof module.createTypeSafeServer).toBe("function");
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
it("should export helper functions", async () => {
|
|
57
|
-
const module = await import("../core/create-type-safe-server");
|
|
57
|
+
const module = await import("../src/core/create-type-safe-server");
|
|
58
58
|
|
|
59
59
|
expect(module).toHaveProperty("defineFlow");
|
|
60
60
|
expect(module).toHaveProperty("defineSimpleFlow");
|
|
@@ -69,7 +69,7 @@ describe("Deprecated createTypeSafeServer", () => {
|
|
|
69
69
|
|
|
70
70
|
describe("New module exports", () => {
|
|
71
71
|
it("should export runtime utilities from core module", async () => {
|
|
72
|
-
const coreModule = await import("../core");
|
|
72
|
+
const coreModule = await import("../src/core");
|
|
73
73
|
|
|
74
74
|
// Runtime validation (these are actual runtime functions)
|
|
75
75
|
expect(coreModule).toHaveProperty("validatePluginRequirements");
|
|
@@ -86,7 +86,7 @@ describe("New module exports", () => {
|
|
|
86
86
|
});
|
|
87
87
|
|
|
88
88
|
it("should export plugin validation utilities", async () => {
|
|
89
|
-
const validationModule = await import("../core/plugin-validation");
|
|
89
|
+
const validationModule = await import("../src/core/plugin-validation");
|
|
90
90
|
|
|
91
91
|
expect(validationModule).toHaveProperty("validatePluginRequirements");
|
|
92
92
|
expect(validationModule).toHaveProperty("formatPluginValidationError");
|
|
@@ -107,7 +107,7 @@ describe("New module exports", () => {
|
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
it("should export plugin types module", async () => {
|
|
110
|
-
const pluginTypesModule = await import("../core/plugin-types");
|
|
110
|
+
const pluginTypesModule = await import("../src/core/plugin-types");
|
|
111
111
|
|
|
112
112
|
// Module should exist and be importable
|
|
113
113
|
expect(pluginTypesModule).toBeDefined();
|
|
@@ -129,7 +129,7 @@ describe("New module exports", () => {
|
|
|
129
129
|
|
|
130
130
|
describe("Import path compatibility", () => {
|
|
131
131
|
it("should support importing from main core module", async () => {
|
|
132
|
-
const exports = await import("../core");
|
|
132
|
+
const exports = await import("../src/core");
|
|
133
133
|
|
|
134
134
|
// Old APIs
|
|
135
135
|
expect(exports.createTypeSafeServer).toBeDefined();
|
|
@@ -144,7 +144,7 @@ describe("Import path compatibility", () => {
|
|
|
144
144
|
});
|
|
145
145
|
|
|
146
146
|
it("should support importing deprecated module directly", async () => {
|
|
147
|
-
const exports = await import("../plugins-typing");
|
|
147
|
+
const exports = await import("../src/plugins-typing");
|
|
148
148
|
|
|
149
149
|
// Module should be importable
|
|
150
150
|
expect(exports).toBeDefined();
|
|
@@ -152,9 +152,9 @@ describe("Import path compatibility", () => {
|
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
it("should support importing new modules directly", async () => {
|
|
155
|
-
const pluginTypes = await import("../core/plugin-types");
|
|
156
|
-
const pluginValidation = await import("../core/plugin-validation");
|
|
157
|
-
const server = await import("../core/server");
|
|
155
|
+
const pluginTypes = await import("../src/core/plugin-types");
|
|
156
|
+
const pluginValidation = await import("../src/core/plugin-validation");
|
|
157
|
+
const server = await import("../src/core/server");
|
|
158
158
|
|
|
159
159
|
// Modules should be importable
|
|
160
160
|
expect(pluginTypes).toBeDefined();
|
|
@@ -169,7 +169,7 @@ describe("Import path compatibility", () => {
|
|
|
169
169
|
|
|
170
170
|
describe("Helper functions compatibility", () => {
|
|
171
171
|
it("should support defineFlow helper", async () => {
|
|
172
|
-
const { defineFlow } = await import("../core/create-type-safe-server");
|
|
172
|
+
const { defineFlow } = await import("../src/core/create-type-safe-server");
|
|
173
173
|
const { Effect } = await import("effect");
|
|
174
174
|
|
|
175
175
|
// Should be able to use defineFlow
|
|
@@ -180,7 +180,7 @@ describe("Helper functions compatibility", () => {
|
|
|
180
180
|
|
|
181
181
|
it("should support defineSimpleFlow helper", async () => {
|
|
182
182
|
const { defineSimpleFlow } = await import(
|
|
183
|
-
"../core/create-type-safe-server"
|
|
183
|
+
"../src/core/create-type-safe-server"
|
|
184
184
|
);
|
|
185
185
|
const { Effect } = await import("effect");
|
|
186
186
|
|
|
@@ -213,21 +213,21 @@ describe("Type availability", () => {
|
|
|
213
213
|
it("should have TypeSafeServerConfig type available", async () => {
|
|
214
214
|
// This is a compile-time check that the type exists
|
|
215
215
|
// At runtime, we just verify the module exports
|
|
216
|
-
const module = await import("../core/create-type-safe-server");
|
|
216
|
+
const module = await import("../src/core/create-type-safe-server");
|
|
217
217
|
|
|
218
218
|
expect(module).toBeDefined();
|
|
219
219
|
// Type would be checked at compile time
|
|
220
220
|
});
|
|
221
221
|
|
|
222
222
|
it("should have plugin validation types available", async () => {
|
|
223
|
-
const module = await import("../core/plugin-validation");
|
|
223
|
+
const module = await import("../src/core/plugin-validation");
|
|
224
224
|
|
|
225
225
|
expect(module).toBeDefined();
|
|
226
226
|
// Types like PluginValidationResult would be checked at compile time
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
it("should have plugin types available", async () => {
|
|
230
|
-
const module = await import("../core/plugin-types");
|
|
230
|
+
const module = await import("../src/core/plugin-types");
|
|
231
231
|
|
|
232
232
|
expect(module).toBeDefined();
|
|
233
233
|
// Types like ValidatePlugins, PluginServices would be checked at compile time
|
|
@@ -242,7 +242,7 @@ describe("Migration scenarios", () => {
|
|
|
242
242
|
it("should support old createTypeSafeServer imports", async () => {
|
|
243
243
|
// Old code pattern
|
|
244
244
|
const { createTypeSafeServer } = await import(
|
|
245
|
-
"../core/create-type-safe-server"
|
|
245
|
+
"../src/core/create-type-safe-server"
|
|
246
246
|
);
|
|
247
247
|
|
|
248
248
|
expect(createTypeSafeServer).toBeDefined();
|
|
@@ -251,7 +251,7 @@ describe("Migration scenarios", () => {
|
|
|
251
251
|
|
|
252
252
|
it("should support new createUploadistaServer imports", async () => {
|
|
253
253
|
// New code pattern
|
|
254
|
-
const { createUploadistaServer } = await import("../core/server");
|
|
254
|
+
const { createUploadistaServer } = await import("../src/core/server");
|
|
255
255
|
|
|
256
256
|
expect(createUploadistaServer).toBeDefined();
|
|
257
257
|
expect(typeof createUploadistaServer).toBe("function");
|
|
@@ -259,7 +259,7 @@ describe("Migration scenarios", () => {
|
|
|
259
259
|
|
|
260
260
|
it("should support old plugin typing imports", async () => {
|
|
261
261
|
// Old code pattern - module should be importable
|
|
262
|
-
const oldModule = await import("../plugins-typing");
|
|
262
|
+
const oldModule = await import("../src/plugins-typing");
|
|
263
263
|
|
|
264
264
|
expect(oldModule).toBeDefined();
|
|
265
265
|
// All exports are type-only (LayerSuccessUnion, FlowRequirementsOf, etc.)
|
|
@@ -267,7 +267,7 @@ describe("Migration scenarios", () => {
|
|
|
267
267
|
|
|
268
268
|
it("should support new plugin typing imports", async () => {
|
|
269
269
|
// New code pattern - module should be importable
|
|
270
|
-
const newModule = await import("../core/plugin-types");
|
|
270
|
+
const newModule = await import("../src/core/plugin-types");
|
|
271
271
|
|
|
272
272
|
expect(newModule).toBeDefined();
|
|
273
273
|
// All exports are type-only (ExtractFlowPluginRequirements, ValidatePlugins, etc.)
|
|
@@ -275,8 +275,8 @@ describe("Migration scenarios", () => {
|
|
|
275
275
|
|
|
276
276
|
it("should support importing both old and new modules", async () => {
|
|
277
277
|
// Both modules should be importable without conflicts
|
|
278
|
-
const oldModule = await import("../plugins-typing");
|
|
279
|
-
const newModule = await import("../core/plugin-types");
|
|
278
|
+
const oldModule = await import("../src/plugins-typing");
|
|
279
|
+
const newModule = await import("../src/core/plugin-types");
|
|
280
280
|
|
|
281
281
|
expect(oldModule).toBeDefined();
|
|
282
282
|
expect(newModule).toBeDefined();
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
AuthCacheService,
|
|
5
5
|
AuthCacheServiceLive,
|
|
6
6
|
NoAuthCacheServiceLive,
|
|
7
|
-
} from "
|
|
8
|
-
import type { AuthContext } from "
|
|
7
|
+
} from "../src/cache";
|
|
8
|
+
import type { AuthContext } from "../src/types";
|
|
9
9
|
|
|
10
10
|
describe("AuthCacheService", () => {
|
|
11
11
|
describe("AuthCacheServiceLive", () => {
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for HTTP Flow Handlers
|
|
3
|
+
*
|
|
4
|
+
* Covers:
|
|
5
|
+
* - Flow data retrieval
|
|
6
|
+
* - Flow execution and job management
|
|
7
|
+
* - Job status tracking
|
|
8
|
+
* - Flow control (pause, resume, cancel)
|
|
9
|
+
* - Auth context handling and caching
|
|
10
|
+
* - HTTP request/response handling
|
|
11
|
+
* - Error handling and status codes
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { it } from "@effect/vitest";
|
|
15
|
+
import type { FlowData, FlowJob } from "@uploadista/core";
|
|
16
|
+
import { FlowServer } from "@uploadista/core/flow";
|
|
17
|
+
import { Effect, Layer } from "effect";
|
|
18
|
+
import { describe, expect } from "vitest";
|
|
19
|
+
import { AuthCacheServiceLive, AuthContextServiceLive } from "../../../src";
|
|
20
|
+
import {
|
|
21
|
+
handleCancelFlow,
|
|
22
|
+
handleGetFlow,
|
|
23
|
+
handleJobStatus,
|
|
24
|
+
handlePauseFlow,
|
|
25
|
+
handleResumeFlow,
|
|
26
|
+
handleRunFlow,
|
|
27
|
+
} from "../../../src/core/http-handlers/flow-http-handlers";
|
|
28
|
+
|
|
29
|
+
// Mock FlowServer implementation for testing
|
|
30
|
+
const mockFlowServerMethods = {
|
|
31
|
+
getFlowData: (flowId: string, _clientId: string | null) =>
|
|
32
|
+
Effect.succeed<FlowData>({
|
|
33
|
+
id: flowId,
|
|
34
|
+
name: `Flow ${flowId}`,
|
|
35
|
+
nodes: [],
|
|
36
|
+
edges: [],
|
|
37
|
+
}),
|
|
38
|
+
|
|
39
|
+
runFlow: ({
|
|
40
|
+
flowId,
|
|
41
|
+
storageId,
|
|
42
|
+
clientId,
|
|
43
|
+
}: {
|
|
44
|
+
flowId: string;
|
|
45
|
+
storageId: string;
|
|
46
|
+
clientId: string | null;
|
|
47
|
+
inputs: Record<string, unknown>;
|
|
48
|
+
}) =>
|
|
49
|
+
Effect.succeed<FlowJob>({
|
|
50
|
+
id: `job-${flowId}-${Date.now()}`,
|
|
51
|
+
flowId,
|
|
52
|
+
storageId,
|
|
53
|
+
clientId,
|
|
54
|
+
status: "running",
|
|
55
|
+
tasks: [],
|
|
56
|
+
createdAt: new Date(),
|
|
57
|
+
updatedAt: new Date(),
|
|
58
|
+
}),
|
|
59
|
+
|
|
60
|
+
getJobStatus: (jobId: string) =>
|
|
61
|
+
Effect.succeed<FlowJob>({
|
|
62
|
+
id: jobId,
|
|
63
|
+
flowId: "flow-123",
|
|
64
|
+
status: "running",
|
|
65
|
+
clientId: null,
|
|
66
|
+
storageId: "storage-1",
|
|
67
|
+
tasks: [],
|
|
68
|
+
createdAt: new Date(),
|
|
69
|
+
updatedAt: new Date(),
|
|
70
|
+
}),
|
|
71
|
+
|
|
72
|
+
resumeFlow: ({
|
|
73
|
+
jobId,
|
|
74
|
+
clientId,
|
|
75
|
+
}: {
|
|
76
|
+
jobId: string;
|
|
77
|
+
nodeId: string;
|
|
78
|
+
newData: unknown;
|
|
79
|
+
clientId: string | null;
|
|
80
|
+
}) =>
|
|
81
|
+
Effect.succeed<FlowJob>({
|
|
82
|
+
id: jobId,
|
|
83
|
+
flowId: "flow-123",
|
|
84
|
+
status: "running",
|
|
85
|
+
clientId,
|
|
86
|
+
storageId: "storage-1",
|
|
87
|
+
tasks: [],
|
|
88
|
+
createdAt: new Date(),
|
|
89
|
+
updatedAt: new Date(),
|
|
90
|
+
}),
|
|
91
|
+
|
|
92
|
+
pauseFlow: (jobId: string, clientId: string | null) =>
|
|
93
|
+
Effect.succeed<FlowJob>({
|
|
94
|
+
id: jobId,
|
|
95
|
+
flowId: "flow-123",
|
|
96
|
+
status: "paused",
|
|
97
|
+
clientId,
|
|
98
|
+
storageId: "storage-1",
|
|
99
|
+
tasks: [],
|
|
100
|
+
createdAt: new Date(),
|
|
101
|
+
updatedAt: new Date(),
|
|
102
|
+
}),
|
|
103
|
+
|
|
104
|
+
cancelFlow: (jobId: string, clientId: string | null) =>
|
|
105
|
+
Effect.succeed<FlowJob>({
|
|
106
|
+
id: jobId,
|
|
107
|
+
flowId: "flow-123",
|
|
108
|
+
status: "cancelled",
|
|
109
|
+
clientId,
|
|
110
|
+
storageId: "storage-1",
|
|
111
|
+
tasks: [],
|
|
112
|
+
createdAt: new Date(),
|
|
113
|
+
updatedAt: new Date(),
|
|
114
|
+
}),
|
|
115
|
+
|
|
116
|
+
// Mock methods required by FlowServerShape but not used in tests
|
|
117
|
+
getFlow: () => Effect.die("getFlow not implemented in test"),
|
|
118
|
+
subscribeToFlowEvents: () => Effect.void,
|
|
119
|
+
unsubscribeFromFlowEvents: () => Effect.void,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const FlowServerTest = Layer.succeed(FlowServer, mockFlowServerMethods);
|
|
123
|
+
|
|
124
|
+
describe("HTTP Flow Handlers", () => {
|
|
125
|
+
describe("handleGetFlow", () => {
|
|
126
|
+
it.effect("should retrieve flow data without auth", () =>
|
|
127
|
+
Effect.gen(function* () {
|
|
128
|
+
const result = yield* handleGetFlow({
|
|
129
|
+
type: "get-flow",
|
|
130
|
+
flowId: "flow-123",
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
expect(result.status).toBe(200);
|
|
134
|
+
expect(result.body.id).toBe("flow-123");
|
|
135
|
+
expect(result.body.name).toBe("Flow flow-123");
|
|
136
|
+
}).pipe(
|
|
137
|
+
Effect.provide(FlowServerTest),
|
|
138
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
139
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
140
|
+
),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
it.effect("should retrieve flow data with auth context", () =>
|
|
144
|
+
Effect.gen(function* () {
|
|
145
|
+
const result = yield* handleGetFlow({
|
|
146
|
+
type: "get-flow",
|
|
147
|
+
flowId: "flow-456",
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
expect(result.status).toBe(200);
|
|
151
|
+
expect(result.body.id).toBe("flow-456");
|
|
152
|
+
}).pipe(
|
|
153
|
+
Effect.provide(FlowServerTest),
|
|
154
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
155
|
+
Effect.provide(
|
|
156
|
+
AuthContextServiceLive({
|
|
157
|
+
clientId: "user-123",
|
|
158
|
+
metadata: { role: "admin" },
|
|
159
|
+
}),
|
|
160
|
+
),
|
|
161
|
+
),
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
it.effect("should include flow metadata in response", () =>
|
|
165
|
+
Effect.gen(function* () {
|
|
166
|
+
const result = yield* handleGetFlow({
|
|
167
|
+
type: "get-flow",
|
|
168
|
+
flowId: "flow-789",
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
expect(result.status).toBe(200);
|
|
172
|
+
expect(result.body).toHaveProperty("id");
|
|
173
|
+
expect(result.body).toHaveProperty("name");
|
|
174
|
+
expect(result.body).toHaveProperty("nodes");
|
|
175
|
+
expect(result.body).toHaveProperty("edges");
|
|
176
|
+
expect(result.body.id).toBe("flow-789");
|
|
177
|
+
}).pipe(
|
|
178
|
+
Effect.provide(FlowServerTest),
|
|
179
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
180
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
181
|
+
),
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe("handleRunFlow", () => {
|
|
186
|
+
it.effect("should execute flow without auth", () =>
|
|
187
|
+
Effect.gen(function* () {
|
|
188
|
+
const result = yield* handleRunFlow<never>({
|
|
189
|
+
type: "run-flow",
|
|
190
|
+
flowId: "flow-123",
|
|
191
|
+
storageId: "storage-1",
|
|
192
|
+
inputs: { key: "value" },
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(result.status).toBe(200);
|
|
196
|
+
expect(result.body).toHaveProperty("id");
|
|
197
|
+
expect(result.body.flowId).toBe("flow-123");
|
|
198
|
+
expect(result.body.storageId).toBe("storage-1");
|
|
199
|
+
expect(result.body.status).toBe("running");
|
|
200
|
+
}).pipe(
|
|
201
|
+
Effect.provide(FlowServerTest),
|
|
202
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
203
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
204
|
+
),
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
it.effect("should execute flow with auth context", () =>
|
|
208
|
+
Effect.gen(function* () {
|
|
209
|
+
const result = yield* handleRunFlow<never>({
|
|
210
|
+
type: "run-flow",
|
|
211
|
+
flowId: "flow-456",
|
|
212
|
+
storageId: "storage-2",
|
|
213
|
+
inputs: { data: "test" },
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
expect(result.status).toBe(200);
|
|
217
|
+
expect(result.body.clientId).toBe("user-123");
|
|
218
|
+
// Auth context is cached internally by the handler
|
|
219
|
+
}).pipe(
|
|
220
|
+
Effect.provide(FlowServerTest),
|
|
221
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
222
|
+
Effect.provide(
|
|
223
|
+
AuthContextServiceLive({
|
|
224
|
+
clientId: "user-123",
|
|
225
|
+
metadata: { plan: "premium" },
|
|
226
|
+
}),
|
|
227
|
+
),
|
|
228
|
+
),
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
it.effect("should not cache auth context when no auth present", () =>
|
|
232
|
+
Effect.gen(function* () {
|
|
233
|
+
const result = yield* handleRunFlow<never>({
|
|
234
|
+
type: "run-flow",
|
|
235
|
+
flowId: "flow-789",
|
|
236
|
+
storageId: "storage-3",
|
|
237
|
+
inputs: {},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
expect(result.status).toBe(200);
|
|
241
|
+
expect(result.body.clientId).toBeNull();
|
|
242
|
+
}).pipe(
|
|
243
|
+
Effect.provide(FlowServerTest),
|
|
244
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
245
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
246
|
+
),
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
it.effect("should return job ID immediately", () =>
|
|
250
|
+
Effect.gen(function* () {
|
|
251
|
+
const result = yield* handleRunFlow<never>({
|
|
252
|
+
type: "run-flow",
|
|
253
|
+
flowId: "flow-async",
|
|
254
|
+
storageId: "storage-4",
|
|
255
|
+
inputs: { async: true },
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
expect(result.status).toBe(200);
|
|
259
|
+
expect(result.body.id).toContain("job-");
|
|
260
|
+
expect(result.body.status).toBe("running");
|
|
261
|
+
}).pipe(
|
|
262
|
+
Effect.provide(FlowServerTest),
|
|
263
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
264
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
265
|
+
),
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe("handleJobStatus", () => {
|
|
270
|
+
it.effect("should get job status", () =>
|
|
271
|
+
Effect.gen(function* () {
|
|
272
|
+
const result = yield* handleJobStatus({
|
|
273
|
+
type: "job-status",
|
|
274
|
+
jobId: "job-123",
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
expect(result.status).toBe(200);
|
|
278
|
+
expect(result.body.id).toBe("job-123");
|
|
279
|
+
expect(result.body.flowId).toBe("flow-123");
|
|
280
|
+
expect(result.body).toHaveProperty("status");
|
|
281
|
+
}).pipe(
|
|
282
|
+
Effect.provide(FlowServerTest),
|
|
283
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
284
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
285
|
+
),
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
it.effect("should clear cache when flow completes", () =>
|
|
289
|
+
Effect.gen(function* () {
|
|
290
|
+
// Create FlowServer that returns completed status
|
|
291
|
+
const completedMethods = {
|
|
292
|
+
...mockFlowServerMethods,
|
|
293
|
+
getJobStatus: (jobId: string) =>
|
|
294
|
+
Effect.succeed<FlowJob>({
|
|
295
|
+
id: jobId,
|
|
296
|
+
flowId: "flow-123",
|
|
297
|
+
status: "completed",
|
|
298
|
+
clientId: null,
|
|
299
|
+
storageId: "storage-1",
|
|
300
|
+
tasks: [],
|
|
301
|
+
createdAt: new Date(),
|
|
302
|
+
updatedAt: new Date(),
|
|
303
|
+
}),
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const result = yield* handleJobStatus({
|
|
307
|
+
type: "job-status",
|
|
308
|
+
jobId: "job-completed",
|
|
309
|
+
}).pipe(
|
|
310
|
+
Effect.provide(Layer.succeed(FlowServer, completedMethods)),
|
|
311
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
312
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
expect(result.status).toBe(200);
|
|
316
|
+
expect(result.body.status).toBe("completed");
|
|
317
|
+
}),
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
it.effect("should not clear cache for running jobs", () =>
|
|
321
|
+
Effect.gen(function* () {
|
|
322
|
+
const result = yield* handleJobStatus({
|
|
323
|
+
type: "job-status",
|
|
324
|
+
jobId: "job-running",
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
expect(result.status).toBe(200);
|
|
328
|
+
expect(result.body.status).toBe("running");
|
|
329
|
+
}).pipe(
|
|
330
|
+
Effect.provide(FlowServerTest),
|
|
331
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
332
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
333
|
+
),
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
describe("handleResumeFlow", () => {
|
|
338
|
+
it.effect("should resume flow with current auth", () =>
|
|
339
|
+
Effect.gen(function* () {
|
|
340
|
+
const result = yield* handleResumeFlow<never>({
|
|
341
|
+
type: "resume-flow",
|
|
342
|
+
jobId: "job-cached",
|
|
343
|
+
nodeId: "node-2",
|
|
344
|
+
newData: { selection: "yes" },
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
expect(result.status).toBe(200);
|
|
348
|
+
expect(result.body.id).toBe("job-cached");
|
|
349
|
+
}).pipe(
|
|
350
|
+
Effect.provide(FlowServerTest),
|
|
351
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
352
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
353
|
+
),
|
|
354
|
+
);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe("handlePauseFlow", () => {
|
|
358
|
+
it.effect("should pause flow", () =>
|
|
359
|
+
Effect.gen(function* () {
|
|
360
|
+
const result = yield* handlePauseFlow({
|
|
361
|
+
type: "pause-flow",
|
|
362
|
+
jobId: "job-pause",
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
expect(result.status).toBe(200);
|
|
366
|
+
expect(result.body.id).toBe("job-pause");
|
|
367
|
+
expect(result.body.status).toBe("paused");
|
|
368
|
+
}).pipe(
|
|
369
|
+
Effect.provide(FlowServerTest),
|
|
370
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
371
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
372
|
+
),
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
it.effect("should pause flow with auth context", () =>
|
|
376
|
+
Effect.gen(function* () {
|
|
377
|
+
const result = yield* handlePauseFlow({
|
|
378
|
+
type: "pause-flow",
|
|
379
|
+
jobId: "job-pause-auth",
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
expect(result.status).toBe(200);
|
|
383
|
+
expect(result.body.status).toBe("paused");
|
|
384
|
+
expect(result.body.clientId).toBe("user-pause");
|
|
385
|
+
}).pipe(
|
|
386
|
+
Effect.provide(FlowServerTest),
|
|
387
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
388
|
+
Effect.provide(AuthContextServiceLive({ clientId: "user-pause" })),
|
|
389
|
+
),
|
|
390
|
+
);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
describe("handleCancelFlow", () => {
|
|
394
|
+
it.effect("should cancel flow", () =>
|
|
395
|
+
Effect.gen(function* () {
|
|
396
|
+
const result = yield* handleCancelFlow({
|
|
397
|
+
type: "cancel-flow",
|
|
398
|
+
jobId: "job-cancel",
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
expect(result.status).toBe(200);
|
|
402
|
+
expect(result.body.id).toBe("job-cancel");
|
|
403
|
+
expect(result.body.status).toBe("cancelled");
|
|
404
|
+
}).pipe(
|
|
405
|
+
Effect.provide(FlowServerTest),
|
|
406
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
407
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
408
|
+
),
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
it.effect("should cancel flow with current auth", () =>
|
|
412
|
+
Effect.gen(function* () {
|
|
413
|
+
const result = yield* handleCancelFlow({
|
|
414
|
+
type: "cancel-flow",
|
|
415
|
+
jobId: "job-cancel-auth",
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
expect(result.status).toBe(200);
|
|
419
|
+
expect(result.body.status).toBe("cancelled");
|
|
420
|
+
expect(result.body.clientId).toBe("user-cancel-auth");
|
|
421
|
+
}).pipe(
|
|
422
|
+
Effect.provide(FlowServerTest),
|
|
423
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
424
|
+
Effect.provide(
|
|
425
|
+
AuthContextServiceLive({
|
|
426
|
+
clientId: "user-cancel-auth",
|
|
427
|
+
metadata: { reason: "manual" },
|
|
428
|
+
}),
|
|
429
|
+
),
|
|
430
|
+
),
|
|
431
|
+
);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
describe("HTTP Status Codes", () => {
|
|
435
|
+
it.effect("should return 200 OK for successful operations", () =>
|
|
436
|
+
Effect.gen(function* () {
|
|
437
|
+
const getFlowResult = yield* handleGetFlow({
|
|
438
|
+
type: "get-flow",
|
|
439
|
+
flowId: "flow-200",
|
|
440
|
+
});
|
|
441
|
+
expect(getFlowResult.status).toBe(200);
|
|
442
|
+
|
|
443
|
+
const runFlowResult = yield* handleRunFlow<never>({
|
|
444
|
+
type: "run-flow",
|
|
445
|
+
flowId: "flow-200",
|
|
446
|
+
storageId: "storage-1",
|
|
447
|
+
inputs: {},
|
|
448
|
+
});
|
|
449
|
+
expect(runFlowResult.status).toBe(200);
|
|
450
|
+
|
|
451
|
+
const statusResult = yield* handleJobStatus({
|
|
452
|
+
type: "job-status",
|
|
453
|
+
jobId: "job-200",
|
|
454
|
+
});
|
|
455
|
+
expect(statusResult.status).toBe(200);
|
|
456
|
+
}).pipe(
|
|
457
|
+
Effect.provide(FlowServerTest),
|
|
458
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
459
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
460
|
+
),
|
|
461
|
+
);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
describe("Auth Context Integration", () => {
|
|
465
|
+
it.effect("should handle unauthenticated flow operations", () =>
|
|
466
|
+
Effect.gen(function* () {
|
|
467
|
+
// All operations should work without auth
|
|
468
|
+
const getResult = yield* handleGetFlow({
|
|
469
|
+
type: "get-flow",
|
|
470
|
+
flowId: "flow-unauth",
|
|
471
|
+
});
|
|
472
|
+
expect(getResult.status).toBe(200);
|
|
473
|
+
|
|
474
|
+
const runResult = yield* handleRunFlow<never>({
|
|
475
|
+
type: "run-flow",
|
|
476
|
+
flowId: "flow-unauth",
|
|
477
|
+
storageId: "storage-1",
|
|
478
|
+
inputs: {},
|
|
479
|
+
});
|
|
480
|
+
expect(runResult.status).toBe(200);
|
|
481
|
+
expect(runResult.body.clientId).toBeNull();
|
|
482
|
+
|
|
483
|
+
const statusResult = yield* handleJobStatus({
|
|
484
|
+
type: "job-status",
|
|
485
|
+
jobId: "job-unauth",
|
|
486
|
+
});
|
|
487
|
+
expect(statusResult.status).toBe(200);
|
|
488
|
+
}).pipe(
|
|
489
|
+
Effect.provide(FlowServerTest),
|
|
490
|
+
Effect.provide(AuthCacheServiceLive()),
|
|
491
|
+
Effect.provide(AuthContextServiceLive(null)),
|
|
492
|
+
),
|
|
493
|
+
);
|
|
494
|
+
});
|
|
495
|
+
});
|