@@ -211,6 +211,106 @@ func TestNoServerUsesLegacySingleServerBehavior(t *testing.T) {
211211 }
212212}
213213
214+ func TestSingleServerDefaultBucketPathSignAndVerify (t * testing.T ) {
215+ now := time .Date (2026 , 6 , 8 , 12 , 0 , 0 , 0 , time .UTC )
216+ expires := now .Add (time .Minute )
217+ raw , err := SignURL (SignInput {
218+ Method : "GET" ,
219+ BaseURL : "https://files.example.com" ,
220+ Bucket : "demo" ,
221+ Key : "file.txt" ,
222+ Secret : "top-secret" ,
223+ Expires : expires ,
224+ DefaultBucketPath : true ,
225+ })
226+ if err != nil {
227+ t .Fatalf ("SignURL() error = %v" , err )
228+ }
229+ u , err := url .Parse (raw )
230+ if err != nil {
231+ t .Fatalf ("parse signed URL: %v" , err )
232+ }
233+ if got , want := u .EscapedPath (), "/file.txt" ; got != want {
234+ t .Fatalf ("path = %q, want %q" , got , want )
235+ }
236+
237+ claims , err := VerifyURL (VerifyInput {Method : "GET" , URL : raw , Secret : "top-secret" , Now : now , DefaultBucket : "demo" })
238+ if err != nil {
239+ t .Fatalf ("VerifyURL() error = %v" , err )
240+ }
241+ if claims .Server != "" || claims .Bucket != "demo" || claims .Key != "file.txt" {
242+ t .Fatalf ("claims = %#v" , claims )
243+ }
244+ wantCanonical := "GET\n demo\n file.txt\n " + formatUnixSeconds (expires ) + "\n \n \n "
245+ if got := CanonicalString (claims ); got != wantCanonical {
246+ t .Fatalf ("CanonicalString() = %q, want %q" , got , wantCanonical )
247+ }
248+ }
249+
250+ func TestSingleServerDefaultBucketPathRequiresDefaultBucket (t * testing.T ) {
251+ now := time .Date (2026 , 6 , 8 , 12 , 0 , 0 , 0 , time .UTC )
252+ raw , err := SignURL (SignInput {
253+ Method : "GET" ,
254+ BaseURL : "https://files.example.com" ,
255+ Bucket : "demo" ,
256+ Key : "file.txt" ,
257+ Secret : "top-secret" ,
258+ Expires : now .Add (time .Minute ),
259+ DefaultBucketPath : true ,
260+ })
261+ if err != nil {
262+ t .Fatalf ("SignURL() error = %v" , err )
263+ }
264+ u , err := url .Parse (raw )
265+ if err != nil {
266+ t .Fatalf ("parse signed URL: %v" , err )
267+ }
268+ if got , want := u .EscapedPath (), "/file.txt" ; got != want {
269+ t .Fatalf ("path = %q, want %q" , got , want )
270+ }
271+
272+ _ , err = VerifyURL (VerifyInput {Method : "GET" , URL : raw , Secret : "top-secret" , Now : now })
273+ if err == nil {
274+ t .Fatal ("VerifyURL() without DefaultBucket succeeded, want error" )
275+ }
276+ }
277+
278+ func TestSingleServerDefaultBucketPathShortFormWins (t * testing.T ) {
279+ now := time .Date (2026 , 6 , 8 , 12 , 0 , 0 , 0 , time .UTC )
280+ expires := now .Add (time .Minute )
281+ raw , err := SignURL (SignInput {
282+ Method : "GET" ,
283+ BaseURL : "https://files.example.com" ,
284+ Bucket : "demo" ,
285+ Key : "demo/file.txt" ,
286+ Secret : "top-secret" ,
287+ Expires : expires ,
288+ DefaultBucketPath : true ,
289+ })
290+ if err != nil {
291+ t .Fatalf ("SignURL() error = %v" , err )
292+ }
293+ u , err := url .Parse (raw )
294+ if err != nil {
295+ t .Fatalf ("parse signed URL: %v" , err )
296+ }
297+ if got , want := u .EscapedPath (), "/demo/file.txt" ; got != want {
298+ t .Fatalf ("path = %q, want %q" , got , want )
299+ }
300+
301+ claims , err := VerifyURL (VerifyInput {Method : "GET" , URL : raw , Secret : "top-secret" , Now : now , DefaultBucket : "demo" })
302+ if err != nil {
303+ t .Fatalf ("VerifyURL() error = %v" , err )
304+ }
305+ if claims .Server != "" || claims .Bucket != "demo" || claims .Key != "demo/file.txt" {
306+ t .Fatalf ("claims = %#v" , claims )
307+ }
308+ wantCanonical := "GET\n demo\n demo/file.txt\n " + formatUnixSeconds (expires ) + "\n \n \n "
309+ if got := CanonicalString (claims ); got != wantCanonical {
310+ t .Fatalf ("CanonicalString() = %q, want %q" , got , wantCanonical )
311+ }
312+ }
313+
214314func loadVectors (t * testing.T ) []vector {
215315 t .Helper ()
216316 data , err := os .ReadFile ("../../testdata/edge-signing-vectors.json" )
0 commit comments