Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/SwaggerProvider.Runtime/RuntimeHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,12 @@ module RuntimeHelpers =
| :? float32 as f -> f.ToString()
| :? double as d -> d.ToString()
| :? Guid as g -> g.ToString()
// Byte arrays are encoded as base64 β€” the OpenAPI "byte" format (RFC 4648).
// Without this arm, byte[] falls through to obj.ToString() β†’ "System.Byte[]",
// which is incorrect for path, header, cookie, and form-urlencoded parameters.
// Note: toQueryParams has its own byte[]/Option<byte[]> arms that use
// client.Serialize; this arm aligns with that behaviour for all other contexts.
| :? array<byte> as xs -> Convert.ToBase64String xs
| _ ->
// Hoist GetType() once; previously tryFormatDateOnly and tryFormatTimeOnly
// each called GetType() internally, resulting in up to 3 GetType() calls for
Expand Down
43 changes: 43 additions & 0 deletions tests/SwaggerProvider.Tests/RuntimeHelpersTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,32 @@ module ToParamTests =
let result = toParam(box(None: TimeOnly option))
result |> shouldEqual null

[<Fact>]
let ``toParam encodes byte array as base64 (OpenAPI 'byte' format)``() =
// Without the byte[] arm, obj.ToString() would yield "System.Byte[]".
// The correct encoding for the OpenAPI "byte" format is standard base64.
let bytes = [| 1uy; 2uy; 3uy |]
let result = toParam(box bytes)
result |> shouldEqual(Convert.ToBase64String bytes)

[<Fact>]
let ``toParam encodes empty byte array as empty base64 string``() =
let result = toParam(box([||]: byte[]))
result |> shouldEqual ""

[<Fact>]
let ``toParam unwraps Some(byte array) and encodes as base64``() =
// Option<byte[]> Some is unwrapped by the generic option arm, then toParam
// is called recursively on the inner byte[] value.
let bytes = [| 72uy; 101uy; 108uy; 108uy; 111uy |] // "Hello" in ASCII
let result = toParam(box(Some bytes))
result |> shouldEqual(Convert.ToBase64String bytes)

[<Fact>]
let ``toParam returns null for None(byte array)``() =
let result = toParam(box(None: byte[] option))
result |> shouldEqual null


// ── CLI enum serialization via toParam ──────────────────────────────────────────────────────
// toParam must serialize string enums to their original OpenAPI wire values and integer
Expand Down Expand Up @@ -1292,6 +1318,23 @@ module ToFormUrlEncodedContentTests =
body |> shouldNotContainText "nestedNone"
}

[<Fact>]
let ``toFormUrlEncodedContent encodes byte array as base64``() =
// Without the byte[] arm in toParam, this produced "data=System.Byte%5B%5D".
// With the fix, byte arrays are encoded as standard base64 (OpenAPI 'byte' format).
task {
let bytes = [| 1uy; 2uy; 3uy |]
let expected = Convert.ToBase64String bytes

use content = toFormUrlEncodedContent(seq { ("data", box bytes) })

let! body = content.ReadAsStringAsync()
let encodedValue = body.Substring("data=".Length)
let decodedValue = WebUtility.UrlDecode(encodedValue)

decodedValue |> shouldEqual expected
}


module ToMultipartFormDataContentTests =

Expand Down
Loading