{"id":38543513,"url":"https://github.com/harpyd/thestis","last_synced_at":"2026-01-17T07:15:30.072Z","repository":{"id":38108831,"uuid":"423095483","full_name":"harpyd/thestis","owner":"harpyd","description":"Thestis is a service for auto tests with a declarative description of tests","archived":false,"fork":false,"pushed_at":"2022-06-28T20:15:06.000Z","size":4179,"stargazers_count":3,"open_issues_count":16,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2024-11-14T13:59:21.888Z","etag":null,"topics":["bdd","ci","clean-architecture","cqrs","ddd","infrastructure","integration-testing","mongodb","openapi-specification","pipeline","test","testing","tests"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/harpyd.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}},"created_at":"2021-10-31T08:42:59.000Z","updated_at":"2022-06-10T07:21:55.000Z","dependencies_parsed_at":"2022-08-24T05:31:01.274Z","dependency_job_id":null,"html_url":"https://github.com/harpyd/thestis","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/harpyd/thestis","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harpyd%2Fthestis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harpyd%2Fthestis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harpyd%2Fthestis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harpyd%2Fthestis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/harpyd","download_url":"https://codeload.github.com/harpyd/thestis/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harpyd%2Fthestis/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28503297,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T06:57:29.758Z","status":"ssl_error","status_checked_at":"2026-01-17T06:56:03.931Z","response_time":85,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["bdd","ci","clean-architecture","cqrs","ddd","infrastructure","integration-testing","mongodb","openapi-specification","pipeline","test","testing","tests"],"created_at":"2026-01-17T07:15:29.205Z","updated_at":"2026-01-17T07:15:30.054Z","avatar_url":"https://github.com/harpyd.png","language":"Go","readme":"# thestis\n\n\u003ca href=\"https://github.com/harpyd/thestis/blob/main/api/openapi/thestis-v1.yml\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/109659/40094839-2bc8f2ee-5897-11e8-8092-583c26e4d0df.png\" width=\"30\" height=\"30\" alt=\"OpenAPI specification\"/\u003e\u003c/a\u003e\n[![codecov](https://codecov.io/gh/harpyd/thestis/branch/main/graph/badge.svg?token=3JN40B9T6H)](https://codecov.io/gh/harpyd/thestis)\n\n___\n\n__Thestis__ is a service for auto tests with declarative description of tests\n\n## Sequence diagram\n\n```mermaid\nsequenceDiagram\n    User-\u003e\u003e+Thestis: Create test campaign POST /v1/test-campaigns\n    Thestis--\u003e\u003e-User: Return test campaign id in Location HTTP header\n    User-\u003e\u003e+Thestis: Get test campaign by id GET /v1/test-campaigns/{id}\n    Thestis--\u003e\u003e-User: Return test campaign data\n    User-\u003e\u003e+Thestis: Load active specification in test campaign POST /v1/test-campaigns/{id}/specification\n    Thestis--\u003e\u003e-User: Return specification id in Location HTTP header\n    User-\u003e\u003e+Thestis: Get specification by id GET /v1/specifications/{id}\n    Thestis--\u003e\u003e-User: Return specification data\n    User-\u003e\u003e+Thestis: Start pipeline by test campaign id POST /v1/test-campaigns/{id}/pipelines\n    Thestis--\u003e\u003e-User: Return pipeline id in Location HTTP header\n    Thestis-\u003e\u003e+Thestis: Acquire pipeline and start it as parallel task\n    loop Restart tries\n        User-\u003e\u003e+Thestis: Restart pipeline by id PUT /v1/pipelines/{id}\n        Thestis--\u003e\u003e-User: Already started pipeline 409 Conflict\n    end\n    Thestis--\u003e\u003e-Thestis: Release pipeline and complete parallel task\n    User-\u003e\u003e+Thestis: Restart pipeline by id PUT /v1/pipelines/{id}\n    Thestis--\u003e\u003e-User: Pipeline restarted\n    Thestis-\u003e\u003e+Thestis: Acquire pipeline and start it as parallel task\n    User--\u003e\u003e+Thestis: Cancel pipeline by id PUT /v1/pipelines/{id}/canceled\n    Thestis--\u003e\u003e-User: Pipeline canceled\n    Thestis--\u003e\u003e-Thestis: Release pipeline and cancel parallel task\n```\n\n## Description\n\nImagine any CI/CD pipeline. For example you can pay attention to Github Actions. You must write workflow with\ndeclarative pipeline description and push it to remote. If you have specified `on.push` parameter, action with\nsatisfying workflow will be started if you push. Or you can manually start Action pipeline. Pipeline may include linter,\nnotifications, unit tests, integration tests, building, deploying, etc...\n\n__Thestis__ is integration tests pipeline for e2e tests. So far pipeline may include 2 thesis types, HTTP and assertion.\n\nUsually, quite a lot of code is written for integration tests in one of the appropriate programming languages, it can be\nJava, Python, Go, etc. I've been on several development teams. Each team used something of its own, someone pure Python,\nsomeone wrote their own project-oriented framework for testing on Python, someone wrote tests using Java, each project\nhad its own integration tests, in the language of the project.\n\nThese tests could contain some errors, for example, in a self-written framework that I saw at work, the mapping of JSON\nfields into the structure was incorrectly written.\n\nOther tests did not use competitive execution, which is why they were slower than they could.\n\nIn others, the code seemed to be correct, but it did not contain the business sense that the manager or analyst wanted,\nand, unfortunately, they do not understand the code.\n\nIn others, the code is constantly flapping, no one knows why, and does not want to engage, because the code is scary.\n\nIn general, there were enough problems. That's how the idea of a pipeline for e2e tests appeared. To get started, you\nneed to create a `TestCampaign`.\n\nIn fact, a `TestCampaign` is the name of your test, information about it, and the history of all\nuploaded `Specifications` and completed `Pipelines`.\n\n`Specification` is a declarative description of the test in BDD style, each test consists of _stories_, each `Story`\nof _scenarios_, each `Scenario` of _theses_. The `Thesis` contains a description of the work of part of the test.\n\n```\n\"Returns go to stock\" — example of story\n\n\"Refunded items should be returned to stock\" — example of scenario\n```\n\nThe thesis can be either `given`, or `when`, or `then`:\n\n```\n\"Given that a customer previously bought a blue garment from me and I have two blue garments in stock and three black garments in stock\" — example of given thesis\n\n\"When they return the blue garment for a replacement in black\" — example of when thesis\n\n\"Then I should have three blue garments in stock and two black garments in stock\" — example of then thesis\n```\n\nYou can see an example of the `Specification` at the\nlink [here](https://github.com/harpyd/thestis/tree/main/examples/specification).\n\nWhen you trigger the pipeline launch in some way, a `Pipeline` is created. The `Pipeline` is a kind of collected context\nabout the test at the time of\nlaunch from the active `Specification` of `TestCampaign`. Somewhat similar to Github Action.`Pipeline` collects the\nentire dynamic context and the state of the current startup in the `Flow`.\n\n`Flow` is an analog of an attempt at Github Action. Stores information about the launch of `Pipeline`. `Pipeline`\nwill always have at least one `Flow`. `Pipeline` can be restarted (for example, if a test fails), each time\nthe `Pipeline` is restarted, the number of `Flows` will increase.\n\nDuring the test run, each individual thesis execution status may end up in one of the states:\n\n* __`NotExecuted`__\n* __`Executing`__\n* __`Passed`__\n* __`Failed`__\n* __`Crashed`__\n* __`Canceled`__\n\nIf the test is __`NotExecuted`__, the test has not started yet for some reason. If the test is in __`Executing`__,\nthen you should expect it to end. If you are in __`Passed`__, you can relax, because the test is passed! If the test is\nin __`Failed`__ state, it is worth looking at either the test or the system under the tests. If something went wrong\nin __`Crashed`__, perhaps from the network, or maybe from our side. If it is __`Canceled`__, then the test was canceled,\nit is possible that you canceled it, and it is possible that we did too because of too long execution.\n\nIt is worth noting that the tests achieve the most effective parallelization of the independent parts of the test. How?\nSee below.\n\n## Entities\n\n### Test campaign\n\n`TestCampaign` is your test, its whole history. You can compare it with a test campaign of some brand like Coca-Cola, a\nseries of successful and not very successful tests.\n\nContains general information about the test and user information. Each `Specification` update and `Pipeline` launch\nis associated with this entity. A `TestCampaign` can have only one active `Specification`, the rest are archived. You\ncan also get a list of all `Pipelines` launched within the campaign.\n\n### Specification\n\n`Specification` is your code for the test. This entity can be collected from various sources, now, for example, in the\nAPI we get a specification in yaml format, but this is all changeable. `Specification` is format tolerant, any format\nwill be converted to the internal format.\n\nStories, scenarios, and theses (_slugged_ objects) have unique identifiers called _slugs_. `Slug` can consist of 1 to 3\nparts. The slug of the story consists of one part. Script slug of 2 parts: story part and scenario part. The thesis slug\nconsists of 3 parts: story part, scenario part ant thesis part.\n\nFormally, each `Specification` consists of _stories_, each `Story` consists of _scenarios_, and each `Scenario` consists\nof _theses_.\n\n`Specification` is described in BDD style, each working step of the test is described in `Thesis`. Each thesis can\neither make __HTTP__ requests or __assertion__ of the collected data.\n\nBDD tests consist of `given`, `when` and `then` stages. The stages are performed sequentially:\n\n```mermaid\nflowchart LR\n    given --\u003e when\n    when --\u003e then\n```\n\nBut theses within one stage will be executed in parallel by default. To specify a dependency, specify the name of the\nthesis in the `after` field. Then this thesis will be fulfilled after the specified one.\n\n### Pipeline\n\n`Pipeline` is the pipeline of your tests built from `Specification`. It starts automatically when it is created. It\ncan also be restarted. For example, you can see that the test fell through no fault of your own, for example, there was\nsome kind of network failure, you can restart the previously created `Pipeline`.\n\nWhen creating a pipeline for specification, _executors_ for each type of thesis are registered. `Executor` receives\nthe thesis, executes an action with it and returns `Result` with `Event` generated inside it for this thesis.\n\nThere may be several events:\n\n* __`FiredExecute`__\n* __`FiredPass`__\n* __`FiredFail`__\n* __`FiredCrash`__\n* __`FiredCancel`__\n\n`Pipeline` cannot be run more than once at any given time. That is, `Pipeline` will never have more than one\nactive `Flow`.\n\nDuring `Pipeline`, each `Scenario` is executed in parallel with `Environment` isolated from other scenarios, and for\neach scenario its own `ScenarioSyncGroup` is created to manage the dependencies of each thesis.\n\nView of `Pipeline`'\ns [example](https://github.com/harpyd/thestis/blob/main/examples/specification/horns-and-hooves-test.yml) flow:\n\n```mermaid\nflowchart TD\n    sellHornsAndHoovesOnTheMarket.sellExistingHornsAndHooves.deliverHorns --\u003e sellHornsAndHoovesOnTheMarket.sellExistingHornsAndHooves.sellHornsAndHooves\n    sellHornsAndHoovesOnTheMarket.sellExistingHornsAndHooves.deliverHooves --\u003e sellHornsAndHoovesOnTheMarket.sellExistingHornsAndHooves.sellHornsAndHooves\n\n    sellHornsAndHoovesOnTheMarket.sellExistingHornsAndHooves.sellHornsAndHooves --\u003e sellHornsAndHoovesOnTheMarket.sellExistingHornsAndHooves.getSoldProducts\n    sellHornsAndHoovesOnTheMarket.sellExistingHornsAndHooves.getSoldProducts --\u003e sellHornsAndHoovesOnTheMarket.sellExistingHornsAndHooves.checkSoldProducts\n    sellHornsAndHoovesOnTheMarket.sellExistingHornsAndHooves.sellHornsAndHooves --\u003e sellHornsAndHoovesOnTheMarket.sellExistingHornsAndHooves.checkSoldProducts\n```\n\nAt each launch, `Pipeline` gives information about the execution step by step. Then, from the __steps__ received from\nthe pipeline, a flow is collected, showing the current state of the active pipeline's flow.\n\n### Flow\n\n`Flow` is unit of `Pipeline` work. Every working pipeline's parallel task accumulates information about the\nprogress of the pipeline and the context in this entity. Each run of `Pipeline` corresponds to one `Flow`.\n\n`Flow` reduced from _steps_ received during the execution of `Pipeline`.\n\n`Flow` consists of _statuses_ (each with `Slug`, `State` and occurred errors). Every `Status` has state that\nrepresents __slugged__ `Specification` objects pipeline progress.\n\n`State` changes under the action of an `Event` and _events_ are generated during the operation of `Pipeline`. For\nexample, if the thesis is passed, then the pipeline creates a step for this thesis with an event, and if all the\ntheses of the scenario are passed, then the pipeline creates a step for the scenario with the same event.\n\nFrom each state, you can go to a certain set of states:\n\n```mermaid\nstateDiagram-v2\n    Canceled --\u003e Canceled\n\n    Crashed --\u003e Crashed\n\n    Failed --\u003e Failed\n    Failed --\u003e Crashed\n\n    Passed --\u003e Passed\n    Passed --\u003e Failed\n    Passed --\u003e Crashed\n\n    Executing --\u003e Executing\n    Executing --\u003e Passed\n    Executing --\u003e Failed\n    Executing --\u003e Crashed\n    Executing --\u003e Canceled\n\n    NotExecuted --\u003e Executing\n    NotExecuted --\u003e Passed\n    NotExecuted --\u003e Failed\n    NotExecuted --\u003e Crashed\n    NotExecuted --\u003e Canceled\n```\n\n### User\n\n`User` has knowledge about which resources can be accessed and which can be managed.\n\n## Architecture\n\nThis project is written using the approaches described in Clean Architecture of Uncle Bob.\n\nThe __entity__ contains entities for creating tests, loading specification, creating and starting pipeline with access\nrights differentiation: `TestCampaign`, `Specification`, `Pipeline`, `Flow`, `User`,\netc.\n\nThe __application__ has everything you need for the overall operation of the application. In this level you can find:\n\n* _commands_ — use cases that changes the state of the system\n* _queries_ — use cases that returns the state of the system\n* _repositories_ — descriptions of the necessary methods that the data layer must have in order for the application to\n  implement the use case\n* _interfaces_ (parsers, metrics, logging, etc.) — all sorts of interfaces, by implementing which you can choose the\n  technology that the application will use in its work\n* _policies_ — application level policies interfaces and internal implementations.\n\nThe __infrastructure__ code contains implementations for application-level interfaces. In runner, the application\ncontext is assembled from a configuration file with the necessary environment settings. On this layer, you can find the\ncode associated with the database in _persistence_, implementation of _pubsub_ with _NATS_, and so on.\n\nThe __interface__ layer contains the code that controls the application. A good example is an HTTP server that invokes a\nsspecific Application method on an endpoint call. Or a scheduler.\n\n## Project structure\n\n* `api` — API contract files like _OpenAPI_ files or _proto_ files\n    * `openapi` — _OpenAPI_ contract files\n* `build` - packaging and CI\n    * `package` — cloud, container, OS package configuration and scripts\n        * `dev/Dockerfile` — _Dockerfile_ for `Dev` environment\n* `cmd` — main applications\n    * `thestis/main.go` — application for **Thestis** backend server\n    * `thestis-validate/main.go` — main application for **Thestis** validation util\n* `configs` — **Thestis** server configuration files\n* `deployments` — container orchestration deployment configurations and template\n* `examples` — specification, code and other stuff example snippets\n* `internal` — private **Thestis** application code\n    * `config` — **Thestis** application config parser\n    * `core` — main logic of the application, divided into layers according to the principle of 1 layer per 1 package\n        * `infrastructure` — application level interface adapters with infrastructure implementation\n            * `auth` — implementations of authentication methods\n                * `fake` — fake authentication with hardcoded mock secret (only for development)\n                * `firebase` — authentication with _Firebase_\n            * `logger` — implementations of logging interface\n                * `zap` — logger adapter using _Uber's zap_\n            * `metrics` — implementations of metrics interface\n                * `prometheus` — metrics service collecting metrics with _Prometheus_\n            * `parser` — implementations of specification parsers\n                * `yaml` — service for parsing specification from _yaml_ files\n            * `persistence` — implementation of persistence interfaces\n                * `mongodb` — repositories and read models using MongoDB as persistence provider\n            * `pubsub` — implementation of pub/sum mechanism\n                * `nats` — signal event bus publisher and subscriber using NATS.io\n        * `app` — application level interfaces and orchestration code\n            * `command` — write operation use cases\n            * `mock` — application level interfaces mocks\n            * `query` — read operation use cases\n        * `entity` — domain logic of **Thestis** divided by main entities\n            * `flow` - flow, state and reducer from pipeline's steps\n            * `pipeline` — pipeline that can run tests flow from specification using concurrent running goroutines\n            * `specification` — tests description in declarative BDD style\n            * `testcampaign` — data about testing project, loaded specifications history and active specification\n            * `user` — access rights differentiation\n        * `interface` — application interface implementations\n            * `rest` — implementation of RESTful API\n                * `v1` — implementation of `api/openapi/thestis-v1.yml` _OpenAPI_ specification\n    * `runner` — **Thestis** backend application code for running from `cmd/thestis/main.go`\n    * `server` — **Thestis** server for running from application config\n    * `validate` — **Thestis** validate util code for running from `cmd/thestis-validate/main.go`\n* `pkg` — public **Thestis** library code\n* `swagger` — _Swagger UI_ static source files for UI rendering\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharpyd%2Fthestis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharpyd%2Fthestis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharpyd%2Fthestis/lists"}