{"id":13741971,"url":"https://github.com/bats-core/bats-detik","last_synced_at":"2025-08-22T03:31:40.408Z","repository":{"id":38994340,"uuid":"193773519","full_name":"bats-core/bats-detik","owner":"bats-core","description":"A library to ease e2e tests of applications in K8s environments","archived":false,"fork":false,"pushed_at":"2024-02-26T12:47:08.000Z","size":229,"stargazers_count":138,"open_issues_count":2,"forks_count":15,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-02-26T13:53:56.808Z","etag":null,"topics":["kubernetes"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bats-core.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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":"2019-06-25T19:49:55.000Z","updated_at":"2024-06-03T08:13:43.320Z","dependencies_parsed_at":"2022-09-02T21:42:31.650Z","dependency_job_id":"d5d4b3c9-3180-44ff-a38a-6b0dd9233865","html_url":"https://github.com/bats-core/bats-detik","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bats-core%2Fbats-detik","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bats-core%2Fbats-detik/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bats-core%2Fbats-detik/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bats-core%2Fbats-detik/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bats-core","download_url":"https://codeload.github.com/bats-core/bats-detik/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230554330,"owners_count":18244234,"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":["kubernetes"],"created_at":"2024-08-03T04:01:04.705Z","updated_at":"2025-08-22T03:31:40.385Z","avatar_url":"https://github.com/bats-core.png","language":"Shell","funding_links":[],"categories":["Tools and Libraries","Shell"],"sub_categories":["Testing and Troubleshooting"],"readme":"# DETIK: DevOps e2e Testing in Kubernetes\n[![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/bats-core/bats-detik/blob/master/LICENSE)\n![Build status](https://github.com/bats-core/bats-detik/actions/workflows/test.yml/badge.svg)\n\nThis repository provides utilities to **execute end-to-end tests** of applications in Kubernetes clusters. This includes performing actions on the cluster (with kubectl, oc - for OpenShift - or helm) and verifying assertions by using a natural language, or almost. This reduces the amount of advanced bash commands to master.\n\nThis kind of test is the ultimate set of verifications to run for a project, long after unit and integration tests. In fact, it is the last part of a pipeline for a project or a Helm package. The major assumption done here is that you have a test cluster, or at least a non-production one, to execute these tests.\n\n\u003e This tooling is inspired from [Pierre Mavro's article](https://blog.deimos.fr/2019/02/08/k8s-euft-run-functional-tests-on-your-helm-charts/), in particular for the BATS approach. However, it has the ambition of making such tests more simple to write. And it does not deal with the deployment of a K8s cluster.\n\n\n## Table of Contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\u003c!-- param::isNotitle::true:: --\u003e\n\n- [Objectives](#objectives)\n- [Examples](#examples)\n  - [Test files and result](#test-files-and-result)\n  - [Working with Kubectl or OC commands](#working-with-kubectl-or-oc-commands)\n  - [Other Examples](#other-examples)\n- [Usage](#usage)\n  - [Manual Setup](#manual-setup)\n  - [Docker Setup](#docker-setup)\n  - [Continuous Integration](#continuous-integration)\n- [Syntax Reference](#syntax-reference)\n  - [Counting Resources](#counting-resources)\n  - [Verifying Property Values](#verifying-property-values)\n  - [Using Regular Expressions](#using-regular-expressions)\n  - [Property Names](#property-names)\n- [Errors](#errors)\n  - [Error Codes](#error-codes)\n  - [Debugging Tests](#debugging-tests)\n  - [Linting](#linting)\n  - [Tips](#tips)\n- [Beyond K8s assertions](#beyond-k8s-assertions)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n\n## Objectives\n\n* Execute Helm / kubectl / oc commands and verify assertions on their output.\n  * Example: get the right number of POD, make sure they are READY, etc.\n* Execute application scenarios: \n  * Example: access a login page and follow a complex UI scenario (e.g. with [Selenium](https://www.seleniumhq.org/)).\n  * Example: simulate events (e.g. the loss of a POD instance) and verify everything keeps on working.\n  * Example: be able to play performance tests for a given configuration.\n* Organize all the tests in scenarios.\n* Obtain an execution report at the end.\n\n\n## Examples\n\n### Test files and result\n\nThis section shows how to write unit tests using this library and [BATS](https://github.com/bats-core/bats-core).\n\n```bash\n#!/usr/bin/env bats\n\nload \"lib/utils\"\nload \"lib/detik\"\n\nDETIK_CLIENT_NAME=\"kubectl\"\n\n@test \"verify the deployment\" {\n\t\n\trun kubectl apply -f my-big-deployment-file.yml\n\t[ \"$status\" -eq 0 ]\n\t\n\tsleep 20\n\t\n\trun verify \"there are 2 pods named 'nginx'\"\n\t[ \"$status\" -eq 0 ]\n\t\n\trun verify \"there is 1 service named 'nginx'\"\n\t[ \"$status\" -eq 0 ]\n\t\n\trun try \"at most 5 times every 30s to find 2 pods named 'nginx' with 'status' being 'running'\"\n\t[ \"$status\" -eq 0 ]\n\t\n\trun try \"at most 5 times every 30s to get pods named 'nginx' and verify that 'status' is 'running'\"\n\t[ \"$status\" -eq 0 ]\n}\n\n\n@test \"verify the undeployment\" {\n\t\n\trun kubectl delete -f my-big-deployment-file.yml\n\t[ \"$status\" -eq 0 ]\n\t\n\tsleep 20\n\t\n\trun try \"at most 5 times every 5s to find 0 pod named 'nginx' with 'status' being 'running'\"\n\t[ \"$status\" -eq 0 ]\n\t\n\trun verify \"there is 0 service named 'nginx'\"\n\t[ \"$status\" -eq 0 ]\n}\n```\n\nExecuting **bats my-tests.bats** would result in the [following output](doc/success.gif)...\n\n```ada\nbats my-tests.bats\n1..2\n✓ 1 verify the deployment\n✓ 2 verify the undeployment\n\nThe command \"bats my-tests.bats\" exited with 0.\n```\n\nIn case of error, [it would show](doc/failure.gif)...\n\n```ada\nbats my-tests.bats\n1..2\n✗ 1 verify the deployment\n    (in test file my-tests.bats, line 14)\n     `[ \"$status\" -eq 0 ]' failed\n \n✓ 2 verify the undeployment\n\nThe command \"bats my-tests.bats\" exited with 1.\n```\n\nSince this project works with BATS, you can use **setup** and **teardown**\nfunctions to prepare and clean after every test in a file.\n\n\n### Working with Kubectl or OC commands\n\nIf you are working with a native Kubernetes cluster.\n\n```bash\nload \"lib/utils\"\nload \"lib/detik\"\n\n# The client function\nDETIK_CLIENT_NAME=\"kubectl\"\n\n# If you want to work in a specific namespace.\n# If not set, queries will be run in the default namespace.\nDETIK_CLIENT_NAMESPACE=\"my-specific-namespace\"\n\n# If instead you want to target all the namespaces,\n# do not set DETIK_CLIENT_NAMESPACE. Instead, use:\n# DETIK_CLIENT_NAMESPACE_ALL=\"true\"\n\n# Verify the number of PODS and services\nverify \"there are 2 pods named 'nginx'\"\nverify \"there is 1 service named 'nginx'\"\nverify \"there are less than 3 pods named 'nginx'\"\nverify \"there are more than 1 pods named 'nginx'\"\n\n# Verify assertions on resources\nverify \"'status' is 'running' for pods named 'nginx'\"\nverify \"'port' is '8484' for services named 'nginx'\"\nverify \"'.spec.ports[*].targetPort' is '8484' for services named 'nginx'\"\n\n# You can also specify a number of attempts\ntry \"at most 5 times every 30s to get pods named 'nginx' and verify that 'status' is 'running'\"\ntry \"at most 5 times every 30s to get svc named 'nginx' and verify that '.spec.ports[*].targetPort' is '8484'\"\ntry \"at most 5 times every 30s to get deployment named 'nginx' and verify that 'status.currentReplicas' is more than '2'\"\ntry \"at most 5 times every 30s to get deployment named 'nginx' and verify that 'status.currentReplicas' is less than '4'\"\n\n# Long assertions can also be split over several lines\ntry \"at most 5 times every 30s \" \\\n    \"to get svc named 'nginx' \" \\\n    \"and verify that '.spec.ports[*].targetPort' is '8484'\"\n    \n# You can also use an altered syntax, without global quotes.\n# Be careful, you must then add double quotes around single ones.\ntry at most 5 times every 30s \\\n    to get svc named \"'nginx'\" \\\n    and verify that \"'.spec.ports[*].targetPort'\" is \"'8484'\"\n\n# Regular expressions can also be used\ntry at most 5 times every 30s \\\n\tto get svc named 'nginx' \\\n\tand verify that '.spec.ports[*].targetPort' matches '[[:digit:]]+'\n```\n\nIf you work with OpenShift and would prefer to use **oc** instead of **kubectl**...\n\n```bash\nload \"lib/utils\"\nload \"lib/detik\"\n\n# The client function\nDETIK_CLIENT_NAME=\"oc\"\n\n# Verify the number of PODS and services\nverify \"there are 2 pods named 'nginx'\"\n```\n\n\n### Other Examples\n\nExamples are available under [the eponym directory](examples/ci).  \nIt includes...\n\n* Library usage\n* Tests for a Helm package\n* Pipeline / CI integrations\n\n\n## Usage\n\n### Manual Setup\n\n* Install [BATS](https://github.com/bats-core/bats-core), a testing framework for scripts.  \nBATS is a test framework for BASH and other scripts.\n* Download the **lib/detik.bash** script.\n\n```bash\nwget https://raw.githubusercontent.com/bats-core/bats-detik/master/lib/detik.bash\nwget https://raw.githubusercontent.com/bats-core/bats-detik/master/lib/linter.bash\nwget https://raw.githubusercontent.com/bats-core/bats-detik/master/lib/utils.bash\nchmod +x *.bash\n```\n\n* Write BATS scripts with assertions.  \nMake sure they import the **lib/utils.bash** and **lib/detik.bash** files.\n* Import the **lib/linter.bash** file to verify the linting of DETIK assertions.\n* Use the BATS command to run your tests: `bats sources/tests/main.bats`\n\n\n### Docker Setup\n\nThis project does not provide any official Docker image.  \nThis is because you may need various clients (kubectl, oc, kustomize...\nwhatever) and it all depends on your requirements.\n\nA sample Dockerfile is provided in this project.  \nTo build a Docker image from it:\n\n```bash\n# Tag it with LATEST\ndocker build -t bats/bats-detik:LATEST .\n\n# Overwrite the default versions\ndocker build \\\n\t--build-arg KUBECTL_VERSION=v1.21.2 \\\n\t--build-arg HELM_VERSION=v3.6.1 \\\n\t--build-arg BATS_VERSION=1.3.0 \\\n\t-t bats/bats-detik:LATEST \\\n\t.    \n```\n\nOn a development machine, you can use it this way:\n\n```bash\n# Run the image with a volume for your project.\n# In this example, we show how to specify the proxy\n# if your organization is using one.\ndocker run -ti \\\n\t-v $(pwd):/home/testing/sources \\\n\t-e http_proxy=\"proxy.local:3128\" \\\n\t-e https_proxy=\"proxy.local:3128\" \\\n\tbats-detik:LATEST\n\n# Log into the cluster\necho \"It all depends on your cluster configuration\"\n\n# Export the namespace for Helm (v2)\n# export TILLER_NAMESPACE=\u003cyour namespace\u003e\n\n# Execute the tests\nbats sources/tests/main.bats\n```\n\nIt can also be used in a continuous integration platform.\n\n\n### Continuous Integration\n\nAn example is given for Jenkins in [the examples](examples/ci).  \nThe syntax is quite simple and may be easily adapted for other solutions, such as GitLab CI, Tracis CI, etc.\n\n\n## Syntax Reference\n\nFor all the available sentences, both a resource name and a resource type are expected.  \nThe resource name can be a simple string (e.g. `nginx`) or a regular expression\n(e.g. `^nginx-[a-z0-9]{9,10}-[a-z0-9]{5}\\$`), while the resource type is one of\nthe K8s ones (e.g. `pods`, `po`, `services`, `svc`...) or a CRD. See\n[https://kubernetes.io/docs/reference/kubectl/overview/#resource-types](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types) for a complete reference of the official resources. The available custom resources (e.g.\n`settings.management.cattle.io`) will depend on your cluster setup.\n\n\n### Counting Resources\n\nVerify there are N resources of this type with this name pattern.\n\n```bash\n# Expecting 0 or 1 instance\nverify \"there is \u003c0 or 1\u003e \u003cresource-type\u003e named '\u003cregular-expression\u003e'\"\n\n# Expecting \u003cnumber\u003e instances\nverify \"there are \u003cnumber\u003e \u003cresource-type\u003e named '\u003cregular-expression\u003e'\"\n\n# Expecting more than \u003cnumber\u003e instances\nverify \"there are more than \u003cnumber\u003e \u003cresource-type\u003e named '\u003cregular-expression\u003e'\"\n\n# Expecting less than \u003cnumber\u003e instances\nverify \"there are less than \u003cnumber\u003e \u003cresource-type\u003e named '\u003cregular-expression\u003e'\"\n```\n\n\u003e :warning: This simple assertion may fail sometimes.\n\u003e\n\u003e As an example, if you count the number of PODs, run your test and then kill the POD, they will still be listed, with the `TERMINATING` state.\n\u003e\n\u003e So, most of the time, you will want to verify the number of instances with a given property value. Example: count the number of PODs with a given name pattern and having the `running` status.\n\nHence this additional syntax (using [next section](#verifying-property-values) documentation to verify additionnal properties):\n\n```bash\n# Expecting a given number of instances\ntry \"at most \u003cnumber\u003e times every \u003cnumber\u003es \\\n\tto find \u003cnumber\u003e \u003cresource-type\u003e named '\u003cregular-expression\u003e' \\\n\twith '\u003cproperty-name\u003e' being '\u003cexpected-value\u003e'\"\n```\n\n:pushpin: This assertion is useful for PODs, whose life cycle changes take time.  \nFor services, you may directly use the simple count assertions.\n\nThis is a checking loop.\nIt breaks the loop if as soon as the assertion is verified. If it reaches the end of the loop without having been verified, an error is thrown. Please, refer to [this section](#property-names) for details about the property names.\n\n\n### Verifying Property Values\n\nVerify the property of a set of resources of this type with this name pattern.\n\n```bash\nverify \"'\u003cproperty-name\u003e' is '\u003cexpected-value\u003e' for \u003cresource-type\u003e named '\u003cregular-expression\u003e'\"\n```\n\nAttempt to verify the property of a set of resources of this type with this name pattern.\n\n```bash\ntry \"at most \u003cnumber\u003e times every \u003cnumber\u003es \\\n\tto get \u003cresource-type\u003e named '\u003cregular-expression\u003e' \\\n\tand verify that '\u003cproperty-name\u003e' is '\u003cexpected-value\u003e'\"\n```\n\nThis is a checking loop.  \nIt breaks the loop if as soon as the assertion is verified. If it reaches the end of the loop\nwithout having been verified, an error is thrown. Please, refer to [this section](#property-names) for details\nabout the property names.\n\n:memo: This assertion verifies _all the instances_ have this property value.\nBut unlike the assertion type to [count resources](#counting-resources), you do not verify _how many instances_ have this value. Notice however that **if it finds 0 item verifying the property, the assertion fails**.\n\n\n### Using Regular Expressions\n\nIt is also possible to verify property values against a regular expression.  \nThis can be used, as an example, to verify values in a JSON array.\n\n```bash\n# Verifying a property\nverify \"'\u003cproperty-name\u003e' matches '\u003cregular-experession\u003e' for \u003cresource-type\u003e named '\u003cregular-expression\u003e'\"\n\n# Finding elements with a matching property\ntry \"at most \u003cnumber\u003e times every \u003cnumber\u003es \\\n\tto get \u003cresource-type\u003e named '\u003cregular-expression\u003e' \\\n\tand verify that '\u003cproperty-name\u003e' matches '\u003cregular-experession\u003e'\"\n\n# Counting elements with a matching property\ntry \"at most \u003cnumber\u003e times every \u003cnumber\u003es \\\n\tto find \u003cnumber\u003e \u003cresource-type\u003e named '\u003cregular-expression\u003e' \\\n\twith '\u003cproperty-name\u003e' matching '\u003cregular-expression\u003e'\"\n```\n\nThe regular expression used for property values relies on\n[BASH regexp](https://en.wikibooks.org/wiki/Regular_Expressions/POSIX-Extended_Regular_Expressions).\nMore exactly, it uses extended regular expressions (EREs). You can simulate the result of such an assertion\nwith `grep`, as it is the command used internally. Hence, you can use `echo your-value | grep -E your-regex`\nto prepare your assertions.\n\n\u003e Unlike the assertions with the verb « to be », those with the verb « to match » are case-sensitive.\n\nAll the assertions using the verb « to be » make case-insensitive comparison.  \nIt means writing `is 'running'` or `is 'Running'` does not change anything.\nIf you want case-sensitive equality, then use a regular expression, i.e. write\n`matches '^Running$'`.\n\nIf for some reasons, one needs case-insensitive matches, you can set the DETIK_CASE_INSENSITIVE_PROPERTIES\nproperty to `true` in your test. All the retrieved values by the DETIK_CLIENT will be lower-cased. It means\nyou can write a pattern that only considers lower-case characters. The following sample illustrates this situation:\n\n```bash\n# Assuming the status of the POD is \"Running\"...\n# ... then the following assertion will fail.\nverify \"'status' matches 'running' for pods named 'nginx'\"\n\n# Same for...\nverify \"'status' matches '^running$' for pods named 'nginx'\"\n\n# This is because the value returned by the client starts with an upper-case letter.\n# For case-insensivity operations with a regular expression, just use...\nDETIK_CASE_INSENSITIVE_PROPERTIES=\"true\"\nverify \"'status' matches 'running' for pods named 'nginx'\"\n\n# The assertion will now be verified.\n# Just make sure the pattern ONLY INCLUDES lower-case characters.\n\n# If you set DETIK_CASE_INSENSITIVE_PROPERTIES directly in a \"@test\" function,\n# there is no need to reset it for the other tests. Its scope is limited to the\n# function that defines it. It is recommended to NOT make this variable a global one.\n```\n\n\n### Property Names\n\nIn all assertions, *property-name* is one of the column names supported by K8s.  \nSee https://kubernetes.io/docs/reference/kubectl/overview/#custom-columns  \nYou can also find column names by using `kubectl get \u003cresource-type\u003e -o custom-columns=ALL:*`.\n\nTo ease the writing of assertions, some aliases are proposed by the library.\n\n| Alias        | Target Property           | Useful For |\n| ------------ | ------------------------- | :--------: |\n| status       | .status.phase             | PODS       |\n| port         | .spec.ports[*].port       | Services   |\n| targetPort   | .spec.ports[*].targetPort | Services   |\n\nOther aliases may appear later.\n\n\n## Errors\n\n### Error Codes\n\nAll the functions rely on the same convention.\n\n| Exit Code | Meaning |\n| --------- | ------- |\n|     0     | Everything is fine. |\n|     1     | The query for the function was empty. |\n|     2     | The query did not respect the syntax. |\n|     3     | The assertion could not be verified when the function returned. It may also indicate an error with the K8s client. |\n\n\n### Debugging Tests\n\nThere is a **debug** function in DETIK.  \nYou can use it in your own tests. Debug traces are stored into **/tmp/detik/**.\nThere is one debug file per test file.\n\nIt is recommended to reset this file at the beginning of every test file.\n\n```bash\n#!/usr/bin/env bats\n\nload \"lib/utils\"\nload \"lib/detik\"\n\n\n# Improve readability of the debug file\nsetup() {\n\tdebug \"\"\n\tdebug  \"\"\n\tdebug  \"-- $BATS_TEST_DESCRIPTION --\"\n\tdebug  \"\"\n\tdebug  \"\"\n}\n\n\n@test \"reset the debug file\" {\n\t# This function is part of DETIK too\n\treset_debug\n}\n\n\n@test \"run my first test\" {\n\n\t# Make an assertion and output the result in the debug file.\n\trun verify ...\n\tdebug \"Command output is: $output\"\n\t[ \"$status\" -eq 0 ]\n\t\n\t# ...\n}\n```\n\nDETIK debug messages are silenced by default.  \nTo enable them, you have to set the **DEBUG_DETIK** variable. In addition to your\nown debug traces, you will see the ones from DETIK and/or its linter.\n\nHere is an example showing how to debug DETIK with a test.\n\n```bash\n# Enable the debug flag\nDEBUG_DETIK=\"true\"\nrun verify \"'status' is 'running' for pods named 'nginx'\"\n\n# Even if you did not log anything, DETIK did.\n# Find the debug file under /tmp/detik.\n\n# Reset the debug flag\nDEBUG_DETIK=\"\"\n```\n\n\n### Linting\n\nDespite the efforts to make the DETIK syntax as simple as possible, BASH remains a non-compiled\nlanguage and mistakes happen. To prevent them, a linter was created to help locating\nsyntax errors when writing DETIK assertions. You can use it with BATS in your tests.\n\n```bash\n#!/usr/bin/env bats\nload \"lib/utils\"\nload \"lib/linter\"\n\n@test \"lint assertions\" {\n\n\trun lint \"tests/my-tests-1.bats\"\n\t# echo -e \"$output\" \u003e /tmp/errors.txt\n\t[ \"$status\" -eq 0 ]\n\t\n\trun lint \"tests/my-tests-2.bats\"\n\t# echo -e \"$output\" \u003e /tmp/errors.txt\n\t[ \"$status\" -eq 0 ]\n}\n```\n\n\n### Tips\n\n1. **Do not use file descriptors 3 and 4 in your tests.**  \nThey are already used by BATS. And 0, 1 and 2 are default file descriptors. Use 5, 6 and higher values.\n\n\n## Beyond K8s assertions\n\nEnd-to-end tests may involve much more than K8s assertions.  \nBATS and DETIK can be combined with other testing frameworks, such\nas performance tests (e.g. with [Gatling](https://gatling.io) and\n[JMeter](https://jmeter.apache.org)) or functional scenarios with user\ninteractions (e.g. with [Selenium](https://www.selenium.dev),\n[Cypress](https://www.cypress.io) or [Robot Framework](https://robotframework.org/)).\n\nTo do so, you would need to have these tools colocated (in a same\nVM, container or POD). Your BATS scenarios would then invoke the various\ntools and verify assertions with BATS. Sky is the limit then: it all\ndepends on what you want to test.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbats-core%2Fbats-detik","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbats-core%2Fbats-detik","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbats-core%2Fbats-detik/lists"}