{"id":20722038,"url":"https://github.com/FuzzyMonkeyCo/monkey","last_synced_at":"2025-05-10T23:32:22.008Z","repository":{"id":26420987,"uuid":"108859534","full_name":"FuzzyMonkeyCo/monkey","owner":"FuzzyMonkeyCo","description":"@FuzzyMonkeyCo's minion","archived":false,"fork":false,"pushed_at":"2025-02-06T15:59:20.000Z","size":1353,"stargazers_count":20,"open_issues_count":27,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-06T16:37:22.128Z","etag":null,"topics":["api","cli-application","exploratory-test-monkey","generative-testing","hacktoberfest","http","integration-testing","model-based-test","openapi","openapi-validation","property-based-testing","property-testing","swagger","test-automation","test-runners","testing","tests","unit-testing","validations"],"latest_commit_sha":null,"homepage":"https://fuzzymonkey.co","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FuzzyMonkeyCo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2017-10-30T14:07:47.000Z","updated_at":"2024-11-14T15:58:35.000Z","dependencies_parsed_at":"2024-02-13T16:29:47.924Z","dependency_job_id":"0979cd22-65e6-4c3d-bb1f-79527dde2f5f","html_url":"https://github.com/FuzzyMonkeyCo/monkey","commit_stats":{"total_commits":314,"total_committers":1,"mean_commits":314.0,"dds":0.0,"last_synced_commit":"3ac493a7f4b97df0baf38c9bbffe007e2669d5ee"},"previous_names":[],"tags_count":127,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FuzzyMonkeyCo%2Fmonkey","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FuzzyMonkeyCo%2Fmonkey/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FuzzyMonkeyCo%2Fmonkey/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FuzzyMonkeyCo%2Fmonkey/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FuzzyMonkeyCo","download_url":"https://codeload.github.com/FuzzyMonkeyCo/monkey/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253497304,"owners_count":21917683,"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":["api","cli-application","exploratory-test-monkey","generative-testing","hacktoberfest","http","integration-testing","model-based-test","openapi","openapi-validation","property-based-testing","property-testing","swagger","test-automation","test-runners","testing","tests","unit-testing","validations"],"created_at":"2024-11-17T03:33:59.326Z","updated_at":"2025-05-10T23:32:16.976Z","avatar_url":"https://github.com/FuzzyMonkeyCo.png","language":"Go","readme":"# [monkey](https://github.com/FuzzyMonkeyCo/monkey) ~ FuzzyMonkeyCo's minion [![Goreport card](https://goreportcard.com/badge/github.com/FuzzyMonkeyCo/monkey)](https://goreportcard.com/report/github.com/FuzzyMonkeyCo/monkey)\n\n[FuzzyMonkey](https://fuzzymonkey.co) is an automated API testing service that behaves as your users would and minimizes sequences of calls that lead to a violation of your software's properties.\n\n[monkey](https://github.com/FuzzyMonkeyCo/monkey) is the official open source client that executes the tests FuzzyMonkey generates.\n\n[![asciicast](https://asciinema.org/a/171571.png)](https://asciinema.org/a/171571?autoplay=1)\n\n```\nmonkey M.m.p go1.23.2 linux amd64\n\nUsage:\n  monkey [-vvv]           env [VAR ...]\n  monkey [-vvv] [-f STAR] fmt [-w]\n  monkey [-vvv] [-f STAR] lint [--show-spec]\n  monkey [-vvv] [-f STAR] exec (repl | start | reset | stop)\n  monkey [-vvv] [-f STAR] schema [--validate-against=REF]\n  monkey [-vvv] [-f STAR] fuzz [--intensity=N] [--seed=SEED]\n                               [--label=KV]...\n                               [--tags=TAGS | --exclude-tags=TAGS]\n                               [--no-shrinking]\n                               [--progress=PROGRESS]\n                               [--time-budget-overall=DURATION]\n                               [--only=REGEX]... [--except=REGEX]...\n                               [--calls-with-input=SCHEMA]...  [--calls-without-input=SCHEMA]...\n                               [--calls-with-output=SCHEMA]... [--calls-without-output=SCHEMA]...\n  monkey        [-f STAR] pastseed\n  monkey        [-f STAR] logs [--previous=N]\n  monkey [-vvv]           update\n  monkey                  version | --version\n  monkey                  help    | --help    | -h\n\nOptions:\n  -v, -vv, -vvv                   Debug verbosity level\n  -f STAR, --file=STAR            Name of the fuzzymonkey.star file\n  version                         Show the version string\n  update                          Ensures monkey is the latest version\n  --intensity=N                   The higher the more complex the tests [default: 10]\n  --time-budget-overall=DURATION  Stop testing after DURATION (e.g. '30s' or '5h')\n  --seed=SEED                     Use specific parameters for the Random Number Generator\n  --label=KV                      Labels that can help classification (format: key=value)\n  --tags=TAGS                     Only run checks whose tags match at least one of these (comma separated)\n  --exclude-tags=TAGS             Skip running checks whose tags match at least one of these (comma separated)\n  --progress=PROGRESS             dots, bar, ci (defaults: dots)\n  --only=REGEX                    Only test matching calls\n  --except=REGEX                  Do not test these calls\n  --calls-with-input=SCHEMA       Test calls which can take schema PTR as input\n  --calls-without-output=SCHEMA   Test calls which never output schema PTR\n  --validate-against=REF          Validate STDIN payload against given schema $ref\n  --previous=N                    Select logs from Nth previous run [default: 1]\n\nTry:\n     export FUZZYMONKEY_API_KEY=fm_42\n     export FUZZYMONKEY_SSL_NO_VERIFY=1\n  monkey update\n  monkey -f fm.star exec reset\n  monkey fuzz --only /pets --calls-without-input=NewPet --seed=$(monkey pastseed)\n  echo '\"kitty\"' | monkey schema --validate-against=#/components/schemas/PetKind\n```\n\n### Getting started\n\n**Recommended way:** using [the GitHub Action](https://github.com/FuzzyMonkeyCo/setup-monkey).\n\nQuick install:\n```shell\ncurl -#fL https://git.io/FuzzyMonkey | BINDIR=/usr/local/bin sh\n\n# or the equivalent:\ncurl -#fL https://raw.githubusercontent.com/FuzzyMonkeyCo/monkey/master/.godownloader.sh | BINDIR=/usr/local/bin sh\n```\n\nWith Docker:\n```shell\nDOCKER_BUILDKIT=1 docker build -o=/usr/local/bin --platform=local https://github.com/FuzzyMonkeyCo/monkey.git\n\n# or the faster:\nDOCKER_BUILDKIT=1 docker build -o=/usr/local/bin --platform=local --build-arg PREBUILT=1 https://github.com/FuzzyMonkeyCo/monkey.git\n```\n\nOr simply install the [latest release](https://github.com/FuzzyMonkeyCo/monkey/releases/latest).\n\n### Configuration\n\n`monkey` uses [Starlark](https://github.com/bazelbuild/starlark) as its configuration language: a simple Python-like deterministic language.\n\n#### Minimal example `fuzzymonkey.star` file\n\n\n```python\nOpenAPIv3(\n  name = \"dev_spec\",\n  file = \"openapi/openapi.yaml\",\n  host = \"http://localhost:3000\",\n\n  ExecReset = \"curl -fsSL -X DELETE http://localhost:3000/api/1/items\",\n)\n```\n\n#### Demos\n\n* [demo_erlang_cowboy_simpleREST](https://github.com/FuzzyMonkeyCo/demo_erlang_cowboy_simpleREST)\n\n#### A more involved [`fuzzymonkey.star`](./fuzzymonkey.star)\n\n```python\n# Invariants of our APIs expressed in a Python-like language\n\nassert that(monkey.env(\"TESTING_WHAT\", \"demo\")).is_equal_to(\"demo\")\nSPEC = \"pkg/runtime/testdata/jsonplaceholder.typicode.comv1.0.0_openapiv3.0.1_spec.yml\"\nprint(\"Using {}.\".format(SPEC))\n\nmonkey.openapi3(\n    name = \"my_spec\",\n    # Note: references to schemas in `file` are resolved relative to file's location.\n    file = SPEC,\n    host = \"https://jsonplaceholder.typicode.com\",\n)\n\n# Note: exec commands are executed in shells sharing the same environment variables,\n# with `set -e` and `set -o pipefail` flags on.\n\n# List here the commands to run so that the service providing \"my_spec\"\n# can be restored to its initial state.\nmonkey.shell(\n    name = \"example_resetter\",\n\n    # Link to above defined spec.\n    provides = [\"my_spec\"],\n\n    # The following gets executed once per test\n    #   so have these commands complete as fast as possible.\n    # For best results, tests should start with a clean slate\n    #   so limit filesystem access, usage of $RANDOM and non-reproducibility.\n    reset = \"\"\"\necho ${BLA:-42}\nBLA=$(( ${BLA:-42} + 1 ))\necho Resetting System Under Test...\n    \"\"\",\n)\n\n## Add headers to some of the requests\n\nMY_HEADER = \"X-Special\"\n\ndef add_special_headers(ctx):\n    \"\"\"Shows how to modify an HTTP request before it is sent\"\"\"\n\n    req = ctx.request\n    if type(req) != \"http_request\":\n        print(\"`ctx.request` isn't an HTTP request! It's a {}\", type(req))\n        return\n\n    assert that(MY_HEADER.title()).is_equal_to(MY_HEADER)\n    assert that(dict(req.headers)).does_not_contain_key(MY_HEADER)\n    req.headers.add(MY_HEADER, \"value!\")\n    print(\"Added an extra header:\", MY_HEADER)\n\n    # Let's also set a bearer token:\n    token = monkey.env(\"DEV_API_TOKEN\", \"dev token is unset!\")\n    req.headers.set(\"authorization\".title(), \"Bearer \" + token)\n\n    # Let's edit (a possibly-empty) body\n    if req.body == None:\n        req.body = {}\n    req.body[\"key\"] = 42\n\nmonkey.check(\n    name = \"adds_special_headers\",\n    before_request = add_special_headers,\n    tags = [\"special_headers\"],\n)\n\nmonkey.check(\n    name = \"checks_special_headers\",\n    after_response = lambda ctx: assert that(dict(ctx.request.headers)).contains_key(MY_HEADER),\n    tags = [\"special_headers\"],\n)\n\n## Ensure some general property\n\ndef ensure_lowish_response_time(ms):\n    def responds_in_a_timely_manner(ctx):\n        assert that(ctx.response).is_of_type(\"http_response\")\n        assert that(ctx.response.elapsed_ms).is_at_most(ms)\n\n    return responds_in_a_timely_manner\n\nmonkey.check(\n    name = \"responds_in_a_timely_manner\",\n    after_response = ensure_lowish_response_time(1000),\n    tags = [\"timings\"],\n)\n\n## Express stateful properties\n\ndef stateful_model_of_posts(ctx):\n    \"\"\"Properties on posts. State collects posts returned by API.\"\"\"\n    if type(ctx.request) != \"http_request\":\n        return\n\n    # NOTE: response has already been decoded \u0026 validated for us.\n\n    url = ctx.request.url\n\n    if all([\n        ctx.request.method == \"GET\",\n        \"/posts/\" in url and url[-1] in \"1234567890\",  # /posts/{post_id}\n        ctx.response.status_code in range(200, 299),\n    ]):\n        post_id = url.split(\"/\")[-1]\n        post = ctx.response.body\n\n        # Ensure post ID in response matches ID in URL (an API contract):\n        assert that(str(int(post[\"id\"]))).is_equal_to(post_id)\n\n        # Verify that retrieved post matches local model\n        if post_id in ctx.state:\n            assert that(post).is_equal_to(ctx.state[post_id])\n\n        return\n\n    if all([\n        ctx.request.method == \"GET\",\n        url.endswith(\"/posts\"),\n        ctx.response.status_code == 200,\n    ]):\n        # Store posts in state\n        for post in ctx.response.body:\n            post_id = str(int(post[\"id\"]))\n            ctx.state[post_id] = post\n        print(\"State contains {} posts\".format(len(ctx.state)))\n\nmonkey.check(\n    name = \"some_props\",\n    after_response = stateful_model_of_posts,\n)\n\n## Encapsulation: ensure each monkey.check owns its own ctx.state.\n\ndef encapsulation_1_of_2(ctx):\n    \"\"\"Show that state is not shared with encapsulation_2_of_2\"\"\"\n    assert that(ctx.state).is_empty()\n\nmonkey.check(\n    name = \"encapsulation_1_of_2\",\n    after_response = encapsulation_1_of_2,\n    tags = [\"encapsulation\"],\n)\n\nmonkey.check(\n    name = \"encapsulation_2_of_2\",\n    after_response = lambda ctx: None,\n    state = {\"data\": 42},\n    tags = [\"encapsulation\"],\n)\n\n## A test that always fails\n\ndef this_always_fails(ctx):\n    assert that(ctx).is_none()\n\nmonkey.check(\n    name = \"always_fails\",\n    after_response = this_always_fails,\n    tags = [\"failing\"],\n)\n```\n\n### Issues?\n\nReport bugs [on the project page](https://github.com/FuzzyMonkeyCo/monkey/issues) or [contact us](mailto:ook@fuzzymonkey.co).\n\n\n## License\n\nSee [LICENSE](./LICENSE)\n","funding_links":[],"categories":["[🔓 security](https://github.com/stars/ketsapiwiq/lists/unlock-security)"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFuzzyMonkeyCo%2Fmonkey","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FFuzzyMonkeyCo%2Fmonkey","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFuzzyMonkeyCo%2Fmonkey/lists"}