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
157 changes: 157 additions & 0 deletions tests/SwaggerProvider.Tests/Schema.XmlDocTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,160 @@ let ``no returns tag when there is no success response``() =
let doc = getMethodXmlDoc noResponseSchema "Fire"
doc.IsSome |> shouldEqual true
doc.Value |> shouldNotContainText "<returns>"

// ── <remarks> tag from operation description ──────────────────────────────────

let private withRemarkSchema =
""" /pets/{id}:
get:
operationId: getPet
summary: Get a pet
description: Returns the full pet record by its unique identifier. Includes all nested sub-resources.
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: OK
content:
application/json:
schema:
type: string
"""

[<Fact>]
let ``remarks tag appears when description differs from summary``() =
let doc = getMethodXmlDoc withRemarkSchema "GetPet"
doc.IsSome |> shouldEqual true
doc.Value |> shouldContainText "<remarks>"
doc.Value |> shouldContainText "Returns the full pet record"
doc.Value |> shouldContainText "</remarks>"

[<Fact>]
let ``summary tag is present when description and summary both set``() =
let doc = getMethodXmlDoc withRemarkSchema "GetPet"
doc.IsSome |> shouldEqual true
doc.Value |> shouldContainText "<summary>Get a pet</summary>"

let private summaryEqualsDescSchema =
""" /status:
get:
operationId: getStatus
summary: Server status
description: Server status
responses:
"200":
description: OK
content:
application/json:
schema:
type: string
"""

[<Fact>]
let ``no remarks tag when description equals summary``() =
let doc = getMethodXmlDoc summaryEqualsDescSchema "GetStatus"
doc.IsSome |> shouldEqual true
doc.Value |> shouldNotContainText "<remarks>"

// ── XML special character escaping ───────────────────────────────────────────

let private xmlEscapeSchema =
""" /items:
get:
operationId: listItems
summary: List items (A & B)
description: Returns items where value < threshold or value > minimum
responses:
"200":
description: OK
content:
application/json:
schema:
type: string
"""

[<Fact>]
let ``ampersand in summary is escaped in XmlDoc``() =
let doc = getMethodXmlDoc xmlEscapeSchema "ListItems"
doc.IsSome |> shouldEqual true
doc.Value |> shouldContainText "&amp;"

[<Fact>]
let ``less-than in description is escaped in XmlDoc``() =
let doc = getMethodXmlDoc xmlEscapeSchema "ListItems"
doc.IsSome |> shouldEqual true
doc.Value |> shouldContainText "&lt;"

[<Fact>]
let ``greater-than in description is escaped in XmlDoc``() =
let doc = getMethodXmlDoc xmlEscapeSchema "ListItems"
doc.IsSome |> shouldEqual true
doc.Value |> shouldContainText "&gt;"

// ── Request body description in param XmlDoc ─────────────────────────────────

let private bodyWithDescSchema =
""" /items:
post:
operationId: createItem
summary: Create an item
requestBody:
description: The item payload to create
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
responses:
"201":
description: Created
"""

[<Fact>]
let ``request body description appears as param tag in method XmlDoc``() =
let doc = getMethodXmlDoc bodyWithDescSchema "CreateItem"
doc.IsSome |> shouldEqual true
doc.Value |> shouldContainText "The item payload to create"
Comment thread
sergey-tihon marked this conversation as resolved.

System.Text.RegularExpressions.Regex.IsMatch(doc.Value, "<param\\b[^>]*>\\s*The item payload to create\\s*</param>")
|> shouldEqual true

// ── 201 Created response as return type ──────────────────────────────────────

let private createdResponseSchema =
""" /items:
post:
operationId: createItem
summary: Create an item
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
responses:
"201":
description: The created item
content:
application/json:
schema:
type: string
"""

[<Fact>]
let ``201 response description appears in returns tag``() =
let doc = getMethodXmlDoc createdResponseSchema "CreateItem"
doc.IsSome |> shouldEqual true
doc.Value |> shouldContainText "<returns>"
doc.Value |> shouldContainText "The created item"
doc.Value |> shouldContainText "</returns>"
160 changes: 160 additions & 0 deletions tests/SwaggerProvider.Tests/UtilsTests.fs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace SwaggerProvider.Tests.UtilsTests

open System.Collections.Generic
open System.Text.Json.Nodes
open Xunit
open FsUnitTyped
open SwaggerProvider.Internal
Expand Down Expand Up @@ -93,3 +95,161 @@ module UniqueNameGeneratorTests =
let ``empty occupied names sequence behaves like default constructor``() =
let gen = UniqueNameGenerator(occupiedNames = [])
gen.MakeUnique "Foo" |> shouldEqual "Foo"

// ── XmlDoc.buildEnumDoc ───────────────────────────────────────────────────────

/// Direct unit tests for the XmlDoc.buildEnumDoc helper, which converts a list of
/// JsonNode enum values into a human-readable "Allowed values: …" string.
module BuildEnumDocTests =

[<Fact>]
let ``null enum list returns None``() =
XmlDoc.buildEnumDoc null |> shouldEqual None

[<Fact>]
let ``empty enum list returns None``() =
let empty = List<JsonNode>() :> IList<JsonNode>
XmlDoc.buildEnumDoc empty |> shouldEqual None

[<Fact>]
let ``single string enum value produces Allowed values line``() =
let values =
List<JsonNode>([| JsonValue.Create("active") :> JsonNode |]) :> IList<JsonNode>

