{"id":24407826,"url":"https://github.com/itv/servicebox","last_synced_at":"2025-04-12T01:16:55.099Z","repository":{"id":57725440,"uuid":"126155163","full_name":"ITV/servicebox","owner":"ITV","description":"Scala library to control docker containers programmatically","archived":false,"fork":false,"pushed_at":"2025-01-09T14:38:41.000Z","size":326,"stargazers_count":13,"open_issues_count":1,"forks_count":2,"subscribers_count":41,"default_branch":"master","last_synced_at":"2025-04-12T01:16:49.965Z","etag":null,"topics":["cats","cats-effect","cst","docker","integration-testing","io-monad","testing-tools"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ITV.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2018-03-21T09:34:03.000Z","updated_at":"2025-01-09T14:38:47.000Z","dependencies_parsed_at":"2025-01-09T15:28:48.040Z","dependency_job_id":"e6becfef-5a01-4577-8f5e-dee905728e6d","html_url":"https://github.com/ITV/servicebox","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ITV%2Fservicebox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ITV%2Fservicebox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ITV%2Fservicebox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ITV%2Fservicebox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ITV","download_url":"https://codeload.github.com/ITV/servicebox/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248501859,"owners_count":21114684,"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":["cats","cats-effect","cst","docker","integration-testing","io-monad","testing-tools"],"created_at":"2025-01-20T05:18:09.218Z","updated_at":"2025-04-12T01:16:55.070Z","avatar_url":"https://github.com/ITV.png","language":"Scala","readme":"# Service box\n\nA library to define and run test dependencies using scala and Docker containers.\n\n_WARNING:_ This library in unmaintained!\n\n## Containers and integration testing\n\nScala's powerful type system can help avoiding a range of otherwise common bugs, \nremoving the need for pedantic, low-level unit testing. However, testing \nhow the various components integrate into a larger system is still necessary. It is in fact at this higher level that errors are \ntypically discovered (e.g. serialisation/deserialisation, missing configuration values, SQL queries working differently \nacross different database vendors, etc.).\n\nServicebox allows to define external dependencies in idiomatic scala, managing their lifecycle and execution within \nDocker containers. The main goal is to support developers in writing effective and reliable integration tests, by making\neasier to setup a CI/CD environment that closely resembles the production one.\n\n### Status\n\n[![Build Status](https://travis-ci.org/ITV/servicebox.svg?branch=master)](https://travis-ci.org/ITV/servicebox)\n[![Latest version](https://index.scala-lang.org/itv/servicebox/servicebox-core/latest.svg?color=orange\u0026v=1)](https://index.scala-lang.org/itv/servicebox/servicebox-core)\n\nThis library is at an early development stage and its API is likely to change significantly over the upcoming releases.\n\n## Getting started\n\nYou can install servicebox by adding the following dependencies to your `build.sbt` file:\n\n```scala\nval serviceboxVersion = \"\u003cCurrentVersion\u003e\"\nlibraryDependencies ++= Seq(\n  \"com.itv\" %% \"servicebox-core\" % serviceboxVersion,\n  \"com.itv\" %% \"servicebox-docker\" % serviceboxVersion, //docker support\n)\n```\n\nTo start with, you must specify your service dependencies as follows:\n\n```scala\nimport cats.effect.{ContextShift, IO}\nimport scala.concurrent.duration._\nimport cats.data.NonEmptyList\n\nimport com.itv.servicebox.algebra._\nimport com.itv.servicebox.interpreter._\nimport com.itv.servicebox.docker\n\nimport doobie._\nimport doobie.implicits._\n\nimport scala.concurrent.ExecutionContext\n\nobject Postgres {\n  case class DbConfig(host: String, dbName: String, password: String, port: Int)\n\n  implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global)\n  \n  val xa = Transactor.fromDriverManager[IO](\n    \"org.postgresql.Driver\", \n    \"jdbc:postgresql:world\",\n    \"postgres\",\n    \"\" \n  )\n  \n  def pingDb(value: DbConfig): IO[Unit] = IO {\n    sql\"select 1\".query[Unit].unique.transact(xa)\n  } \n\n  def apply(config: DbConfig): Service.Spec[IO] = {\n    // this will be re-attempted if an error is raised when running the query\n    def dbConnect(endpoints: Endpoints): IO[Unit] =\n      for {\n        _ \u003c- IOLogger.info(\"Attempting to connect to DB ...\")\n        ep = endpoints.toNel.head\n        serviceConfig = config.copy(host = ep.host, port = ep.port)\n        _ \u003c- pingDb(serviceConfig)\n        _ \u003c- IOLogger.info(\"... connected\")\n      } yield ()\n\n    Service.Spec[IO](\n      \"Postgres\",\n      NonEmptyList.of(\n        Container.Spec(\"postgres:9.5.4\",\n                       Map(\"POSTGRES_DB\" -\u003e config.dbName, \"POSTGRES_PASSWORD\" -\u003e config.password),\n                       Set(PortSpec.autoAssign(5432)),\n                       None,\n                       None)),\n      Service.ReadyCheck[IO](dbConnect, 50.millis, 5.seconds)\n    )\n  }\n}\n```\n\nA `Service.Spec[F[_]]` consists of one or more container descriptions, together with a `ReadyCheck`: an effectfull function\nwhich will be called repeatedly (i.e. every 50 millis) until it either returns successfully or it times out.\n\nOnce defined, one or several service specs might be executed through a `Runner`:\n\n```scala\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nimplicit val tag: AppTag = AppTag(\"com.example\", \"some-app\")\n\nval config = Postgres.DbConfig(\"localhost\", \"user\", \"pass\", 5432)\nval postgresSpec = Postgres(config)\n\n//evaluate only once to prevent shutdown hook to be fired multiple times\nlazy val runner = {\n  val instance = docker.runner()(postgresSpec)\n  sys.addShutdownHook {\n    instance.tearDown.unsafeRunSync()\n  }\n  instance\n}\n```\n\nThe service `Runner` exposes two main methods: a `tearDown`, which will kill all the containers\ndefined in the spec, and a `setUp`:\n\n```scala\nval registeredServices = runner.setUp.unsafeRunSync()\n```\n\nThis returns us a wrapper of a `Map[Service.Ref, Service.Registered[F]]`\nproviding us with some convenience methods to resolve running services/containers:\n\n```scala\nval pgLocation = registeredServices.locationFor(postgresSpec.ref, 5432).unsafeRunSync()\n```\n\nNotice that, while in the `Postgres` spec we define a container port, the library will automatically bind it to\nan available host port (see `InMemoryServiceRegistry` for details). Remember that, in order to use the service\nin your tests, you will have to point your app to the dynamically assigned host/port\n\n```scala\npgLocation.port\n```\n\n## Detailed example\n\nPlease refer to [this module](example) for a more detailed usage example illustrating how to integrate the library\nwith `scalatest`.\n\n## Key components\n\nThe library currently consists of the following modules:\n\n- An \"algebra\" to define test dependencies (aka `Service`) as aggregates of one or several `Container`.\n- An `InMemoryServiceRegistry` that can automatically assign available host ports to a service containers.\n- A `Scheduler`, which provides a simple interface suitable to repeatedly check if a service is ready\n- \"Interpreters\" to setup/check/teardown services using Docker as container technology, and `scala.concurrent.Future` or `cats.effect.IO`\nas the effect system.\n\n![Component diagram](docs/modules.png)\n\n## Modules\n\n- `core`: the core algebra, with built-in support for `scala.concurrent.Future`.\n- `core-io`: optional support for `cats.effect.IO`\n- `docker`: a docker interpreter for the core algebra.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitv%2Fservicebox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fitv%2Fservicebox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitv%2Fservicebox/lists"}