@uploadista/core 0.0.13-beta.5 → 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/README.md +1 -1
- package/dist/{checksum-P9C2JlRk.mjs → checksum-CtOagryS.mjs} +2 -2
- package/dist/{checksum-P9C2JlRk.mjs.map → checksum-CtOagryS.mjs.map} +1 -1
- package/dist/errors/index.d.cts +2 -2
- package/dist/errors/index.d.mts +2 -2
- package/dist/errors/index.mjs +1 -1
- package/dist/flow/index.cjs +1 -1
- package/dist/flow/index.d.cts +5 -5
- package/dist/flow/index.d.mts +5 -5
- package/dist/flow/index.mjs +1 -1
- package/dist/{flow-DkTE3siV.cjs → flow-ChADffZ5.cjs} +1 -1
- package/dist/{flow-IgE8hj7H.mjs → flow-_J9-Dm_m.mjs} +2 -2
- package/dist/flow-_J9-Dm_m.mjs.map +1 -0
- package/dist/{index-CrZopnP9.d.cts → index-4VDJDcWM.d.cts} +227 -241
- package/dist/index-4VDJDcWM.d.cts.map +1 -0
- package/dist/{index-BPBI84iT.d.mts → index-Bi9YYid8.d.mts} +2 -2
- package/dist/{index-BPBI84iT.d.mts.map → index-Bi9YYid8.d.mts.map} +1 -1
- package/dist/{index-BteFEg-c.d.mts → index-Cbf1OPLp.d.mts} +2 -2
- package/dist/{index-BteFEg-c.d.mts.map → index-Cbf1OPLp.d.mts.map} +1 -1
- package/dist/{index-DMfADSSJ.d.cts → index-De4wQJwR.d.cts} +2 -2
- package/dist/{index-DMfADSSJ.d.cts.map → index-De4wQJwR.d.cts.map} +1 -1
- package/dist/{index-DHt7Ht_J.d.mts → index-RgOX4psL.d.mts} +305 -139
- package/dist/index-RgOX4psL.d.mts.map +1 -0
- package/dist/{index-DubOIur4.d.cts → index-qZ90PVNl.d.cts} +2 -2
- package/dist/index-qZ90PVNl.d.cts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +1 -1
- package/dist/{stream-limiter-DFtRZczp.mjs → stream-limiter-D9KSAaoY.mjs} +2 -2
- package/dist/{stream-limiter-DFtRZczp.mjs.map → stream-limiter-D9KSAaoY.mjs.map} +1 -1
- package/dist/streams/index.d.cts +2 -2
- package/dist/streams/index.d.mts +2 -2
- package/dist/streams/index.mjs +1 -1
- package/dist/testing/index.cjs +1 -0
- package/dist/testing/index.d.cts +110 -0
- package/dist/testing/index.d.cts.map +1 -0
- package/dist/testing/index.d.mts +110 -0
- package/dist/testing/index.d.mts.map +1 -0
- package/dist/testing/index.mjs +2 -0
- package/dist/testing/index.mjs.map +1 -0
- package/dist/types/index.d.cts +5 -5
- package/dist/types/index.d.mts +5 -5
- package/dist/types/index.mjs +1 -1
- package/dist/{types-DGZ892my.mjs → types-BI_KmpTc.mjs} +2 -2
- package/dist/types-BI_KmpTc.mjs.map +1 -0
- package/dist/upload/index.d.cts +5 -5
- package/dist/upload/index.d.mts +5 -5
- package/dist/upload/index.mjs +1 -1
- package/dist/{upload-DJTptYqV.mjs → upload-Yj5lrtZo.mjs} +2 -2
- package/dist/{upload-DJTptYqV.mjs.map → upload-Yj5lrtZo.mjs.map} +1 -1
- package/dist/{uploadista-error-9yLWP7TC.d.cts → uploadista-error-BQLhNZcY.d.cts} +1 -1
- package/dist/{uploadista-error-9yLWP7TC.d.cts.map → uploadista-error-BQLhNZcY.d.cts.map} +1 -1
- package/dist/{uploadista-error-nZ_q-EZy.mjs → uploadista-error-Buscq-FR.mjs} +1 -1
- package/dist/{uploadista-error-nZ_q-EZy.mjs.map → uploadista-error-Buscq-FR.mjs.map} +1 -1
- package/dist/{uploadista-error-CBkvsyZ3.d.mts → uploadista-error-DUWw6OqS.d.mts} +1 -1
- package/dist/{uploadista-error-CBkvsyZ3.d.mts.map → uploadista-error-DUWw6OqS.d.mts.map} +1 -1
- package/dist/utils/index.d.cts +2 -2
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.mjs +1 -1
- package/dist/{utils-BicUw_lt.mjs → utils-BWiu6lqv.mjs} +2 -2
- package/dist/{utils-BicUw_lt.mjs.map → utils-BWiu6lqv.mjs.map} +1 -1
- package/package.json +14 -6
- package/src/flow/node.ts +4 -4
- package/src/flow/nodes/transform-node.ts +23 -2
- package/src/flow/plugins/credential-provider.ts +1 -1
- package/src/flow/plugins/image-ai-plugin.ts +1 -1
- package/src/flow/plugins/image-plugin.ts +1 -1
- package/src/flow/plugins/video-plugin.ts +1 -1
- package/src/flow/plugins/zip-plugin.ts +1 -1
- package/src/flow/types/type-utils.ts +14 -3
- package/src/testing/index.ts +14 -0
- package/src/testing/mock-image-ai-plugin.ts +33 -0
- package/src/testing/mock-image-plugin.ts +56 -0
- package/src/testing/mock-upload-server.ts +176 -0
- package/src/testing/mock-video-plugin.ts +94 -0
- package/src/testing/mock-zip-plugin.ts +41 -0
- package/src/types/data-store.ts +1 -1
- package/{src/errors/__tests__ → tests/errors}/uploadista-error.test.ts +23 -19
- package/{src → tests}/flow/edge.test.ts +1 -1
- package/tests/flow/flow.test.ts +853 -0
- package/tests/flow/node.test.ts +757 -0
- package/{src → tests}/streams/stream-limiter.test.ts +2 -2
- package/tests/types/typed-event-emitter.test.ts +282 -0
- package/{src → tests}/utils/debounce.test.ts +1 -1
- package/{src → tests}/utils/once.test.ts +1 -1
- package/tests/utils/test-layers.ts +183 -0
- package/{src → tests}/utils/throttle.test.ts +1 -1
- package/tsdown.config.ts +1 -0
- package/type-tests/flow.test-d.ts +93 -0
- package/type-tests/type-utils.test-d.ts +104 -51
- package/vitest.config.ts +19 -1
- package/dist/flow-IgE8hj7H.mjs.map +0 -1
- package/dist/index-CrZopnP9.d.cts.map +0 -1
- package/dist/index-DHt7Ht_J.d.mts.map +0 -1
- package/dist/index-DubOIur4.d.cts.map +0 -1
- package/dist/types-DGZ892my.mjs.map +0 -1
- /package/dist/{errors-C0zLx77t.mjs → errors-DEFjN-xn.mjs} +0 -0
- /package/dist/{index-BtBZHVmz.d.cts → index-C-svZlpj.d.mts} +0 -0
- /package/dist/{index-DEHBdV_z.d.mts → index-_wQ5ClJU.d.cts} +0 -0
- /package/dist/{streams-CJKKIAwy.mjs → streams-DPU17bYp.mjs} +0 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
import type { InputFile, UploadFile, WebSocketConnection } from "../types";
|
|
3
|
+
import { UploadServer } from "../upload";
|
|
4
|
+
import type { DataStoreCapabilities } from "../types/data-store";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Mock UploadServer implementation for testing.
|
|
8
|
+
*
|
|
9
|
+
* Provides a complete in-memory implementation of all UploadServer methods
|
|
10
|
+
* suitable for unit and integration tests.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { TestUploadServer } from "@uploadista/core/testing";
|
|
15
|
+
*
|
|
16
|
+
* const program = Effect.gen(function* () {
|
|
17
|
+
* const server = yield* UploadServer;
|
|
18
|
+
* const upload = yield* server.createUpload(inputFile, "client-123");
|
|
19
|
+
* return upload;
|
|
20
|
+
* }).pipe(Effect.provide(TestUploadServer));
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export const TestUploadServer = Layer.succeed(
|
|
24
|
+
UploadServer,
|
|
25
|
+
UploadServer.of({
|
|
26
|
+
read: (fileId: string, _clientId: string | null) =>
|
|
27
|
+
Effect.sync(() => {
|
|
28
|
+
// Generate mock file data based on fileId
|
|
29
|
+
const text = `Content of file ${fileId}`;
|
|
30
|
+
return new TextEncoder().encode(text);
|
|
31
|
+
}),
|
|
32
|
+
upload: (file, _clientId, stream) =>
|
|
33
|
+
Effect.gen(function* () {
|
|
34
|
+
// Read stream to completion
|
|
35
|
+
const reader = stream.getReader();
|
|
36
|
+
let totalSize = 0;
|
|
37
|
+
const chunks: Uint8Array[] = [];
|
|
38
|
+
|
|
39
|
+
while (true) {
|
|
40
|
+
const { done, value } = yield* Effect.promise(() => reader.read());
|
|
41
|
+
if (done) break;
|
|
42
|
+
if (value) {
|
|
43
|
+
chunks.push(value);
|
|
44
|
+
totalSize += value.byteLength;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Parse existing metadata
|
|
49
|
+
const existingMetadata =
|
|
50
|
+
typeof file.metadata === "string"
|
|
51
|
+
? JSON.parse(file.metadata)
|
|
52
|
+
: file.metadata || {};
|
|
53
|
+
|
|
54
|
+
// Extract extension from fileName
|
|
55
|
+
const extension = file.fileName
|
|
56
|
+
? file.fileName.split(".").pop()
|
|
57
|
+
: existingMetadata.extension;
|
|
58
|
+
|
|
59
|
+
// Create new UploadFile with merged metadata
|
|
60
|
+
return {
|
|
61
|
+
id: `uploaded-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
|
62
|
+
offset: totalSize,
|
|
63
|
+
size: totalSize,
|
|
64
|
+
storage: {
|
|
65
|
+
id: file.storageId,
|
|
66
|
+
type: "memory",
|
|
67
|
+
},
|
|
68
|
+
metadata: {
|
|
69
|
+
...existingMetadata,
|
|
70
|
+
// Update with InputFile type and fileName
|
|
71
|
+
mimeType: file.type,
|
|
72
|
+
type: file.type,
|
|
73
|
+
"content-type": file.type,
|
|
74
|
+
fileName: file.fileName,
|
|
75
|
+
originalName: file.fileName,
|
|
76
|
+
name: file.fileName,
|
|
77
|
+
extension,
|
|
78
|
+
},
|
|
79
|
+
creationDate: new Date().toISOString(),
|
|
80
|
+
} satisfies UploadFile;
|
|
81
|
+
}),
|
|
82
|
+
delete: (_fileId: string, _clientId: string | null) => Effect.void,
|
|
83
|
+
createUpload: (file: InputFile, _clientId: string | null) =>
|
|
84
|
+
Effect.succeed({
|
|
85
|
+
id: `uploaded-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
|
86
|
+
offset: 0,
|
|
87
|
+
size: 0,
|
|
88
|
+
storage: { id: file.storageId, type: "memory" },
|
|
89
|
+
metadata:
|
|
90
|
+
typeof file.metadata === "string"
|
|
91
|
+
? JSON.parse(file.metadata)
|
|
92
|
+
: file.metadata,
|
|
93
|
+
} satisfies UploadFile),
|
|
94
|
+
uploadChunk: (
|
|
95
|
+
uploadId: string,
|
|
96
|
+
_clientId: string | null,
|
|
97
|
+
chunk: ReadableStream,
|
|
98
|
+
) =>
|
|
99
|
+
Effect.gen(function* () {
|
|
100
|
+
// Read stream to completion
|
|
101
|
+
const reader = chunk.getReader();
|
|
102
|
+
let totalSize = 0;
|
|
103
|
+
const chunks: Uint8Array[] = [];
|
|
104
|
+
|
|
105
|
+
while (true) {
|
|
106
|
+
const { done, value } = yield* Effect.promise(() => reader.read());
|
|
107
|
+
if (done) break;
|
|
108
|
+
if (value) {
|
|
109
|
+
chunks.push(value);
|
|
110
|
+
totalSize += value.byteLength;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
id: uploadId,
|
|
115
|
+
offset: totalSize,
|
|
116
|
+
size: totalSize,
|
|
117
|
+
storage: { id: "test-storage", type: "memory" },
|
|
118
|
+
metadata: { mimeType: "application/octet-stream", extension: "bin" },
|
|
119
|
+
creationDate: new Date().toISOString(),
|
|
120
|
+
} satisfies UploadFile;
|
|
121
|
+
}),
|
|
122
|
+
getCapabilities: (_storageId: string, _clientId: string | null) =>
|
|
123
|
+
Effect.succeed({
|
|
124
|
+
supportsParallelUploads: true,
|
|
125
|
+
supportsConcatenation: true,
|
|
126
|
+
supportsDeferredLength: true,
|
|
127
|
+
supportsResumableUploads: true,
|
|
128
|
+
supportsTransactionalUploads: false,
|
|
129
|
+
maxConcurrentUploads: 10,
|
|
130
|
+
minChunkSize: 5 * 1024 * 1024, // 5MB
|
|
131
|
+
maxChunkSize: 100 * 1024 * 1024, // 100MB
|
|
132
|
+
maxParts: 10000,
|
|
133
|
+
optimalChunkSize: 10 * 1024 * 1024, // 10MB
|
|
134
|
+
requiresOrderedChunks: false,
|
|
135
|
+
requiresMimeTypeValidation: false,
|
|
136
|
+
} satisfies DataStoreCapabilities),
|
|
137
|
+
uploadFromUrl: (
|
|
138
|
+
inputFile: InputFile,
|
|
139
|
+
_clientId: string | null,
|
|
140
|
+
url: string,
|
|
141
|
+
) =>
|
|
142
|
+
Effect.succeed({
|
|
143
|
+
id: `uploaded-from-url-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
|
144
|
+
offset: 0,
|
|
145
|
+
size: 0,
|
|
146
|
+
storage: { id: inputFile.storageId, type: "memory" },
|
|
147
|
+
metadata:
|
|
148
|
+
typeof inputFile.metadata === "string"
|
|
149
|
+
? JSON.parse(inputFile.metadata)
|
|
150
|
+
: inputFile.metadata,
|
|
151
|
+
url,
|
|
152
|
+
creationDate: new Date().toISOString(),
|
|
153
|
+
} satisfies UploadFile),
|
|
154
|
+
getUpload: (uploadId: string) =>
|
|
155
|
+
Effect.succeed({
|
|
156
|
+
id: uploadId,
|
|
157
|
+
offset: 0,
|
|
158
|
+
size: 1024,
|
|
159
|
+
storage: {
|
|
160
|
+
id: "test-storage",
|
|
161
|
+
type: "memory",
|
|
162
|
+
},
|
|
163
|
+
metadata: {
|
|
164
|
+
mimeType: "text/plain",
|
|
165
|
+
originalName: `file-${uploadId}.txt`,
|
|
166
|
+
extension: "txt",
|
|
167
|
+
},
|
|
168
|
+
creationDate: new Date().toISOString(),
|
|
169
|
+
} satisfies UploadFile),
|
|
170
|
+
subscribeToUploadEvents: (
|
|
171
|
+
_uploadId: string,
|
|
172
|
+
_connection: WebSocketConnection,
|
|
173
|
+
) => Effect.void,
|
|
174
|
+
unsubscribeFromUploadEvents: (_uploadId: string) => Effect.void,
|
|
175
|
+
}),
|
|
176
|
+
);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
import { VideoPlugin } from "../flow";
|
|
3
|
+
import type { DescribeVideoMetadata } from "../flow/plugins/types/describe-video-node";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Mock VideoPlugin implementation for testing.
|
|
7
|
+
*
|
|
8
|
+
* Provides simple mock implementations of video processing operations
|
|
9
|
+
* that return mock Uint8Array data without requiring FFmpeg or node-av.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { TestVideoPlugin } from "@uploadista/core/testing";
|
|
14
|
+
*
|
|
15
|
+
* const program = Effect.gen(function* () {
|
|
16
|
+
* const plugin = yield* VideoPlugin;
|
|
17
|
+
* const transcoded = yield* plugin.transcode(videoBytes, { format: "webm", codec: "vp9" });
|
|
18
|
+
* return transcoded;
|
|
19
|
+
* }).pipe(Effect.provide(TestVideoPlugin));
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export const TestVideoPlugin = Layer.succeed(
|
|
23
|
+
VideoPlugin,
|
|
24
|
+
VideoPlugin.of({
|
|
25
|
+
describe: (input: Uint8Array) =>
|
|
26
|
+
Effect.sync(() => {
|
|
27
|
+
// Mock describe: return fake metadata based on input size
|
|
28
|
+
const metadata: DescribeVideoMetadata = {
|
|
29
|
+
duration: 120, // 2 minutes
|
|
30
|
+
width: 1920,
|
|
31
|
+
height: 1080,
|
|
32
|
+
codec: "h264",
|
|
33
|
+
format: "mp4",
|
|
34
|
+
bitrate: 5000000, // 5 Mbps
|
|
35
|
+
frameRate: 30,
|
|
36
|
+
aspectRatio: "16:9",
|
|
37
|
+
hasAudio: true,
|
|
38
|
+
audioCodec: "aac",
|
|
39
|
+
audioBitrate: 128000, // 128 kbps
|
|
40
|
+
size: input.byteLength,
|
|
41
|
+
};
|
|
42
|
+
return metadata;
|
|
43
|
+
}),
|
|
44
|
+
transcode: (_input: Uint8Array, options) =>
|
|
45
|
+
Effect.sync(() => {
|
|
46
|
+
// Mock transcode: return modified array
|
|
47
|
+
// Simulate different file sizes for different codecs
|
|
48
|
+
let sizeMultiplier = 1.0;
|
|
49
|
+
if (options.codec === "vp9") {
|
|
50
|
+
sizeMultiplier = 0.8; // VP9 is more efficient
|
|
51
|
+
} else if (options.codec === "h265") {
|
|
52
|
+
sizeMultiplier = 0.7; // H265 is even more efficient
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const newSize = Math.floor(_input.byteLength * sizeMultiplier);
|
|
56
|
+
return new Uint8Array(newSize).fill(42);
|
|
57
|
+
}),
|
|
58
|
+
resize: (input: Uint8Array, options) =>
|
|
59
|
+
Effect.sync(() => {
|
|
60
|
+
// Mock resize: return array with size based on dimensions
|
|
61
|
+
const width = options.width || 1280;
|
|
62
|
+
const height = options.height || 720;
|
|
63
|
+
// Simulate file size roughly proportional to resolution
|
|
64
|
+
const mockSize = Math.floor((width * height) / 50);
|
|
65
|
+
return new Uint8Array(mockSize).fill(84);
|
|
66
|
+
}),
|
|
67
|
+
trim: (_input: Uint8Array, options) =>
|
|
68
|
+
Effect.sync(() => {
|
|
69
|
+
// Mock trim: return smaller array based on duration
|
|
70
|
+
let duration: number;
|
|
71
|
+
if (options.duration !== undefined) {
|
|
72
|
+
duration = options.duration;
|
|
73
|
+
} else if (options.endTime !== undefined) {
|
|
74
|
+
duration = options.endTime - options.startTime;
|
|
75
|
+
} else {
|
|
76
|
+
// Assume 120s total duration
|
|
77
|
+
duration = 120 - options.startTime;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Simulate proportional file size based on duration
|
|
81
|
+
const ratio = duration / 120; // Assuming 120s original
|
|
82
|
+
const newSize = Math.floor(_input.byteLength * ratio);
|
|
83
|
+
return new Uint8Array(newSize).fill(63);
|
|
84
|
+
}),
|
|
85
|
+
extractFrame: (input: Uint8Array, options) =>
|
|
86
|
+
Effect.sync(() => {
|
|
87
|
+
// Mock extractFrame: return image bytes (smaller than video)
|
|
88
|
+
const format = options.format || "jpeg";
|
|
89
|
+
// JPEG typically smaller than PNG
|
|
90
|
+
const mockSize = format === "png" ? 50000 : 30000;
|
|
91
|
+
return new Uint8Array(mockSize).fill(255);
|
|
92
|
+
}),
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
import { ZipPlugin } from "../flow";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mock ZipPlugin implementation for testing.
|
|
6
|
+
*
|
|
7
|
+
* Provides a simple in-memory implementation that creates mock zip data
|
|
8
|
+
* by serializing file metadata as JSON.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { TestZipPlugin } from "@uploadista/core/testing";
|
|
13
|
+
*
|
|
14
|
+
* const program = Effect.gen(function* () {
|
|
15
|
+
* const zipPlugin = yield* ZipPlugin;
|
|
16
|
+
* const zipData = yield* zipPlugin.zip(inputs, options);
|
|
17
|
+
* return zipData;
|
|
18
|
+
* }).pipe(Effect.provide(TestZipPlugin));
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export const TestZipPlugin = Layer.succeed(
|
|
22
|
+
ZipPlugin,
|
|
23
|
+
ZipPlugin.of({
|
|
24
|
+
zip: (inputs, options) =>
|
|
25
|
+
Effect.gen(function* () {
|
|
26
|
+
// Create mock zip data
|
|
27
|
+
const files = inputs.map((input) => ({
|
|
28
|
+
id: input.id,
|
|
29
|
+
size: input.data.byteLength,
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
const zipContent = JSON.stringify({
|
|
33
|
+
zipName: options.zipName,
|
|
34
|
+
includeMetadata: options.includeMetadata,
|
|
35
|
+
files,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return new TextEncoder().encode(zipContent);
|
|
39
|
+
}),
|
|
40
|
+
}),
|
|
41
|
+
);
|
package/src/types/data-store.ts
CHANGED
|
@@ -158,7 +158,7 @@ export type DataStore<TData = unknown> = {
|
|
|
158
158
|
onProgress?: (chunkSize: number) => void;
|
|
159
159
|
},
|
|
160
160
|
) => Effect.Effect<number, UploadistaError>;
|
|
161
|
-
readonly deleteExpired?: Effect.Effect<number, UploadistaError>;
|
|
161
|
+
readonly deleteExpired?: () => Effect.Effect<number, UploadistaError>;
|
|
162
162
|
readonly getCapabilities: () => DataStoreCapabilities;
|
|
163
163
|
readonly validateUploadStrategy: (
|
|
164
164
|
strategy: UploadStrategy,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
1
|
+
import { Effect, Exit } from "effect";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
3
|
import {
|
|
4
4
|
ERROR_CATALOG,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
isUploadistaError,
|
|
7
7
|
UploadistaError,
|
|
8
8
|
type UploadistaErrorCode,
|
|
9
|
-
} from "
|
|
9
|
+
} from "../../src/errors";
|
|
10
10
|
|
|
11
11
|
describe("ERROR_CATALOG", () => {
|
|
12
12
|
it("should contain all error codes with status and body", () => {
|
|
@@ -76,7 +76,6 @@ describe("UploadistaError", () => {
|
|
|
76
76
|
expect(error.status).toBe(404);
|
|
77
77
|
expect(error.status_code).toBe(404); // legacy alias
|
|
78
78
|
expect(error.body).toBe("File not found");
|
|
79
|
-
expect(error.message).toBe("File not found");
|
|
80
79
|
expect(error.details).toEqual({ fileId: "123" });
|
|
81
80
|
expect((error as { cause?: unknown }).cause).toBeInstanceOf(Error);
|
|
82
81
|
});
|
|
@@ -173,12 +172,14 @@ describe("UploadistaError", () => {
|
|
|
173
172
|
});
|
|
174
173
|
|
|
175
174
|
describe("toFailure", () => {
|
|
176
|
-
it("should convert error to failure result", () => {
|
|
175
|
+
it("should convert error to failure result", async () => {
|
|
177
176
|
const error = UploadistaError.fromCode("FILE_NOT_FOUND");
|
|
178
|
-
const
|
|
177
|
+
const effect = error.toEffect();
|
|
179
178
|
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
const result = await Effect.runPromiseExit(effect);
|
|
180
|
+
|
|
181
|
+
expect(Exit.isFailure(result)).toBe(true);
|
|
182
|
+
expect(Exit.isSuccess(result)).toBe(false);
|
|
182
183
|
});
|
|
183
184
|
});
|
|
184
185
|
});
|
|
@@ -216,25 +217,27 @@ describe("isUploadistaError", () => {
|
|
|
216
217
|
});
|
|
217
218
|
|
|
218
219
|
describe("httpFailure", () => {
|
|
219
|
-
it("should create failure result from error code", () => {
|
|
220
|
-
const
|
|
220
|
+
it("should create failure result from error code", async () => {
|
|
221
|
+
const effect = httpFailure("FILE_NOT_FOUND");
|
|
222
|
+
const result = await Effect.runPromiseExit(effect);
|
|
221
223
|
|
|
222
|
-
expect(
|
|
223
|
-
expect(
|
|
224
|
+
expect(Exit.isFailure(result)).toBe(true);
|
|
225
|
+
expect(Exit.isSuccess(result)).toBe(false);
|
|
224
226
|
});
|
|
225
227
|
|
|
226
|
-
it("should allow overriding error properties", () => {
|
|
227
|
-
const
|
|
228
|
+
it("should allow overriding error properties", async () => {
|
|
229
|
+
const effect = httpFailure("UNKNOWN_ERROR", {
|
|
228
230
|
status: 503,
|
|
229
231
|
body: "Service unavailable",
|
|
230
232
|
details: { retryAfter: 60 },
|
|
231
233
|
});
|
|
234
|
+
const result = await Effect.runPromiseExit(effect);
|
|
232
235
|
|
|
233
|
-
expect(
|
|
234
|
-
expect(
|
|
236
|
+
expect(Exit.isFailure(result)).toBe(true);
|
|
237
|
+
expect(Exit.isSuccess(result)).toBe(false);
|
|
235
238
|
});
|
|
236
239
|
|
|
237
|
-
it("should work with all error codes", () => {
|
|
240
|
+
it("should work with all error codes", async () => {
|
|
238
241
|
const errorCodes: UploadistaErrorCode[] = [
|
|
239
242
|
"MISSING_OFFSET",
|
|
240
243
|
"FLOW_NODE_ERROR",
|
|
@@ -242,9 +245,10 @@ describe("httpFailure", () => {
|
|
|
242
245
|
];
|
|
243
246
|
|
|
244
247
|
for (const code of errorCodes) {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
expect(
|
|
248
|
+
const effect = httpFailure(code);
|
|
249
|
+
const result = await Effect.runPromiseExit(effect);
|
|
250
|
+
expect(Exit.isFailure(result)).toBe(true);
|
|
251
|
+
expect(Exit.isSuccess(result)).toBe(false);
|
|
248
252
|
}
|
|
249
253
|
});
|
|
250
254
|
});
|