let doc = XmlDoc.buildEnumDoc values
doc |> shouldEqual(Some "Allowed values: active")

[<Fact>]
let ``multiple string enum values are comma-separated``() =
let values =
List<JsonNode>(
[| JsonValue.Create("active") :> JsonNode
JsonValue.Create("inactive") :> JsonNode
JsonValue.Create("pending") :> JsonNode |]
)
:> IList<JsonNode>

XmlDoc.buildEnumDoc values
|> shouldEqual(Some "Allowed values: active, inactive, pending")

[<Fact>]
let ``integer enum values appear as numbers``() =
let values =
List<JsonNode>([| JsonValue.Create(1) :> JsonNode; JsonValue.Create(2) :> JsonNode |]) :> IList<JsonNode>

let doc = XmlDoc.buildEnumDoc values
doc.IsSome |> shouldEqual true
doc.Value |> shouldContainText "1"
doc.Value |> shouldContainText "2"

[<Fact>]
let ``null json node in enum list renders as null``() =
let values = List<JsonNode>([| null |]) :> IList<JsonNode>
let doc = XmlDoc.buildEnumDoc values
doc.IsSome |> shouldEqual true
doc.Value |> shouldContainText "null"

// ── XmlDoc.combineDescAndEnum ─────────────────────────────────────────────────

/// Direct unit tests for XmlDoc.combineDescAndEnum which merges a schema description
/// with optional enum documentation.
module CombineDescAndEnumTests =

[<Fact>]
let ``null description and None enum returns null``() =
XmlDoc.combineDescAndEnum null None |> shouldEqual null

[<Fact>]
let ``empty description and None enum returns null``() =
XmlDoc.combineDescAndEnum "" None |> shouldEqual null

[<Fact>]
let ``whitespace-only description and None enum returns null``() =
XmlDoc.combineDescAndEnum " " None |> shouldEqual null

[<Fact>]
let ``description only is returned unchanged``() =
XmlDoc.combineDescAndEnum "My description" None
|> shouldEqual "My description"

[<Fact>]
let ``enum doc only is returned when description is null``() =
XmlDoc.combineDescAndEnum null (Some "Allowed values: a, b")
|> shouldEqual "Allowed values: a, b"

[<Fact>]
let ``both description and enum doc are combined with newline``() =
XmlDoc.combineDescAndEnum "The status" (Some "Allowed values: a, b")
|> shouldEqual "The status\nAllowed values: a, b"

// ── XmlDoc.buildXmlDoc ────────────────────────────────────────────────────────

/// Direct unit tests for XmlDoc.buildXmlDoc β€” the central doc-string builder
/// for summary, remarks, parameter, and returns tags.
module BuildXmlDocTests =

[<Fact>]
let ``summary only produces summary tag``() =
let doc = XmlDoc.buildXmlDoc "Get pet" null [] None
doc |> shouldContainText "<summary>Get pet</summary>"

[<Fact>]
let ``description equal to summary does not produce remarks tag``() =
let doc = XmlDoc.buildXmlDoc "Get pet" "Get pet" [] None
doc |> shouldNotContainText "<remarks>"

[<Fact>]
let ``description different from summary produces remarks tag``() =
let doc = XmlDoc.buildXmlDoc "Get pet" "Returns the pet by ID" [] None
doc |> shouldContainText "<summary>Get pet</summary>"
doc |> shouldContainText "<remarks>Returns the pet by ID</remarks>"

[<Fact>]
let ``null description does not produce remarks tag``() =
let doc = XmlDoc.buildXmlDoc "Get pet" null [] None
doc |> shouldNotContainText "<remarks>"

[<Fact>]
let ``param descriptions appear as param tags``() =
let doc = XmlDoc.buildXmlDoc "Op" null [ ("petId", "The pet identifier") ] None

doc
|> shouldContainText "<param name=\"petId\">The pet identifier</param>"

[<Fact>]
let ``null param description is omitted``() =
let doc = XmlDoc.buildXmlDoc "Op" null [ ("petId", null) ] None
doc |> shouldNotContainText "<param"

[<Fact>]
let ``returns tag appears when returnDoc is Some``() =
let doc = XmlDoc.buildXmlDoc "Op" null [] (Some "The pet")
doc |> shouldContainText "<returns>The pet</returns>"

[<Fact>]
let ``returns tag is absent when returnDoc is None``() =
let doc = XmlDoc.buildXmlDoc "Op" null [] None
doc |> shouldNotContainText "<returns>"

[<Fact>]
let ``ampersand in summary is XML-escaped``() =
let doc = XmlDoc.buildXmlDoc "Cats & Dogs" null [] None
doc |> shouldContainText "Cats &amp; Dogs"
doc |> shouldNotContainText "Cats & Dogs"

[<Fact>]
let ``less-than in description is XML-escaped``() =
let doc = XmlDoc.buildXmlDoc "Op" "Value < threshold" [] None
doc |> shouldContainText "&lt;"
doc |> shouldNotContainText "Value < threshold"

[<Fact>]
let ``greater-than in description is XML-escaped``() =
let doc = XmlDoc.buildXmlDoc "Op" "Value > threshold" [] None
doc |> shouldContainText "&gt;"
doc |> shouldNotContainText "Value > threshold"

[<Fact>]
let ``empty summary and null description and no params returns empty string``() =
let doc = XmlDoc.buildXmlDoc "" null [] None
doc |> shouldEqual ""
Loading