33import { Amplify } from 'aws-amplify' ;
44import { generateClient } from 'aws-amplify/api' ;
55import { getCurrentUser } from 'aws-amplify/auth' ;
6+ import { uploadData , getUrl , remove } from 'aws-amplify/storage' ;
7+ import { DynamoDBClient , ListTablesCommand } from '@aws-sdk/client-dynamodb' ;
8+ import { DynamoDBDocumentClient , PutCommand , GetCommand , DeleteCommand } from '@aws-sdk/lib-dynamodb' ;
69import { getTopic , listTopics , getPost , listPosts , getComment , listComments , fetchUserActivity } from './src/graphql/queries' ;
710import {
811 createTopic ,
@@ -319,6 +322,115 @@ export function createTestFunctions() {
319322 console . log ( '✅ Deleted comment:' , deleted . content ?. substring ( 0 , 30 ) + '...' ) ;
320323 }
321324
325+ // ============================================================
326+ // S3 Avatar Test Functions
327+ // ============================================================
328+
329+ async function testUploadAvatar ( ) : Promise < string | null > {
330+ console . log ( '\n📤 Testing uploadData (S3 avatar upload)...' ) ;
331+ // 1x1 transparent PNG
332+ const testImageBase64 =
333+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==' ;
334+ const imageBuffer = Buffer . from ( testImageBase64 , 'base64' ) ;
335+ const fileName = `test-avatar-${ Date . now ( ) } .png` ;
336+
337+ console . log ( ` Uploading to: ${ fileName } ` ) ;
338+ console . log ( ` File size: ${ imageBuffer . length } bytes` ) ;
339+
340+ const result = await uploadData ( {
341+ key : fileName ,
342+ data : imageBuffer ,
343+ options : { contentType : 'image/png' } ,
344+ } ) . result ;
345+
346+ console . log ( '✅ Upload successful!' ) ;
347+ console . log ( ' Key:' , result . key ) ;
348+ return result . key ;
349+ }
350+
351+ async function testGetAvatarUrl ( avatarKey : string ) : Promise < string | null > {
352+ console . log ( `\n🔗 Testing getUrl (S3 signed URL)...` ) ;
353+ console . log ( ` Avatar key: ${ avatarKey } ` ) ;
354+
355+ const result = await getUrl ( {
356+ key : avatarKey ,
357+ options : { expiresIn : 3600 } ,
358+ } ) ;
359+ console . log ( '✅ Got signed URL!' ) ;
360+ console . log ( ' URL:' , result . url . toString ( ) . substring ( 0 , 80 ) + '...' ) ;
361+ return result . url . toString ( ) ;
362+ }
363+
364+ async function testRemoveAvatar ( avatarKey : string ) : Promise < void > {
365+ console . log ( `\n🗑️ Testing remove (S3 avatar delete)...` ) ;
366+ console . log ( ` Avatar key: ${ avatarKey } ` ) ;
367+
368+ await remove ( { key : avatarKey } ) ;
369+ console . log ( '✅ Avatar removed successfully!' ) ;
370+ }
371+
372+ // ============================================================
373+ // Bookmarks DDB Test Functions
374+ // ============================================================
375+
376+ async function testCreateBookmark ( tableName : string , userId : string , postId : string ) : Promise < void > {
377+ console . log ( '\n🔖 Testing PutItem (create bookmark)...' ) ;
378+ console . log ( ` Table: ${ tableName } ` ) ;
379+ console . log ( ` userId: ${ userId . substring ( 0 , 8 ) } ..., postId: ${ postId . substring ( 0 , 8 ) } ...` ) ;
380+
381+ const ddbClient = DynamoDBDocumentClient . from (
382+ new DynamoDBClient ( { region : ( amplifyconfig as any ) . aws_project_region } ) ,
383+ ) ;
384+ await ddbClient . send (
385+ new PutCommand ( {
386+ TableName : tableName ,
387+ Item : { userId, postId, createdAt : new Date ( ) . toISOString ( ) } ,
388+ } ) ,
389+ ) ;
390+ console . log ( '✅ Bookmark created!' ) ;
391+ }
392+
393+ async function testGetBookmark ( tableName : string , userId : string , postId : string ) : Promise < void > {
394+ console . log ( '\n🔖 Testing GetItem (read bookmark)...' ) ;
395+ console . log ( ` Table: ${ tableName } ` ) ;
396+ console . log ( ` userId: ${ userId . substring ( 0 , 8 ) } ..., postId: ${ postId . substring ( 0 , 8 ) } ...` ) ;
397+
398+ const ddbClient = DynamoDBDocumentClient . from (
399+ new DynamoDBClient ( { region : ( amplifyconfig as any ) . aws_project_region } ) ,
400+ ) ;
401+ const result = await ddbClient . send (
402+ new GetCommand ( {
403+ TableName : tableName ,
404+ Key : { userId, postId } ,
405+ } ) ,
406+ ) ;
407+ if ( ! result . Item ) {
408+ throw new Error ( 'Bookmark not found' ) ;
409+ }
410+ console . log ( '✅ Bookmark found:' , {
411+ userId : result . Item . userId . substring ( 0 , 8 ) + '...' ,
412+ postId : result . Item . postId . substring ( 0 , 8 ) + '...' ,
413+ createdAt : result . Item . createdAt ,
414+ } ) ;
415+ }
416+
417+ async function testDeleteBookmark ( tableName : string , userId : string , postId : string ) : Promise < void > {
418+ console . log ( '\n🗑️ Testing DeleteItem (remove bookmark)...' ) ;
419+ console . log ( ` Table: ${ tableName } ` ) ;
420+ console . log ( ` userId: ${ userId . substring ( 0 , 8 ) } ..., postId: ${ postId . substring ( 0 , 8 ) } ...` ) ;
421+
422+ const ddbClient = DynamoDBDocumentClient . from (
423+ new DynamoDBClient ( { region : ( amplifyconfig as any ) . aws_project_region } ) ,
424+ ) ;
425+ await ddbClient . send (
426+ new DeleteCommand ( {
427+ TableName : tableName ,
428+ Key : { userId, postId } ,
429+ } ) ,
430+ ) ;
431+ console . log ( '✅ Bookmark deleted!' ) ;
432+ }
433+
322434 return {
323435 testListTopics,
324436 testGetTopic,
@@ -336,9 +448,33 @@ export function createTestFunctions() {
336448 testCreateComment,
337449 testUpdateComment,
338450 testDeleteComment,
451+ testUploadAvatar,
452+ testGetAvatarUrl,
453+ testRemoveAvatar,
454+ testCreateBookmark,
455+ testGetBookmark,
456+ testDeleteBookmark,
339457 } ;
340458}
341459
460+ // ============================================================
461+ // Bookmarks Table Discovery
462+ // ============================================================
463+
464+ export async function discoverBookmarksTable ( region : string ) : Promise < string > {
465+ const client = new DynamoDBClient ( { region } ) ;
466+ const result = await client . send ( new ListTablesCommand ( { } ) ) ;
467+ const tables = result . TableNames ?? [ ] ;
468+ const bookmarksTables = tables . filter ( ( t ) => t . startsWith ( 'bookmarks-' ) ) ;
469+ if ( bookmarksTables . length === 0 ) {
470+ throw new Error ( 'No bookmarks table found. Expected a DynamoDB table starting with "bookmarks-".' ) ;
471+ }
472+ if ( bookmarksTables . length > 1 ) {
473+ console . log ( `⚠️ Found multiple bookmarks tables: ${ bookmarksTables . join ( ', ' ) } . Using first one.` ) ;
474+ }
475+ return bookmarksTables [ 0 ] ;
476+ }
477+
342478// ============================================================
343479// Shared Test Orchestration Functions
344480// ============================================================
@@ -440,9 +576,35 @@ export function createTestOrchestrator(testFunctions: ReturnType<typeof createTe
440576 await runner . runTest ( 'fetchUserActivity' , ( ) => testFunctions . testFetchUserActivity ( userId ) ) ;
441577 }
442578
579+ async function runStorageTests ( ) : Promise < void > {
580+ console . log ( '\n' + '=' . repeat ( 60 ) ) ;
581+ console . log ( '📸 PART 6: S3 Storage (Avatars)' ) ;
582+ console . log ( '=' . repeat ( 60 ) ) ;
583+
584+ const avatarKey = await runner . runTest ( 'uploadAvatar' , testFunctions . testUploadAvatar ) ;
585+ if ( avatarKey ) {
586+ await runner . runTest ( 'getAvatarUrl' , ( ) => testFunctions . testGetAvatarUrl ( avatarKey ) ) ;
587+ await runner . runTest ( 'removeAvatar' , ( ) => testFunctions . testRemoveAvatar ( avatarKey ) ) ;
588+ }
589+ }
590+
591+ async function runBookmarksTests ( userId : string , postId : string ) : Promise < void > {
592+ console . log ( '\n' + '=' . repeat ( 60 ) ) ;
593+ console . log ( '🔖 PART 7: Bookmarks DDB' ) ;
594+ console . log ( '=' . repeat ( 60 ) ) ;
595+
596+ const region = ( amplifyconfig as any ) . aws_project_region || 'us-east-1' ;
597+ const tableName = await discoverBookmarksTable ( region ) ;
598+ console . log ( ` Discovered bookmarks table: ${ tableName } ` ) ;
599+
600+ await runner . runTest ( 'createBookmark' , ( ) => testFunctions . testCreateBookmark ( tableName , userId , postId ) ) ;
601+ await runner . runTest ( 'getBookmark' , ( ) => testFunctions . testGetBookmark ( tableName , userId , postId ) ) ;
602+ await runner . runTest ( 'deleteBookmark' , ( ) => testFunctions . testDeleteBookmark ( tableName , userId , postId ) ) ;
603+ }
604+
443605 async function runCleanupTests ( topicId : string | null , postId : string | null , commentId : string | null ) : Promise < void > {
444606 console . log ( '\n' + '=' . repeat ( 60 ) ) ;
445- console . log ( '🧹 PART 6 : Cleanup (Delete Test Data)' ) ;
607+ console . log ( '🧹 PART 8 : Cleanup (Delete Test Data)' ) ;
446608 console . log ( '=' . repeat ( 60 ) ) ;
447609
448610 // Delete in reverse order of creation (comments -> posts -> topics)
@@ -457,6 +619,8 @@ export function createTestOrchestrator(testFunctions: ReturnType<typeof createTe
457619 runPostMutationTests,
458620 runCommentMutationTests,
459621 runActivityTests,
622+ runStorageTests,
623+ runBookmarksTests,
460624 runCleanupTests,
461625 } ;
462626}
0 commit comments