1+ import { describe , it , expect , vi , beforeEach } from 'vitest' ;
2+ import { uploadAction } from '../../src/actions/upload' ;
3+ import { validateStorageClientConfig } from '../../src/environments' ;
4+ import { createStorageClient } from '../../src/clients/storage' ;
5+ import fs from 'fs' ;
6+
7+ // Mock dependencies
8+ vi . mock ( 'fs' , ( ) => ( {
9+ default : {
10+ readFileSync : vi . fn ( ) . mockReturnValue ( Buffer . from ( 'test file content' ) ) ,
11+ promises : {
12+ readFile : vi . fn ( ) . mockResolvedValue ( Buffer . from ( 'test file content' ) ) ,
13+ stat : vi . fn ( ) . mockResolvedValue ( {
14+ isFile : ( ) => true ,
15+ size : 1024
16+ } )
17+ }
18+ }
19+ } ) ) ;
20+
21+ vi . mock ( '@elizaos/core' , ( ) => ( {
22+ elizaLogger : {
23+ log : vi . fn ( ) ,
24+ error : vi . fn ( ) ,
25+ info : vi . fn ( ) ,
26+ success : vi . fn ( )
27+ }
28+ } ) ) ;
29+
30+ vi . mock ( '../../src/environments' , ( ) => ( {
31+ validateStorageClientConfig : vi . fn ( ) . mockResolvedValue ( {
32+ GATEWAY_URL : 'https://mock-gateway.link'
33+ } )
34+ } ) ) ;
35+
36+ vi . mock ( '../../src/clients/storage' , ( ) => {
37+ const mockPut = vi . fn ( ) . mockResolvedValue ( {
38+ car : { cid : 'mock-car-cid' } ,
39+ root : 'mock-root-cid'
40+ } ) ;
41+
42+ return {
43+ createStorageClient : vi . fn ( ) . mockResolvedValue ( {
44+ upload : vi . fn ( ) . mockReturnValue ( {
45+ put : mockPut
46+ } )
47+ } )
48+ } ;
49+ } ) ;
50+
51+ describe ( 'uploadAction' , ( ) => {
52+ let mockRuntime : any ;
53+ let mockCallback : any ;
54+
55+ beforeEach ( ( ) => {
56+ mockRuntime = {
57+ getParameter : vi . fn ( )
58+ } ;
59+ mockCallback = vi . fn ( ) ;
60+
61+ vi . clearAllMocks ( ) ;
62+ } ) ;
63+
64+ describe ( 'validate' , ( ) => {
65+ it ( 'should return true when validation passes' , async ( ) => {
66+ const mockMessage = { content : { text : 'Test message' } } ;
67+ const result = await uploadAction . validate ( mockRuntime , mockMessage as any ) ;
68+
69+ expect ( result ) . toBe ( true ) ;
70+ expect ( validateStorageClientConfig ) . toHaveBeenCalledWith ( mockRuntime ) ;
71+ } ) ;
72+ } ) ;
73+
74+ describe ( 'handler' , ( ) => {
75+ it ( 'should return false when no attachments are provided' , async ( ) => {
76+ const mockMessage = { content : { attachments : [ ] } } ;
77+
78+ const result = await uploadAction . handler (
79+ mockRuntime ,
80+ mockMessage as any ,
81+ { } as any ,
82+ { } ,
83+ mockCallback
84+ ) ;
85+
86+ expect ( result ) . toBe ( false ) ;
87+ expect ( mockCallback ) . toHaveBeenCalledWith ( {
88+ text : "Looks like you didn't attach any files. Please attach a file and try again." ,
89+ action : null
90+ } ) ;
91+ } ) ;
92+
93+ it ( 'should handle file uploads when attachments are provided' , async ( ) => {
94+ const mockAttachments = [
95+ { url : 'file:///path/to/file1.txt' , title : 'file1.txt' } ,
96+ { url : 'file:///path/to/file2.jpg' , title : 'file2.jpg' }
97+ ] ;
98+ const mockMessage = { content : { attachments : mockAttachments } } ;
99+
100+ const result = await uploadAction . handler (
101+ mockRuntime ,
102+ mockMessage as any ,
103+ { } as any ,
104+ { } ,
105+ mockCallback
106+ ) ;
107+
108+ expect ( mockCallback ) . toHaveBeenNthCalledWith ( 1 , {
109+ text : "Sure thing! Starting the engines, hold on tight. Uploading file(s) to Storacha..." ,
110+ action : null
111+ } ) ;
112+ expect ( validateStorageClientConfig ) . toHaveBeenCalled ( ) ;
113+ expect ( createStorageClient ) . toHaveBeenCalled ( ) ;
114+ expect ( fs . readFileSync ) . toHaveBeenCalled ( ) ;
115+ } ) ;
116+
117+ it ( 'should handle errors from reading non-file URLs' , async ( ) => {
118+ const mockAttachments = [
119+ { url : 'https://example.com/image.jpg' , title : 'web-image.jpg' }
120+ ] ;
121+ const mockMessage = { content : { attachments : mockAttachments } } ;
122+
123+ ( fs . readFileSync as any ) . mockImplementationOnce ( ( ) => {
124+ throw new Error ( 'default.readFileSync is not a function' ) ;
125+ } ) ;
126+
127+ const result = await uploadAction . handler (
128+ mockRuntime ,
129+ mockMessage as any ,
130+ { } as any ,
131+ { } ,
132+ mockCallback
133+ ) ;
134+
135+ expect ( mockCallback ) . toHaveBeenNthCalledWith ( 1 , {
136+ text : "Sure thing! Starting the engines, hold on tight. Uploading file(s) to Storacha..." ,
137+ action : null
138+ } ) ;
139+ expect ( mockCallback ) . toHaveBeenNthCalledWith ( 2 , {
140+ text : "I'm sorry, I couldn't upload the file(s) to Storacha. Please try again later." ,
141+ content : { error : 'default.readFileSync is not a function' }
142+ } ) ;
143+ } ) ;
144+
145+ it ( 'should handle errors during client initialization' , async ( ) => {
146+ const mockAttachments = [
147+ { url : 'file:///path/to/file.txt' , title : 'file.txt' }
148+ ] ;
149+ const mockMessage = { content : { attachments : mockAttachments } } ;
150+ const mockError = new Error ( 'Upload failed' ) ;
151+ ( createStorageClient as any ) . mockRejectedValueOnce ( mockError ) ;
152+
153+ const result = await uploadAction . handler (
154+ mockRuntime ,
155+ mockMessage as any ,
156+ { } as any ,
157+ { } ,
158+ mockCallback
159+ ) ;
160+
161+ expect ( result ) . toBe ( false ) ;
162+ expect ( mockCallback ) . toHaveBeenNthCalledWith ( 1 , {
163+ text : "Sure thing! Starting the engines, hold on tight. Uploading file(s) to Storacha..." ,
164+ action : null
165+ } ) ;
166+ expect ( mockCallback ) . toHaveBeenNthCalledWith ( 2 , {
167+ text : "I'm sorry, I couldn't upload the file(s) to Storacha. Please try again later." ,
168+ content : { error : 'Upload failed' }
169+ } ) ;
170+ } ) ;
171+ } ) ;
172+ } ) ;
0 commit comments