diff --git a/README.md b/README.md index 8e1a4fa..a9a7094 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,101 @@ -# Gatepost spec +
+
+
The shared contract for every Gatepost SDK: the postcode grammar, the data files, the test vectors and the coding standards.
+ + + ++ Docs + · + Playground + · + Grammar + · + Contributing + · + Discussions +
> Unofficial. Not made or endorsed by NIPOST. +This repo defines how every Gatepost SDK reads and writes Nigeria's digital postcodes, and how every Gatepost client calls NIPOST's gateway. An SDK is correct when it passes the shared vectors and the contract scenarios. If `grammar.md` and a vector disagree, `grammar.md` wins, and the disagreement is a bug. + +The current version is in [`VERSION`](VERSION). + +## A postcode in three forms + +| Form | Example | Use | +|---|---|---| +| Compact | `EK01A03FK01` | storage and comparison | +| Canonical | `EK-01-A03-FK-01` | APIs and logs | +| Display | `EK 01 A03 FK 01` | screens and print | + +A postcode has five segments, from large to small: state, LGA, district, area and unit. [`grammar.md`](grammar.md) has the rules. + +## One test vector + +This case is `contains-001` in [`vectors/contains.json`](vectors/contains.json). + +```json +{ + "id": "contains-001", + "description": "finds a full code in its district", + "input": { + "prefix": "EK-01-A03", + "code": "EK-01-A03-FK-01" + }, + "options": {}, + "expect": { + "value": true + } +} +``` + +Every SDK runs the cases in [`vectors/`](vectors). + ## Contents | Path | Holds | |---|---| -| `grammar.md` | how every SDK reads and writes postcodes, and the public interface | -| `client.md` | how every client calls NIPOST's gateway: results, errors, retries and the queue | -| `openapi/` | NIPOST's gateway, completed with the responses that its own file leaves out | -| `fixtures/` | synthetic gateway responses and keys for the mock server | -| `contract/` | shared client test cases that every client must pass against the mock server | -| `data/` | state codes, GPS accuracy limits and the postcode format | -| `vectors/` | shared test cases that every SDK must pass | -| `standards/` | the coding standards and one file for each language | -| `CONTEXT.md` | the glossary | -| `templates/` | files for new repos | -| `scripts/` | `check-tells`, the vector builder, a plain reading of the grammar that checks each vector, and the checks of the OpenAPI file, the fixtures and the scenarios | - -## Check - -Run `make check`. It needs Python 3.11 or later and `uv`. +| [`grammar.md`](grammar.md) | how every SDK reads and writes postcodes, and the public interface | +| [`client.md`](client.md) | how every client calls NIPOST's gateway: results, errors, retries and the queue | +| [`openapi/`](openapi) | NIPOST's gateway, completed with the responses that its own file leaves out | +| [`fixtures/`](fixtures) | synthetic gateway responses and keys for the mock server | +| [`contract/`](contract) | shared client test cases that every client must pass against the mock server | +| [`data/`](data) | state codes, GPS accuracy limits and the postcode format | +| [`vectors/`](vectors) | shared test cases that every SDK must pass | +| [`standards/`](standards) | the coding standards and one file for each language | +| [`CONTEXT.md`](CONTEXT.md) | the glossary | +| [`templates/`](templates) | files for new repos | +| [`scripts/`](scripts) | `check-tells`, the vector builder, a plain reading of the grammar that checks each vector, and the checks of the OpenAPI file, the fixtures and the scenarios | + +## Who uses it + +| Repo | Language | Implements | +|---|---|---| +| [js](https://github.com/gatepost-dev/js) | TypeScript | the grammar and the client | +| [php](https://github.com/gatepost-dev/php) | PHP | the grammar and the client | + +The [docs site](https://gatepost-dev.github.io/docs/) shows the same grammar and client contract as pages for readers. + +## Change the spec + +A change to the public interface of an SDK starts here. Open an issue first. Then read [`CONTRIBUTING.md`](https://github.com/gatepost-dev/.github/blob/main/CONTRIBUTING.md). Run `make check` before you open a pull request. It needs Python 3.11 or later and `uv`. + +## Support + +Ask questions in [GitHub Discussions](https://github.com/gatepost-dev/spec/discussions). Report bugs in [GitHub Issues](https://github.com/gatepost-dev/spec/issues). Report security problems through the [private form](https://github.com/gatepost-dev/spec/security/advisories/new). ## Licence -Apache-2.0. See `LICENSE` and `NOTICE`. +Apache-2.0. See [`LICENSE`](LICENSE) and [`NOTICE`](NOTICE). diff --git a/scripts/tests/test_readme.py b/scripts/tests/test_readme.py new file mode 100644 index 0000000..4b4871c --- /dev/null +++ b/scripts/tests/test_readme.py @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: 2026 The Gatepost authors +# SPDX-License-Identifier: Apache-2.0 +"""Tests that the README example is a real vector, and that the README keeps its header.""" + +import json +import re +import unittest +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +NOTICE = "> Unofficial. Not made or endorsed by NIPOST." + + +class ReadmeTest(unittest.TestCase): + def test_example_vector_is_the_case_in_the_vectors_file(self) -> None: + readme = (ROOT / "README.md").read_text(encoding="utf-8") + block = re.search(r"```json\n(.*?)\n```", readme, re.DOTALL) + self.assertIsNotNone(block) + shown = json.loads(block.group(1) if block else "") + vectors = json.loads((ROOT / "vectors" / "contains.json").read_text(encoding="utf-8")) + case = next(item for item in vectors["cases"] if item["id"] == shown["id"]) + self.assertEqual(shown, case) + + def test_readme_and_template_keep_the_notice_after_the_header_block(self) -> None: + for name in ("README.md", "templates/README.md"): + with self.subTest(name=name): + text = (ROOT / name).read_text(encoding="utf-8") + self.assertIn(NOTICE, text) + self.assertLess(text.index('
+
+
{{ONE_SENTENCE_SUMMARY}}
+ + + ++ Docs + · + Playground + · + {{DOCS_PAGE_NAME}} + · + Contributing + · + Discussions +
> Unofficial. Not made or endorsed by NIPOST. -[]({{CI_URL}}) -[]({{REGISTRY_URL}}) -[](LICENSE) +```{{LANGUAGE_ID}} +{{QUICKSTART_CODE}} +``` + +{{ONE_SENTENCE_ABOUT_THE_OUTPUT}} ## Install -```{{LANGUAGE_ID}} +```sh {{INSTALL_COMMAND}} ``` -## Quickstart +{{UNPUBLISHED_NOTE}} + +## Use ```{{LANGUAGE_ID}} -{{QUICKSTART_CODE}} +{{SECOND_EXAMPLE_CODE}} ``` -{{ONE_SENTENCE_ABOUT_THE_OUTPUT}} +| Function | What it does | +|---|---| +| `{{FUNCTION}}` | {{WHAT_IT_DOES}} | ## What it does @@ -39,21 +71,27 @@ The guide and the API reference are at {{DOCS_URL}}. ## Support -Ask questions in GitHub Discussions. Report bugs in GitHub Issues. Report security problems privately, as `SECURITY.md` describes. +Ask questions in [GitHub Discussions](https://github.com/gatepost-dev/{{REPO_NAME}}/discussions). Report bugs in [GitHub Issues](https://github.com/gatepost-dev/{{REPO_NAME}}/issues). Report security problems through the [private form](https://github.com/gatepost-dev/{{REPO_NAME}}/security/advisories/new). -## Contributing +## Develop -Read `CONTRIBUTING.md` before you open a pull request. +Read [`CONTRIBUTING.md`](https://github.com/gatepost-dev/.github/blob/main/CONTRIBUTING.md) before you open a pull request. {{REPO_STEPS_LINK}} ## Licence -Apache-2.0. See `LICENSE` and `NOTICE`. +Apache-2.0. See [`LICENSE`](LICENSE) and [`NOTICE`](NOTICE).