@@ -894,6 +894,10 @@ describe('S3Adapter tests', () => {
894
894
{ mount : 'http://example.com' , applicationId : 'test123' }
895
895
) ;
896
896
897
+ expect ( s3 . getFileLocation ) . toHaveBeenCalledWith (
898
+ jasmine . objectContaining ( { mount : 'http://example.com' , applicationId : 'test123' } ) ,
899
+ 'file.txt'
900
+ ) ;
897
901
expect ( result ) . toEqual ( {
898
902
location : jasmine . any ( String ) ,
899
903
name : 'file.txt' ,
@@ -929,13 +933,14 @@ describe('S3Adapter tests', () => {
929
933
s3ClientMock . send . and . returnValue ( Promise . resolve ( { } ) ) ;
930
934
s3 . _s3Client = s3ClientMock ;
931
935
932
- await s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { } ) ;
936
+ const out = await s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { } ) ;
933
937
934
938
expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
935
939
const commands = s3ClientMock . send . calls . all ( ) ;
936
940
const commandArg = commands [ 1 ] . args [ 0 ] ;
937
941
expect ( commandArg ) . toBeInstanceOf ( PutObjectCommand ) ;
938
942
expect ( commandArg . input . Key ) . toBe ( 'async-file.txt' ) ;
943
+ expect ( out . name ) . toBe ( 'async-file.txt' ) ;
939
944
} ) ;
940
945
941
946
it ( 'should handle generateKey that returns a Promise' , async ( ) => {
@@ -949,13 +954,14 @@ describe('S3Adapter tests', () => {
949
954
s3ClientMock . send . and . returnValue ( Promise . resolve ( { } ) ) ;
950
955
s3 . _s3Client = s3ClientMock ;
951
956
952
- await s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { } ) ;
957
+ const out = await s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { } ) ;
953
958
954
959
expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
955
960
const commands = s3ClientMock . send . calls . all ( ) ;
956
961
const commandArg = commands [ 1 ] . args [ 0 ] ;
957
962
expect ( commandArg ) . toBeInstanceOf ( PutObjectCommand ) ;
958
963
expect ( commandArg . input . Key ) . toBe ( 'promise-file.txt' ) ;
964
+ expect ( out . name ) . toBe ( 'promise-file.txt' ) ;
959
965
} ) ;
960
966
961
967
it ( 'should validate generateKey returns a non-empty string' , async ( ) => {
@@ -984,6 +990,19 @@ describe('S3Adapter tests', () => {
984
990
) . toBeRejectedWithError ( 'generateKey must return a non-empty string' ) ;
985
991
} ) ;
986
992
993
+ it ( 'should reject when generateKey returns only whitespace' , async ( ) => {
994
+ const options = {
995
+ bucket : 'bucket-1' ,
996
+ generateKey : ( ) => ' '
997
+ } ;
998
+ const s3 = new S3Adapter ( options ) ;
999
+ s3 . _s3Client = s3ClientMock ;
1000
+
1001
+ await expectAsync (
1002
+ s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { } )
1003
+ ) . toBeRejectedWithError ( 'generateKey must return a non-empty string' ) ;
1004
+ } ) ;
1005
+
987
1006
it ( 'should validate async generateKey returns a string' , async ( ) => {
988
1007
const options = {
989
1008
bucket : 'bucket-1' ,
@@ -998,6 +1017,88 @@ describe('S3Adapter tests', () => {
998
1017
} ) ;
999
1018
} ) ;
1000
1019
1020
+ describe ( 'URL construction with custom endpoints' , ( ) => {
1021
+ let s3ClientMock ;
1022
+
1023
+ beforeEach ( ( ) => {
1024
+ s3ClientMock = jasmine . createSpyObj ( 'S3Client' , [ 'send' ] ) ;
1025
+ s3ClientMock . send . and . returnValue ( Promise . resolve ( { } ) ) ;
1026
+ } ) ;
1027
+
1028
+ it ( 'should handle endpoint with path and query correctly' , async ( ) => {
1029
+ const s3 = new S3Adapter ( {
1030
+ bucket : 'test-bucket' ,
1031
+ s3overrides : {
1032
+ endpoint : 'https://example.com:8080/path?foo=bar'
1033
+ }
1034
+ } ) ;
1035
+ s3 . _s3Client = s3ClientMock ;
1036
+
1037
+ const result = await s3 . createFile ( 'test.txt' , 'hello world' , 'text/utf8' ) ;
1038
+
1039
+ // Should construct proper URL without breaking query parameters
1040
+ expect ( result . location ) . toBe ( 'https://example.com:8080/path/test-bucket/test.txt' ) ;
1041
+ } ) ;
1042
+
1043
+ it ( 'should handle path-style endpoint without bucket in hostname' , async ( ) => {
1044
+ const s3 = new S3Adapter ( {
1045
+ bucket : 'test-bucket' ,
1046
+ s3overrides : {
1047
+ endpoint : 'https://minio.example.com'
1048
+ }
1049
+ } ) ;
1050
+ s3 . _s3Client = s3ClientMock ;
1051
+
1052
+ const result = await s3 . createFile ( 'test.txt' , 'hello world' , 'text/utf8' ) ;
1053
+
1054
+ // Should add bucket to path for path-style
1055
+ expect ( result . location ) . toBe ( 'https://minio.example.com/test-bucket/test.txt' ) ;
1056
+ } ) ;
1057
+
1058
+ it ( 'should handle virtual-hosted-style endpoint with bucket in hostname' , async ( ) => {
1059
+ const s3 = new S3Adapter ( {
1060
+ bucket : 'test-bucket' ,
1061
+ s3overrides : {
1062
+ endpoint : 'https://test-bucket.s3.example.com'
1063
+ }
1064
+ } ) ;
1065
+ s3 . _s3Client = s3ClientMock ;
1066
+
1067
+ const result = await s3 . createFile ( 'test.txt' , 'hello world' , 'text/utf8' ) ;
1068
+
1069
+ // Should not duplicate bucket when it's already in hostname
1070
+ expect ( result . location ) . toBe ( 'https://test-bucket.s3.example.com/test.txt' ) ;
1071
+ } ) ;
1072
+
1073
+ it ( 'should fallback for malformed endpoint' , async ( ) => {
1074
+ const s3 = new S3Adapter ( {
1075
+ bucket : 'test-bucket' ,
1076
+ s3overrides : {
1077
+ endpoint : 'not-a-valid-url'
1078
+ }
1079
+ } ) ;
1080
+ s3 . _s3Client = s3ClientMock ;
1081
+
1082
+ const result = await s3 . createFile ( 'test.txt' , 'hello world' , 'text/utf8' ) ;
1083
+
1084
+ // Should fallback to safe construction
1085
+ expect ( result . location ) . toBe ( 'https://not-a-valid-url/test-bucket/test.txt' ) ;
1086
+ } ) ;
1087
+
1088
+ it ( 'should use default AWS endpoint when no custom endpoint' , async ( ) => {
1089
+ const s3 = new S3Adapter ( {
1090
+ bucket : 'test-bucket' ,
1091
+ region : 'us-west-2'
1092
+ } ) ;
1093
+ s3 . _s3Client = s3ClientMock ;
1094
+
1095
+ const result = await s3 . createFile ( 'test.txt' , 'hello world' , 'text/utf8' ) ;
1096
+
1097
+ // Should use standard AWS S3 URL
1098
+ expect ( result . location ) . toBe ( 'https://test-bucket.s3.us-west-2.amazonaws.com/test.txt' ) ;
1099
+ } ) ;
1100
+ } ) ;
1101
+
1001
1102
describe ( 'handleFileStream' , ( ) => {
1002
1103
const filename = 'file.txt' ;
1003
1104
let s3 ;
0 commit comments