
ConfigurationTest.pudu
Pudu80 lines10.6 KB
1/** @Docgen.Configuration.Tests — project configuration reads typed options and refuses mistakes */2module PuduLangDocgen.ConfigurationTest34import Std.Io as Io5import Std.Test as Test6import PuduLangDocgen.Configuration as Configuration7import PuduLangDocgen as Docgen8910fn read(text: Str) -> Configuration.Config {11 match Configuration.parse("docgen.json", text) {12 case Ok(found) => found13 case Err(_) => Configuration.defaults()14 }15}161718fn refused(file: Str, text: Str) -> Str {19 match Configuration.parse(file, text) {20 case Ok(_) => "accepted"21 case Err(problems) => problems[0].message22 }23}242526fn main() -> Int {27 let full = read("\{\"metadata\": \{\"src\": \"src/**/*.pudu\", \"dest\": \"reference\", \"includePrivateMembers\": true, \"filter\": \{\"exclude\": [\"*.Internal.*\"]\}, \"outputFormat\": \"markdown\", \"namespaceLayout\": \"nested\", \"memberLayout\": \"separatePages\", \"categoryLayout\": \"nested\", \"enumSortOrder\": \"alphabetic\", \"shouldSkipMarkup\": true, \"sourceLinkExclude\": \"gen/**\"\}, \"build\": \{\"content\": [\{\"files\": [\"**/*.md\"], \"exclude\": \"drafts/**\", \"src\": \"./docs/\", \"dest\": \"guide\"\}], \"resource\": \"images/**\", \"output\": \"public\", \"globalMetadata\": \{\"_appTitle\": \"Site\"\}, \"fileMetadata\": \{\"_layout\": \{\"index.md\": \"landing\"\}\}, \"template\": [\"default\", \"brand\"], \"xref\": \"https://example.org/xrefmap.yml\", \"sitemap\": \{\"baseUrl\": \"https://example.org\", \"priority\": \"0.8\", \"changefreq\": \"daily\"\}, \"disableGitFeatures\": true, \"gitContribute\": \{\"repo\": \"https://github.com/o/r\", \"branch\": \"dev\"\}, \"warningsAsErrors\": true, \"rules\": \{\"DG123\": \"off\"\}, \"markdownEngineProperties\": \{\"alerts\": \{\"todo\": \"alert alert-todo\"\}, \"plantUml\": \{\"remoteUrl\": \"https://plant.example/\", \"outputFormat\": \"png\"\}\}, \"dryRun\": true, \"exportRawModel\": true\}, \"pdf\": \{\"renderer\": [\"chromium\", \"--print-to-pdf=\{output\}\", \"\{url\}\"]\}\}")28 let checks = Test.suite("Configuration", &[29 Test.equals("defaults", &(Configuration.defaults().output, Configuration.defaults().templates, Configuration.defaults().gitFeatures), &("_site", ["default"], true)),30 Test.equals("content mapping", &full.content, &[Configuration.Mapping{files: ["**/*.md"], exclude: ["drafts/**"], src: "docs", dest: "guide", meta: []}]),31 Test.equals("plain glob mapping", &full.resource, &[Configuration.Mapping{files: ["images/**"], exclude: [], src: "", dest: "", meta: []}]),32 Test.equals("metadata source", &(full.metadata[0].options.dest, full.metadata[0].options.includePrivate, full.metadata[0].options.exclude, full.metadata[0].format, full.metadata[0].options.nested, full.metadata[0].options.separate), &("reference", true, ["*.Internal.*"], "markdown", true, true)),33 Test.equals("more metadata options", &(full.metadata[0].options.categories, full.metadata[0].options.alphabetic, full.metadata[0].options.skipMarkup, full.metadata[0].options.sourceExclude), &("nested", true, true, ["gen/**"])),34 Test.equals("filter file", &match Configuration.parse("docgen.json", "\{\"metadata\": \{\"src\": \"a\", \"filter\": \"filter.yml\"\}\}") { case Ok(found) => found.metadata[0].filterFile case Err(_) => "" }, &"filter.yml"),35 Test.equals("groups", &match Configuration.parse("docgen.json", "\{\"build\": \{\"content\": [\{\"files\": \"**/*.md\", \"group\": \"v1\"\}], \"groups\": \{\"v1\": \{\"dest\": \"v1\", \"_appTitle\": \"Version 1\"\}\}, \"theme\": \"dark\"\}\}") { case Ok(found) => (found.content[0].dest, found.content[0].meta, found.templates) case Err(_) => ("", [], []) }, &("v1", [("_appTitle", Docgen.Text("Version 1"))], ["default", "dark"])),36 Test.equals("unknown group", &refused("docgen.json", "\{\"build\": \{\"content\": [\{\"files\": \"a\", \"group\": \"v9\"\}]\}\}"), &"build.content[0].group: names no entry of build.groups: v9"),37 Test.equals("api sources may sit above the project", &match Configuration.parse("docgen.json", "\{\"metadata\": \{\"src\": [\{\"files\": \"**/*.pudu\", \"src\": \"../src/\"\}]\}\}") { case Ok(found) => found.metadata[0].mappings[0].src case Err(problems) => problems[0].message }, &"../src"),38 Test.equals("content may not", &refused("docgen.json", "\{\"build\": \{\"content\": [\{\"files\": \"*.md\", \"src\": \"../docs\"\}]\}\}"), &"build.content[0].src: must be a relative folder inside the project: ../docs"),39 Test.equals("bounded ascent", &refused("docgen.json", "\{\"metadata\": \{\"src\": [\{\"files\": \"*\", \"src\": \"../../../../../../../../../x\"\}]\}\}"), &"metadata.src[0].src: leads more than 8 folders above the project: ../../../../../../../../../x"),40 Test.equals("distinct api destinations", &refused("docgen.json", "\{\"metadata\": [\{\"src\": \"a\"\}, \{\"src\": \"b\"\}]\}"), &"metadata.dest: each API source needs its own destination folder"),41 Test.equals("web repository", &refused("docgen.json", "\{\"build\": \{\"gitContribute\": \{\"repo\": \"github.com/o/r\"\}\}\}"), &"build.gitContribute.repo: must be an http or https address"),42 Test.equals("renderer output", &refused("docgen.json", "\{\"pdf\": \{\"renderer\": [\"chromium\"]\}\}"), &"pdf.renderer: must name a program and an argument holding \{output\}"),43 Test.equals("xref entries", &refused("docgen.json", "\{\"build\": \{\"xref\": [\"javascript:x\"]\}\}"), &"build.xref[0]: must be a web address or a relative path inside the project"),44 Test.equals("xref services", &read("\{\"build\": \{\"xrefService\": [\"https://xref.example/query?uid=\{uid\}\"]\}\}").xrefService, &["https://xref.example/query?uid=\{uid\}"]),45 Test.equals("xref services default empty", &read("\{\}").xrefService, &[]),46 Test.equals("xref service needs a slot", &refused("docgen.json", "\{\"build\": \{\"xrefService\": \"https://xref.example/query\"\}\}"), &"build.xrefService[0]: must be a web address holding \{uid\}"),47 Test.equals("xref service must be web", &refused("docgen.json", "\{\"build\": \{\"xrefService\": \"ftp://x/\{uid\}\"\}\}"), &"build.xrefService[0]: must be a web address holding \{uid\}"),48 Test.equals("output and templates", &(full.output, full.templates, full.xref), &("public", ["default", "brand"], ["https://example.org/xrefmap.yml"])),49 Test.equals("global and file metadata", &(full.globalMetadata, full.fileMetadata.length()), &([("_appTitle", Docgen.Text("Site"))], 1)),50 Test.equals("switches", &(full.gitFeatures, full.warningsAsErrors, full.dryRun, full.exportRawModel), &(false, true, true, "public")),51 Test.equals("contribution", &full.contribution, &Configuration.Contribution{repo: "https://github.com/o/r", branch: "dev", path: ""}),52 Test.equals("rules and alerts", &(full.rules, full.alerts), &([("DG123", "off")], [("TODO", "alert alert-todo")])),53 Test.equals("diagrams", &(full.diagrams.remote, full.diagrams.format, full.diagrams.local), &("https://plant.example", "png", false)),54 Test.that("sitemap", match full.sitemap { case Some(found) => found.baseUrl == "https://example.org" && found.priority == "0.8" case None => false }),55 Test.that("pdf renderer", match full.pdf { case Some(found) => found.renderer == ["chromium", "--print-to-pdf=\{output\}", "\{url\}"] case None => false }),56 Test.equals("yaml configuration", &read("build:\n output: out").output, &"_site"),57 Test.equals("yaml read by name", &match Configuration.parse("docgen.yml", "build:\n output: out\n") { case Ok(found) => found.output case Err(_) => "" }, &"out"),58 Test.equals("not json", &refused("docgen.json", "\{"), &"configuration is not valid JSON: unterminated object"),59 Test.equals("not an object", &refused("docgen.json", "[]"), &"configuration must be an object"),60 Test.equals("unknown top option", &refused("docgen.json", "\{\"site\": 1\}"), &"site: unknown option"),61 Test.equals("unknown build option", &refused("docgen.json", "\{\"build\": \{\"colour\": 1\}\}"), &"build.colour: unknown option"),62 Test.equals("wrong type", &refused("docgen.json", "\{\"build\": \{\"dryRun\": \"yes\"\}\}"), &"build.dryRun: must be true or false"),63 Test.equals("escaping folder", &refused("docgen.json", "\{\"build\": \{\"output\": \"../out\"\}\}"), &"build.output: must be a relative folder inside the project: ../out"),64 Test.equals("project folder as output", &refused("docgen.json", "\{\"build\": \{\"output\": \".\"\}\}"), &"build.output: must name a folder other than the project folder"),65 Test.equals("mapping needs files", &refused("docgen.json", "\{\"build\": \{\"content\": [\{\"src\": \"docs\"\}]\}\}"), &"build.content[0].files: must name at least one glob"),66 Test.equals("rule level", &refused("docgen.json", "\{\"build\": \{\"rules\": \{\"DG1\": \"loud\"\}\}\}"), &"build.rules.DG1: must be one of error, warning, info, off"),67 Test.equals("sitemap address", &refused("docgen.json", "\{\"build\": \{\"sitemap\": \{\"baseUrl\": \"docs.example\"\}\}\}"), &"build.sitemap.baseUrl: must be an absolute http or https address"),68 Test.equals("sitemap priority", &refused("docgen.json", "\{\"build\": \{\"sitemap\": \{\"baseUrl\": \"https://a.b\", \"priority\": \"1.5\"\}\}\}"), &"build.sitemap.priority: must be a number from 0.0 to 1.0"),69 Test.equals("sitemap frequency", &refused("docgen.json", "\{\"build\": \{\"sitemap\": \{\"baseUrl\": \"https://a.b\", \"changefreq\": \"often\"\}\}\}"), &"build.sitemap.changefreq: must be one of always, hourly, daily, weekly, monthly, yearly, never"),70 Test.equals("metadata needs sources", &refused("docgen.json", "\{\"metadata\": [\{\"dest\": \"api\"\}]\}"), &"metadata[0].src: must name the Pudu sources to document"),71 Test.equals("metadata format", &refused("docgen.json", "\{\"metadata\": [\{\"src\": \"a\", \"outputFormat\": \"xml\"\}]\}"), &"metadata[0].outputFormat: must be one of json, markdown, apiPage"),72 Test.equals("member layout", &refused("docgen.json", "\{\"metadata\": [\{\"src\": \"a\", \"memberLayout\": \"wide\"\}]\}"), &"metadata[0].memberLayout: must be one of samePage, separatePages"),73 Test.equals("diagram mode", &refused("docgen.json", "\{\"build\": \{\"markdownEngineProperties\": \{\"plantUml\": \{\"renderingMode\": \"cloud\"\}\}\}\}"), &"build.markdownEngineProperties.plantUml.renderingMode: must be remote or local"),74 Test.equals("file metadata shape", &refused("docgen.json", "\{\"build\": \{\"fileMetadata\": \{\"k\": 1\}\}\}"), &"build.fileMetadata: file metadata k must map glob patterns to values")75 ])76 let ran = Test.run(&checks)77 for failure in Test.failuresOf(&ran) { let _printed = Io.writeErrorLine(failure) }78 Test.report(&ran)79}80