@uploadista/server 0.0.11 → 0.0.13-beta.1
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/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/backward-compatibility.test.ts +218 -218
- package/src/core/__tests__/plugin-validation.test.ts +404 -398
- package/src/core/http-handlers/flow-http-handlers.ts +36 -18
- package/src/core/plugin-types.ts +32 -32
- package/src/core/plugin-validation.ts +142 -144
- package/src/plugins-typing.ts +37 -34
- package/tsconfig.json +1 -1
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
import { Layer } from "effect";
|
|
8
8
|
import { describe, expect, it } from "vitest";
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
extractServiceIdentifiers,
|
|
11
|
+
formatPluginValidationError,
|
|
12
|
+
validatePluginRequirements,
|
|
13
|
+
validatePluginsOrThrow,
|
|
14
14
|
} from "../plugin-validation";
|
|
15
15
|
|
|
16
16
|
// ============================================================================
|
|
@@ -18,24 +18,24 @@ import {
|
|
|
18
18
|
// ============================================================================
|
|
19
19
|
|
|
20
20
|
class TestImagePlugin {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
readonly _tag = "ImagePlugin";
|
|
22
|
+
resize(data: Buffer, width: number): Buffer {
|
|
23
|
+
return data;
|
|
24
|
+
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
class TestZipPlugin {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
readonly _tag = "ZipPlugin";
|
|
29
|
+
compress(files: Buffer[]): Buffer {
|
|
30
|
+
return Buffer.from([]);
|
|
31
|
+
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
class TestVideoPlugin {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
readonly _tag = "VideoPlugin";
|
|
36
|
+
transcode(data: Buffer): Buffer {
|
|
37
|
+
return data;
|
|
38
|
+
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const imagePluginLayer = Layer.succeed(TestImagePlugin, new TestImagePlugin());
|
|
@@ -47,29 +47,29 @@ const videoPluginLayer = Layer.succeed(TestVideoPlugin, new TestVideoPlugin());
|
|
|
47
47
|
// ============================================================================
|
|
48
48
|
|
|
49
49
|
describe("extractServiceIdentifiers", () => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
it("should extract identifiers from plugin array", () => {
|
|
51
|
+
const plugins = [imagePluginLayer, zipPluginLayer];
|
|
52
|
+
const identifiers = extractServiceIdentifiers(plugins);
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
// Should return array of strings (exact values depend on Effect internals)
|
|
55
|
+
expect(Array.isArray(identifiers)).toBe(true);
|
|
56
|
+
expect(identifiers.length).toBeGreaterThanOrEqual(0);
|
|
57
|
+
});
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
it("should handle empty plugin array", () => {
|
|
60
|
+
const plugins: never[] = [];
|
|
61
|
+
const identifiers = extractServiceIdentifiers(plugins);
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
expect(Array.isArray(identifiers)).toBe(true);
|
|
64
|
+
expect(identifiers).toHaveLength(0);
|
|
65
|
+
});
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
it("should handle single plugin", () => {
|
|
68
|
+
const plugins = [imagePluginLayer];
|
|
69
|
+
const identifiers = extractServiceIdentifiers(plugins);
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
expect(Array.isArray(identifiers)).toBe(true);
|
|
72
|
+
});
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
// ============================================================================
|
|
@@ -77,97 +77,97 @@ describe("extractServiceIdentifiers", () => {
|
|
|
77
77
|
// ============================================================================
|
|
78
78
|
|
|
79
79
|
describe("validatePluginRequirements", () => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
80
|
+
it("should return success when no services are expected", () => {
|
|
81
|
+
const result = validatePluginRequirements({
|
|
82
|
+
plugins: [imagePluginLayer],
|
|
83
|
+
expectedServices: [],
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(result.success).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should return success when expected services is undefined", () => {
|
|
90
|
+
const result = validatePluginRequirements({
|
|
91
|
+
plugins: [imagePluginLayer],
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(result.success).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should detect missing services", () => {
|
|
98
|
+
const result = validatePluginRequirements({
|
|
99
|
+
plugins: [],
|
|
100
|
+
expectedServices: ["ImagePlugin", "ZipPlugin"],
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expect(result.success).toBe(false);
|
|
104
|
+
if (!result.success) {
|
|
105
|
+
expect(result.missing).toEqual(["ImagePlugin", "ZipPlugin"]);
|
|
106
|
+
expect(result.required).toEqual(["ImagePlugin", "ZipPlugin"]);
|
|
107
|
+
expect(result.provided).toEqual([]);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("should detect partially missing services", () => {
|
|
112
|
+
const result = validatePluginRequirements({
|
|
113
|
+
plugins: [imagePluginLayer],
|
|
114
|
+
expectedServices: ["ImagePlugin", "ZipPlugin"],
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Since we can't reliably extract service identifiers from Effect layers,
|
|
118
|
+
// this test verifies the validation logic structure
|
|
119
|
+
expect(result).toHaveProperty("success");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should generate suggestions for known plugins", () => {
|
|
123
|
+
const result = validatePluginRequirements({
|
|
124
|
+
plugins: [],
|
|
125
|
+
expectedServices: ["ImagePlugin"],
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(result.success).toBe(false);
|
|
129
|
+
if (!result.success) {
|
|
130
|
+
expect(result.suggestions.length).toBeGreaterThan(0);
|
|
131
|
+
const suggestion = result.suggestions[0];
|
|
132
|
+
expect(suggestion).toHaveProperty("name");
|
|
133
|
+
expect(suggestion).toHaveProperty("packageName");
|
|
134
|
+
expect(suggestion).toHaveProperty("importStatement");
|
|
135
|
+
expect(suggestion.name).toBe("ImagePlugin");
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should handle unknown plugin services", () => {
|
|
140
|
+
const result = validatePluginRequirements({
|
|
141
|
+
plugins: [],
|
|
142
|
+
expectedServices: ["UnknownPlugin"],
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
expect(result.success).toBe(false);
|
|
146
|
+
if (!result.success) {
|
|
147
|
+
expect(result.missing).toContain("UnknownPlugin");
|
|
148
|
+
// Should not generate suggestion for unknown plugins
|
|
149
|
+
const unknownSuggestion = result.suggestions.find(
|
|
150
|
+
(s) => s.name === "UnknownPlugin",
|
|
151
|
+
);
|
|
152
|
+
expect(unknownSuggestion).toBeUndefined();
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should handle multiple known plugins", () => {
|
|
157
|
+
const result = validatePluginRequirements({
|
|
158
|
+
plugins: [],
|
|
159
|
+
expectedServices: ["ImagePlugin", "ZipPlugin"],
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
expect(result.success).toBe(false);
|
|
163
|
+
if (!result.success) {
|
|
164
|
+
expect(result.suggestions.length).toBe(2);
|
|
165
|
+
expect(result.suggestions.map((s) => s.name)).toEqual([
|
|
166
|
+
"ImagePlugin",
|
|
167
|
+
"ZipPlugin",
|
|
168
|
+
]);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
171
|
});
|
|
172
172
|
|
|
173
173
|
// ============================================================================
|
|
@@ -175,88 +175,88 @@ describe("validatePluginRequirements", () => {
|
|
|
175
175
|
// ============================================================================
|
|
176
176
|
|
|
177
177
|
describe("formatPluginValidationError", () => {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
178
|
+
it("should format error message with suggestions", () => {
|
|
179
|
+
const result = validatePluginRequirements({
|
|
180
|
+
plugins: [],
|
|
181
|
+
expectedServices: ["ImagePlugin", "ZipPlugin"],
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
expect(result.success).toBe(false);
|
|
185
|
+
if (!result.success) {
|
|
186
|
+
const message = formatPluginValidationError(result);
|
|
187
|
+
|
|
188
|
+
expect(message).toContain("Server initialization failed");
|
|
189
|
+
expect(message).toContain("Missing required plugins");
|
|
190
|
+
expect(message).toContain("ImagePlugin");
|
|
191
|
+
expect(message).toContain("ZipPlugin");
|
|
192
|
+
expect(message).toContain("Required:");
|
|
193
|
+
expect(message).toContain("Missing:");
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("should include import statements in error message", () => {
|
|
198
|
+
const result = validatePluginRequirements({
|
|
199
|
+
plugins: [],
|
|
200
|
+
expectedServices: ["ImagePlugin"],
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
expect(result.success).toBe(false);
|
|
204
|
+
if (!result.success) {
|
|
205
|
+
const message = formatPluginValidationError(result);
|
|
206
|
+
|
|
207
|
+
expect(message).toContain("import");
|
|
208
|
+
expect(message).toContain("@uploadista/flow-images-sharp");
|
|
209
|
+
expect(message).toContain("sharpImagePlugin");
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("should include example server configuration", () => {
|
|
214
|
+
const result = validatePluginRequirements({
|
|
215
|
+
plugins: [],
|
|
216
|
+
expectedServices: ["ImagePlugin"],
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
expect(result.success).toBe(false);
|
|
220
|
+
if (!result.success) {
|
|
221
|
+
const message = formatPluginValidationError(result);
|
|
222
|
+
|
|
223
|
+
expect(message).toContain("createUploadistaServer");
|
|
224
|
+
expect(message).toContain("plugins:");
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("should handle missing suggestions gracefully", () => {
|
|
229
|
+
const result = validatePluginRequirements({
|
|
230
|
+
plugins: [],
|
|
231
|
+
expectedServices: ["UnknownPlugin"],
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
expect(result.success).toBe(false);
|
|
235
|
+
if (!result.success) {
|
|
236
|
+
const message = formatPluginValidationError(result);
|
|
237
|
+
|
|
238
|
+
expect(message).toContain("Server initialization failed");
|
|
239
|
+
expect(message).toContain("UnknownPlugin");
|
|
240
|
+
expect(message).toContain(
|
|
241
|
+
"Could not determine package names for missing plugins",
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("should show provided services when some are present", () => {
|
|
247
|
+
const result = validatePluginRequirements({
|
|
248
|
+
plugins: [imagePluginLayer],
|
|
249
|
+
expectedServices: ["ImagePlugin", "ZipPlugin"],
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
expect(result.success).toBe(false);
|
|
253
|
+
if (!result.success) {
|
|
254
|
+
const message = formatPluginValidationError(result);
|
|
255
|
+
|
|
256
|
+
expect(message).toContain("Provided:");
|
|
257
|
+
// The exact provided services depend on Effect's internal representation
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
260
|
});
|
|
261
261
|
|
|
262
262
|
// ============================================================================
|
|
@@ -264,48 +264,48 @@ describe("formatPluginValidationError", () => {
|
|
|
264
264
|
// ============================================================================
|
|
265
265
|
|
|
266
266
|
describe("validatePluginsOrThrow", () => {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
267
|
+
it("should not throw when validation passes", () => {
|
|
268
|
+
expect(() => {
|
|
269
|
+
validatePluginsOrThrow({
|
|
270
|
+
plugins: [imagePluginLayer],
|
|
271
|
+
expectedServices: [],
|
|
272
|
+
});
|
|
273
|
+
}).not.toThrow();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("should throw when validation fails", () => {
|
|
277
|
+
expect(() => {
|
|
278
|
+
validatePluginsOrThrow({
|
|
279
|
+
plugins: [],
|
|
280
|
+
expectedServices: ["ImagePlugin"],
|
|
281
|
+
});
|
|
282
|
+
}).toThrow("Server initialization failed");
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("should throw error with detailed message", () => {
|
|
286
|
+
expect(() => {
|
|
287
|
+
validatePluginsOrThrow({
|
|
288
|
+
plugins: [],
|
|
289
|
+
expectedServices: ["ImagePlugin", "ZipPlugin"],
|
|
290
|
+
});
|
|
291
|
+
}).toThrow(/Missing required plugins/);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("should include plugin names in error", () => {
|
|
295
|
+
try {
|
|
296
|
+
validatePluginsOrThrow({
|
|
297
|
+
plugins: [],
|
|
298
|
+
expectedServices: ["ImagePlugin", "ZipPlugin"],
|
|
299
|
+
});
|
|
300
|
+
expect.fail("Should have thrown");
|
|
301
|
+
} catch (error) {
|
|
302
|
+
expect(error).toBeInstanceOf(Error);
|
|
303
|
+
if (error instanceof Error) {
|
|
304
|
+
expect(error.message).toContain("ImagePlugin");
|
|
305
|
+
expect(error.message).toContain("ZipPlugin");
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
309
|
});
|
|
310
310
|
|
|
311
311
|
// ============================================================================
|
|
@@ -313,61 +313,61 @@ describe("validatePluginsOrThrow", () => {
|
|
|
313
313
|
// ============================================================================
|
|
314
314
|
|
|
315
315
|
describe("Plugin Validation Integration", () => {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
316
|
+
it("should handle realistic server configuration scenario", () => {
|
|
317
|
+
// Scenario: Server configured with image plugin, but flow needs both image and zip
|
|
318
|
+
const result = validatePluginRequirements({
|
|
319
|
+
plugins: [imagePluginLayer],
|
|
320
|
+
expectedServices: ["ImagePlugin", "ZipPlugin"],
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
if (!result.success) {
|
|
324
|
+
const message = formatPluginValidationError(result);
|
|
325
|
+
|
|
326
|
+
// Should provide actionable error message
|
|
327
|
+
expect(message).toContain("Missing required plugins");
|
|
328
|
+
expect(message).toContain("@uploadista/flow-utility-zipjs");
|
|
329
|
+
expect(message).toContain("zipPlugin");
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it("should handle empty configuration", () => {
|
|
334
|
+
const result = validatePluginRequirements({
|
|
335
|
+
plugins: [],
|
|
336
|
+
expectedServices: [],
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
expect(result.success).toBe(true);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("should handle over-provisioned plugins", () => {
|
|
343
|
+
// More plugins than required should still pass
|
|
344
|
+
const result = validatePluginRequirements({
|
|
345
|
+
plugins: [imagePluginLayer, zipPluginLayer, videoPluginLayer],
|
|
346
|
+
expectedServices: ["ImagePlugin"],
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// This would pass if we could extract identifiers reliably
|
|
350
|
+
// For now, just verify structure
|
|
351
|
+
expect(result).toHaveProperty("success");
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("should provide helpful error for complete plugin set missing", () => {
|
|
355
|
+
const result = validatePluginRequirements({
|
|
356
|
+
plugins: [],
|
|
357
|
+
expectedServices: ["ImagePlugin", "ZipPlugin", "VideoPlugin"],
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
expect(result.success).toBe(false);
|
|
361
|
+
if (!result.success) {
|
|
362
|
+
expect(result.missing).toHaveLength(3);
|
|
363
|
+
const message = formatPluginValidationError(result);
|
|
364
|
+
|
|
365
|
+
// Should list all missing plugins
|
|
366
|
+
expect(message).toContain("ImagePlugin");
|
|
367
|
+
expect(message).toContain("ZipPlugin");
|
|
368
|
+
expect(message).toContain("VideoPlugin");
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
371
|
});
|
|
372
372
|
|
|
373
373
|
// ============================================================================
|
|
@@ -375,98 +375,104 @@ describe("Plugin Validation Integration", () => {
|
|
|
375
375
|
// ============================================================================
|
|
376
376
|
|
|
377
377
|
describe("Known Plugins Mapping", () => {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
378
|
+
it("should generate suggestions for missing plugins", () => {
|
|
379
|
+
const result = validatePluginRequirements({
|
|
380
|
+
plugins: [],
|
|
381
|
+
expectedServices: ["ImagePlugin"],
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
expect(result.success).toBe(false);
|
|
385
|
+
if (!result.success) {
|
|
386
|
+
// Verify suggestions array exists
|
|
387
|
+
expect(result.suggestions).toBeDefined();
|
|
388
|
+
expect(Array.isArray(result.suggestions)).toBe(true);
|
|
389
|
+
|
|
390
|
+
// If there are suggestions, check the structure
|
|
391
|
+
if (result.suggestions.length > 0) {
|
|
392
|
+
const suggestion = result.suggestions[0];
|
|
393
|
+
expect(suggestion).toHaveProperty("name");
|
|
394
|
+
expect(suggestion).toHaveProperty("packageName");
|
|
395
|
+
expect(suggestion).toHaveProperty("importStatement");
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it("should provide correct package for ImagePlugin", () => {
|
|
401
|
+
const result = validatePluginRequirements({
|
|
402
|
+
plugins: [],
|
|
403
|
+
expectedServices: ["ImagePlugin"],
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
expect(result.success).toBe(false);
|
|
407
|
+
if (!result.success && result.suggestions.length > 0) {
|
|
408
|
+
const suggestion = result.suggestions[0];
|
|
409
|
+
expect(suggestion.name).toBe("ImagePlugin");
|
|
410
|
+
expect(suggestion.packageName).toBe("@uploadista/flow-images-sharp");
|
|
411
|
+
expect(suggestion.importStatement).toContain("sharpImagePlugin");
|
|
412
|
+
expect(suggestion.importStatement).toContain(
|
|
413
|
+
"@uploadista/flow-images-sharp",
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it("should provide correct package for ZipPlugin", () => {
|
|
419
|
+
const result = validatePluginRequirements({
|
|
420
|
+
plugins: [],
|
|
421
|
+
expectedServices: ["ZipPlugin"],
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
expect(result.success).toBe(false);
|
|
425
|
+
if (!result.success) {
|
|
426
|
+
expect(result.suggestions.length).toBeGreaterThan(0);
|
|
427
|
+
|
|
428
|
+
const suggestion = result.suggestions[0];
|
|
429
|
+
expect(suggestion).toBeDefined();
|
|
430
|
+
expect(suggestion.name).toBe("ZipPlugin");
|
|
431
|
+
expect(suggestion.packageName).toBe("@uploadista/flow-utility-zipjs");
|
|
432
|
+
expect(suggestion.importStatement).toContain("zipPlugin");
|
|
433
|
+
expect(suggestion.importStatement).toContain(
|
|
434
|
+
"@uploadista/flow-utility-zipjs",
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it("should provide correct package for ImageAiPlugin", () => {
|
|
440
|
+
const result = validatePluginRequirements({
|
|
441
|
+
plugins: [],
|
|
442
|
+
expectedServices: ["ImageAiPlugin"],
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
expect(result.success).toBe(false);
|
|
446
|
+
if (!result.success) {
|
|
447
|
+
expect(result.suggestions.length).toBeGreaterThan(0);
|
|
448
|
+
|
|
449
|
+
const suggestion = result.suggestions[0];
|
|
450
|
+
expect(suggestion).toBeDefined();
|
|
451
|
+
expect(suggestion.name).toBe("ImageAiPlugin");
|
|
452
|
+
expect(suggestion.packageName).toBe("@uploadista/flow-images-replicate");
|
|
453
|
+
expect(suggestion.importStatement).toContain("replicateImagePlugin");
|
|
454
|
+
expect(suggestion.importStatement).toContain(
|
|
455
|
+
"@uploadista/flow-images-replicate",
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it("should provide correct package for CredentialProvider", () => {
|
|
461
|
+
const result = validatePluginRequirements({
|
|
462
|
+
plugins: [],
|
|
463
|
+
expectedServices: ["CredentialProvider"],
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
expect(result.success).toBe(false);
|
|
467
|
+
if (!result.success) {
|
|
468
|
+
expect(result.suggestions.length).toBeGreaterThan(0);
|
|
469
|
+
|
|
470
|
+
const suggestion = result.suggestions[0];
|
|
471
|
+
expect(suggestion).toBeDefined();
|
|
472
|
+
expect(suggestion.name).toBe("CredentialProvider");
|
|
473
|
+
expect(suggestion.packageName).toBe("@uploadista/core");
|
|
474
|
+
expect(suggestion.importStatement).toContain("credentialProviderLayer");
|
|
475
|
+
expect(suggestion.importStatement).toContain("@uploadista/core");
|
|
476
|
+
}
|
|
477
|
+
});
|
|
472
478
|
});
|