{"id":16819562,"url":"https://github.com/erikgrinaker/goldenscript","last_synced_at":"2025-05-05T14:41:49.155Z","repository":{"id":237531918,"uuid":"786406473","full_name":"erikgrinaker/goldenscript","owner":"erikgrinaker","description":"A scriptable, data-driven Rust test framework using golden masters","archived":false,"fork":false,"pushed_at":"2024-07-01T12:24:34.000Z","size":208,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-02T07:07:48.315Z","etag":null,"topics":["rust-crate","testing"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/erikgrinaker.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-14T11:16:32.000Z","updated_at":"2025-03-27T09:19:51.000Z","dependencies_parsed_at":"2024-05-28T18:15:02.755Z","dependency_job_id":"ffdeb005-31ce-4111-8046-a2941d1a0e94","html_url":"https://github.com/erikgrinaker/goldenscript","commit_stats":null,"previous_names":["erikgrinaker/goldenscript"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikgrinaker%2Fgoldenscript","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikgrinaker%2Fgoldenscript/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikgrinaker%2Fgoldenscript/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikgrinaker%2Fgoldenscript/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/erikgrinaker","download_url":"https://codeload.github.com/erikgrinaker/goldenscript/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252516129,"owners_count":21760728,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rust-crate","testing"],"created_at":"2024-10-13T10:53:50.081Z","updated_at":"2025-05-05T14:41:49.136Z","avatar_url":"https://github.com/erikgrinaker.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Goldenscript\n\n[![Crates.io](https://img.shields.io/crates/v/goldenscript.svg)](https://crates.io/crates/goldenscript)\n[![Docs.rs](https://img.shields.io/docsrs/goldenscript/latest)](https://docs.rs/goldenscript)\n[![CI](https://github.com/erikgrinaker/goldenscript/actions/workflows/ci.yml/badge.svg)](https://github.com/erikgrinaker/goldenscript/actions/workflows/ci.yml)\n\nA Rust testing framework loosely based on Cockroach Labs'\n[`datadriven`](https://github.com/cockroachdb/datadriven) framework for Go. It\ncombines several testing techniques that make it easy and efficient to write and\nupdate test cases:\n\n* [Golden master testing](https://en.wikipedia.org/wiki/Characterization_test)\n  (aka characterization testing or historical oracle)\n* [Data-driven testing](https://en.wikipedia.org/wiki/Data-driven_testing)\n  (aka table-driven testing or parameterized testing)\n* [Keyword-driven testing](https://en.wikipedia.org/wiki/Keyword-driven_testing)\n\nA goldenscript is a plain text file that contains a set of arbitrary input\ncommands and their expected text output, separated by `---`:\n\n```\ncommand\n---\noutput\n\ncommand argument\ncommand key=value\n---\noutput\n```\n\nThe commands are executed by a provided [`Runner`](https://docs.rs/goldenscript/latest/goldenscript/trait.Runner.html).\nThe expected output is usually not written by hand, but instead generated by\nrunning tests with the environment variable `UPDATE_GOLDENFILES=1`:\n\n```sh\n$ UPDATE_GOLDENFILES=1 cargo test\n```\n\nThe files are then verified by inspection and checked in to version control.\nTests will fail with a diff if they don't match the expected output.\n\nThis approach is particularly useful when testing complex stateful systems, such\nas database operations, network protocols, or language parsing. It can be\ntedious and labor-intensive to write and assert such cases by hand, so scripting\nand recording these interactions often yields much better test coverage at a\nfraction of the cost.\n\nInternally, the [`goldenfile`](https://docs.rs/goldenfile/latest/goldenfile/)\ncrate is used to manage golden files.\n\n## Documentation\n\nSee the [crate documentation](https://docs.rs/goldenscript/latest/goldenscript/)\nwhich has more information on syntax and features.\n\n## Examples\n\nFor real-world examples, see e.g.:\n\n* [toyDB Raft](https://github.com/erikgrinaker/toydb/tree/master/src/raft/testscripts/node):\n  distributed consensus cluster.\n* [toyDB MVCC](https://github.com/erikgrinaker/toydb/tree/master/src/storage/testscripts/mvcc):\n  ACID transactions.\n* [goldenscript parser](https://github.com/erikgrinaker/goldenscript/tree/main/tests/scripts):\n  Goldenscript uses itself to test its parser and runner.\n\nBelow is a basic example, testing the Rust standard library's\n[`BTreeMap`](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html).\n\n```yaml\n# Tests the Rust standard library BTreeMap.\n\n# Get and range returns nothing for an empty map.\nget foo\nrange\n---\nget → None\n\n# Inserting keys out of order will return them in order. Silence the insert\n# output with ().\n(insert b=2 a=1 c=3)\nrange\n---\na=1\nb=2\nc=3\n\n# Getting a key returns its value.\nget b\n---\nget → Some(\"2\")\n\n# Bounded scans, where the end is exclusive.\nrange b\n---\nb=2\nc=3\n\nrange a c\n---\na=1\nb=2\n\n# An end bound less than the start bound panics. Expect the failure with !.\n!range b a\n---\nPanic: range start is greater than range end in BTreeMap\n\n# Replacing a key updates the value and returns the old one.\ninsert b=foo\nget b\n---\ninsert → Some(\"2\")\nget → Some(\"foo\")\n```\n\nThe corresponding runner for this script:\n\n```rust\n#[derive(Default)]\nstruct BTreeMapRunner {\n    map: std::collections::BTreeMap\u003cString, String\u003e,\n}\n\nimpl goldenscript::Runner for BTreeMapRunner {\n    fn run(\u0026mut self, command: \u0026goldenscript::Command) -\u003e Result\u003cString, Box\u003cdyn Error\u003e\u003e {\n        let mut output = String::new();\n        match command.name.as_str() {\n            // get KEY: fetches the value of the given key, or None if it does not exist.\n            \"get\" =\u003e {\n                let mut args = command.consume_args();\n                let key = \u0026args.next_pos().ok_or(\"key not given\")?.value;\n                args.reject_rest()?;\n                let value = self.map.get(key);\n                writeln!(output, \"get → {value:?}\")?;\n            }\n\n            // insert KEY=VALUE...: inserts the given key/value pairs, returning the old value.\n            \"insert\" =\u003e {\n                let mut args = command.consume_args();\n                for arg in args.rest_key() {\n                    let old = self.map.insert(arg.key.clone().unwrap(), arg.value.clone());\n                    writeln!(output, \"insert → {old:?}\")?;\n                }\n                args.reject_rest()?;\n            }\n\n            // range [FROM] [TO]: iterates over the key/value pairs in the range from..to.\n            \"range\" =\u003e {\n                use std::ops::Bound::*;\n                let mut args = command.consume_args();\n                let from = args.next_pos().map(|a| Included(a.value.clone())).unwrap_or(Unbounded);\n                let to = args.next_pos().map(|a| Excluded(a.value.clone())).unwrap_or(Unbounded);\n                args.reject_rest()?;\n                for (key, value) in self.map.range((from, to)) {\n                    writeln!(output, \"{key}={value}\")?;\n                }\n            }\n\n            name =\u003e return Err(format!(\"invalid command {name}\").into()),\n        };\n        Ok(output)\n    }\n}\n\n\n#[test]\nfn btreemap() {\n    goldenscript::run(\u0026mut BTreeMapRunner::default(), \"btreemap\").expect(\"goldenscript failed\")\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferikgrinaker%2Fgoldenscript","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ferikgrinaker%2Fgoldenscript","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferikgrinaker%2Fgoldenscript/lists"}