
RestTest.pudu
Pudu149 lines7.3 KB
1/** @Docgen.Rest.Tests — interface descriptions become operation and schema references */2module PuduLangDocgen.RestTest34import Std.Io as Io5import Std.Json as Json6import Std.Result as Result7import Std.Test as Test8import PuduLangDocgen as Docgen9import PuduLangDocgen.Markdown.Phrase as Phrase10import PuduLangDocgen.Meta as Meta11import PuduLangDocgen.Rest.OpenApi as OpenApi12import PuduLangDocgen.Rest.Pages as Pages13import PuduLangDocgen.Yaml as Yaml141516fn current() -> Str {17 [18 "openapi: 3.0.3",19 "info:",20 " title: Pet Store",21 " version: 1.2.0",22 " description: Manage **pets**.",23 "servers:",24 " - url: https://pets.example/v1",25 "paths:",26 " /pets/\{id\}:",27 " parameters:",28 " - $ref: '#/components/parameters/Id'",29 " get:",30 " operationId: getPet",31 " summary: Get a pet",32 " tags: [pets]",33 " responses:",34 " '200':",35 " description: The pet",36 " content:",37 " application/json:",38 " schema:",39 " $ref: '#/components/schemas/Pet'",40 " '404':",41 " description: Not found",42 " delete:",43 " operationId: deletePet",44 " deprecated: true",45 " responses:",46 " '204':",47 " description: Deleted",48 " /pets:",49 " post:",50 " operationId: addPet",51 " tags: [pets]",52 " requestBody:",53 " required: true",54 " content:",55 " application/json:",56 " schema:",57 " type: array",58 " items:",59 " $ref: '#/components/schemas/Pet'",60 " responses:",61 " '201':",62 " description: Created",63 "components:",64 " parameters:",65 " Id:",66 " name: id",67 " in: path",68 " schema:",69 " type: integer",70 " format: int64",71 " schemas:",72 " Pet:",73 " type: object",74 " required: [name]",75 " properties:",76 " name:",77 " type: string",78 " description: Given name",79 " kind:",80 " $ref: '#/components/schemas/Kind'",81 " Kind:",82 " type: string",83 " enum: [cat, dog]"84 ].join("\n")85}868788fn older() -> Str {89 "\{\"swagger\": \"2.0\", \"info\": \{\"title\": \"Legacy\"\}, \"host\": \"legacy.example\", \"basePath\": \"/api\", \"paths\": \{\"/items\": \{\"post\": \{\"parameters\": [\{\"name\": \"item\", \"in\": \"body\", \"required\": true, \"schema\": \{\"$ref\": \"#/definitions/Item\"\}\}], \"responses\": \{\"200\": \{\"description\": \"ok\", \"schema\": \{\"type\": \"string\"\}\}\}\}\}\}, \"definitions\": \{\"Item\": \{\"type\": \"object\"\}\}\}"90}919293fn service(text: Str) -> OpenApi.Service {94 let value = if text.startsWith("\{") {95 match Json.decode(text) {96 case Ok(found) => Meta.fromJson(&found)97 case Err(_) => Docgen.Nothing98 }99 } else { Result.unwrapOr(Yaml.decode(text), Docgen.Nothing) }100 match OpenApi.read(&value) {101 case Ok(found) => found102 case Err(_) => OpenApi.Service{title: "", version: "", description: "", servers: [], operations: [], schemas: []}103 }104}105106107fn refused(text: Str) -> Str {108 match OpenApi.read(&Result.unwrapOr(Yaml.decode(text), Docgen.Nothing)) {109 case Ok(_) => "accepted"110 case Err(reason) => reason111 }112}113114115fn main() -> Int {116 let pets = service(current())117 let legacy = service(older())118 let context = Phrase.Scope{page: "rest/pets.html", source: "docs/rest/pets.yml", outputs: mapOf([]), references: mapOf([]), settings: Phrase.rendering()}119 let (page, references, problems) = Pages.page(&pets, "pets", &context)120 let checks = Test.suite("Rest", &[121 Test.equals("recognized", &[OpenApi.recognizes(&Result.unwrapOr(Yaml.decode(current()), Docgen.Nothing)), OpenApi.recognizes(&Docgen.Fields([("title", Docgen.Text("x"))]))], &[true, false]),122 Test.equals("title and version", &(pets.title, pets.version), &("Pet Store", "1.2.0")),123 Test.equals("servers", &[pets.servers, legacy.servers], &[["https://pets.example/v1"], ["https://legacy.example/api"]]),124 Test.equals("operations in path order", &pets.operations.map(|held: OpenApi.Operation| held.method + " " + held.id), &["GET getPet", "DELETE deletePet", "POST addPet"]),125 Test.equals("shared path parameter", &pets.operations[0].parameters, &[OpenApi.Parameter{name: "id", location: "path", required: true, kind: "integer (int64)", description: ""}]),126 Test.equals("response content", &pets.operations[0].responses[0].payloads, &[OpenApi.Payload{media: "application/json", kind: "Pet"}]),127 Test.equals("request body", &(pets.operations[2].body, pets.operations[2].bodyRequired), &([OpenApi.Payload{media: "application/json", kind: "array of Pet"}], true)),128 Test.equals("deprecated flag", &pets.operations[1].deprecated, &true),129 Test.equals("schemas sorted", &pets.schemas.map(|held: OpenApi.Schema| held.name), &["Kind", "Pet"]),130 Test.equals("schema properties", &pets.schemas[1].properties, &[OpenApi.Property{name: "name", kind: "string", required: true, description: "Given name"}, OpenApi.Property{name: "kind", kind: "Kind", required: false, description: ""}]),131 Test.equals("enumerations", &pets.schemas[0].values, &["cat", "dog"]),132 Test.equals("body parameter becomes a payload", &legacy.operations[0].body, &[OpenApi.Payload{media: "application/json", kind: "Item"}]),133 Test.equals("older response schema", &legacy.operations[0].responses[0].payloads[0].kind, &"string"),134 Test.equals("generated operation id", &legacy.operations[0].id, &"post /items"),135 Test.equals("title required", &refused("openapi: 3.0.0\ninfo:\n version: 1"), &"interface description needs info.title"),136 Test.equals("describe combinations", &OpenApi.describe(&Docgen.Fields([("oneOf", Docgen.Items([Docgen.Fields([("type", Docgen.Text("string"))]), Docgen.Fields([("$ref", Docgen.Text("#/definitions/A"))])]))])), &"string or A"),137 Test.equals("references", &references.map(|held: Docgen.Reference| held.uid + " " + held.href), &["pets rest/pets.html", "pets.getPet rest/pets.html#getpet", "pets.addPet rest/pets.html#addpet", "pets.deletePet rest/pets.html#deletepet", "pets.schemas.Kind rest/pets.html#schema-kind", "pets.schemas.Pet rest/pets.html#schema-pet"]),138 Test.equals("no problems", &problems, &[]),139 Test.equals("outline groups by tag", &page.headings.filter(|held: Docgen.Heading| held.level == 2).map(|held: Docgen.Heading| held.title), &["Servers", "pets", "Operations", "Schemas"]),140 Test.that("method badge and path", page.body.contains("<span class=\"method method-get\">GET</span><code>/pets/\{id\}</code>")),141 Test.that("schema links", page.body.contains("<a class=\"xref\" href=\"#schema-pet\">Pet</a>")),142 Test.that("description rendered", page.body.contains("<strong>pets</strong>")),143 Test.that("deprecated badge", page.body.contains("<span class=\"badge deprecated\">Deprecated</span>"))144 ])145 let ran = Test.run(&checks)146 for failure in Test.failuresOf(&ran) { let _printed = Io.writeErrorLine(failure) }147 Test.report(&ran)148}149