Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-docgen

Documentation publishing for Pudu: articles, API references, navigation, search, and static websites

0.1.0Apache-2.01

InstallClose

ConfigurationTest.pudu

Pudu80 lines10.6 KB

GitHub ↗
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 Docgen89/// A configuration read from JSON, or the defaults when it is refused.10fn read(text: Str) -> Configuration.Config {11  match Configuration.parse("docgen.json", text) {12    case Ok(found) => found13    case Err(_) => Configuration.defaults()14  }15}1617/// The message of a refused configuration.18fn refused(file: Str, text: Str) -> Str {19  match Configuration.parse(file, text) {20    case Ok(_) => "accepted"21    case Err(problems) => problems[0].message22  }23}2425/// Runs every option, default, and refusal contract.26fn 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