Showing preview only (4,139K chars total). Download the full file or copy to clipboard to get everything.
Repository: Trendyol/stove
Branch: main
Commit: ca8f6da12b3b
Files: 1062
Total size: 3.6 MB
Directory structure:
gitextract_mbe2tl_w/
├── .claude/
│ └── skills/
│ └── stove/
│ ├── SKILL.md
│ ├── container.md
│ ├── custom-systems.md
│ ├── go-setup.md
│ ├── gradle-config.md
│ ├── mcp.md
│ ├── other-languages.md
│ ├── system-setup.md
│ ├── tracing.md
│ └── writing-tests.md
├── .editorconfig
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── build-jvm-recipes.yml
│ ├── build-process-recipes.yml
│ ├── build.yml
│ ├── gradle-publish-release.yml
│ ├── gradle-publish-snapshot.yml
│ ├── publish-to-ghpages.yml
│ ├── scorecard.yml
│ ├── stove-cli-ci.yml
│ └── stove-cli-release.yml
├── .gitignore
├── LICENSE
├── README.md
├── api/
│ └── stove.api
├── build.gradle.kts
├── buildSrc/
│ ├── build.gradle.kts
│ ├── settings.gradle.kts
│ └── src/
│ └── main/
│ └── kotlin/
│ ├── BuildConstants.kt
│ ├── CI.kt
│ ├── GenerateDashboardVersionSourceTask.kt
│ ├── Helpers.kt
│ ├── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── gradle/
│ │ └── StoveTracingConfiguration.kt
│ └── stove-publishing.gradle.kts
├── codecov.yml
├── detekt.yml
├── docs/
│ ├── Components/
│ │ ├── 01-couchbase.md
│ │ ├── 02-kafka.md
│ │ ├── 03-elasticsearch.md
│ │ ├── 04-wiremock.md
│ │ ├── 05-http.md
│ │ ├── 06-postgresql.md
│ │ ├── 07-mongodb.md
│ │ ├── 08-mssql.md
│ │ ├── 09-redis.md
│ │ ├── 10-bridge.md
│ │ ├── 11-provided-instances.md
│ │ ├── 12-grpc.md
│ │ ├── 13-reporting.md
│ │ ├── 14-grpc-mock.md
│ │ ├── 15-tracing.md
│ │ ├── 16-mysql.md
│ │ ├── 17-cassandra.md
│ │ ├── 18-dashboard.md
│ │ ├── 19-provided-application.md
│ │ ├── 20-multiple-systems.md
│ │ ├── 21-mcp.md
│ │ ├── 22-container.md
│ │ └── index.md
│ ├── assets/
│ │ ├── rough-notation.iife.js
│ │ └── stove_dashboard.webm
│ ├── best-practices.md
│ ├── blog/
│ │ ├── dashboard-0.23.0.md
│ │ ├── polyglot-0.24.0.md
│ │ └── tracing-0.21.0.md
│ ├── css/
│ │ └── custom.css
│ ├── frameworks/
│ │ ├── index.md
│ │ ├── ktor.md
│ │ ├── micronaut.md
│ │ ├── quarkus.md
│ │ └── spring-boot.md
│ ├── getting-started.md
│ ├── index.md
│ ├── js/
│ │ └── rough-notation-mkdocs.js
│ ├── other-languages/
│ │ ├── go-container.md
│ │ ├── go-process.md
│ │ ├── go.md
│ │ └── index.md
│ ├── release-notes/
│ │ ├── 0.15.0.md
│ │ ├── 0.19.0.md
│ │ ├── 0.20.0.md
│ │ ├── 0.21.0.md
│ │ ├── 0.21.2.md
│ │ ├── 0.22.2.md
│ │ ├── 0.23.0.md
│ │ └── 0.24.0.md
│ ├── troubleshooting.md
│ └── writing-custom-systems.md
├── examples/
│ ├── build.gradle.kts
│ ├── ktor-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── ktor/
│ │ │ │ └── example/
│ │ │ │ ├── Application.kt
│ │ │ │ ├── app/
│ │ │ │ │ ├── app.kt
│ │ │ │ │ ├── configuration.kt
│ │ │ │ │ ├── database.kt
│ │ │ │ │ ├── kafka.kt
│ │ │ │ │ └── routing.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── ExampleAppConsumer.kt
│ │ │ │ │ ├── LockProvider.kt
│ │ │ │ │ ├── ProductService.kt
│ │ │ │ │ └── UpdateProductRequest.kt
│ │ │ │ ├── domain/
│ │ │ │ │ ├── Product.kt
│ │ │ │ │ └── ProductRepository.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── FeatureToggleClient.kt
│ │ │ │ └── PricingClient.kt
│ │ │ ├── proto/
│ │ │ │ ├── feature_toggle.proto
│ │ │ │ └── pricing.proto
│ │ │ └── resources/
│ │ │ ├── application.yaml
│ │ │ └── logback.xml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── ktor/
│ │ │ └── example/
│ │ │ └── e2e/
│ │ │ ├── ExampleTest.kt
│ │ │ ├── StoveConfig.kt
│ │ │ └── TestStub.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── micronaut-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── micronaut/
│ │ │ │ └── example/
│ │ │ │ ├── Application.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── domain/
│ │ │ │ │ │ └── Product.kt
│ │ │ │ │ ├── repository/
│ │ │ │ │ │ └── ProductRepository.kt
│ │ │ │ │ └── services/
│ │ │ │ │ ├── ProductService.kt
│ │ │ │ │ └── SupplierService.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── ObjectMapperConfig.kt
│ │ │ │ ├── api/
│ │ │ │ │ ├── ProductController.kt
│ │ │ │ │ └── model/
│ │ │ │ │ └── request/
│ │ │ │ │ └── CreateProductRequest.kt
│ │ │ │ ├── http/
│ │ │ │ │ └── SupplierHttpService.kt
│ │ │ │ ├── persistence/
│ │ │ │ │ └── ProductJdbcRepository.kt
│ │ │ │ └── postgres/
│ │ │ │ └── PostgresConfiguration.kt
│ │ │ └── resources/
│ │ │ ├── application.yml
│ │ │ └── logback.xml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── micronaut/
│ │ │ └── example/
│ │ │ └── e2e/
│ │ │ ├── CreateProductsTableMigration.kt
│ │ │ ├── ProductControllerTest.kt
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── quarkus-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── quarkus/
│ │ │ │ └── example/
│ │ │ │ ├── QuarkusMainApp.kt
│ │ │ │ ├── StoveStartupSignal.kt
│ │ │ │ ├── api/
│ │ │ │ │ └── ProductResource.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── Models.kt
│ │ │ │ │ └── ProductCreator.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── http/
│ │ │ │ │ └── SupplierHttpService.kt
│ │ │ │ ├── kafka/
│ │ │ │ │ ├── CustomProducerInterceptor.kt
│ │ │ │ │ ├── KafkaClientConfiguration.kt
│ │ │ │ │ ├── KafkaSerde.kt
│ │ │ │ │ ├── ProductCommandConsumer.kt
│ │ │ │ │ └── ProductEventPublisher.kt
│ │ │ │ └── postgres/
│ │ │ │ └── ProductRepository.kt
│ │ │ └── resources/
│ │ │ ├── application.properties
│ │ │ └── db/
│ │ │ └── migration/
│ │ │ └── V1__create_products.sql
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── quarkus/
│ │ │ └── example/
│ │ │ └── e2e/
│ │ │ ├── ExampleTest.kt
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-4x-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── spring/
│ │ │ │ └── example4x/
│ │ │ │ ├── ExampleApp.kt
│ │ │ │ ├── application/
│ │ │ │ │ └── handlers/
│ │ │ │ │ └── ProductCreator.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── api/
│ │ │ │ │ └── ProductController.kt
│ │ │ │ └── messaging/
│ │ │ │ └── kafka/
│ │ │ │ ├── KafkaConfiguration.kt
│ │ │ │ ├── KafkaProducer.kt
│ │ │ │ └── ProductCreateConsumer.kt
│ │ │ └── resources/
│ │ │ └── application.yml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── spring/
│ │ │ └── example4x/
│ │ │ └── e2e/
│ │ │ ├── ExampleTest.kt
│ │ │ ├── StoveConfig.kt
│ │ │ └── jackson3.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── spring/
│ │ │ │ └── example/
│ │ │ │ ├── ExampleApp.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── handlers/
│ │ │ │ │ │ └── ProductCreator.kt
│ │ │ │ │ └── services/
│ │ │ │ │ └── SupplierService.kt
│ │ │ │ ├── domain/
│ │ │ │ │ └── ProductTable.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── Constants.kt
│ │ │ │ ├── ObjectMapperConfig.kt
│ │ │ │ ├── api/
│ │ │ │ │ └── ProductController.kt
│ │ │ │ ├── http/
│ │ │ │ │ ├── SupplierHttpService.kt
│ │ │ │ │ ├── WebClientConfiguration.kt
│ │ │ │ │ └── WebClientConfigurationProperties.kt
│ │ │ │ ├── messaging/
│ │ │ │ │ └── kafka/
│ │ │ │ │ ├── KafkaProducer.kt
│ │ │ │ │ ├── configuration/
│ │ │ │ │ │ ├── ConsumerSettings.kt
│ │ │ │ │ │ ├── KafkaConsumerConfiguration.kt
│ │ │ │ │ │ ├── KafkaProducerConfiguration.kt
│ │ │ │ │ │ ├── KafkaProperties.kt
│ │ │ │ │ │ ├── MapBasedSettings.kt
│ │ │ │ │ │ └── ProducerSettings.kt
│ │ │ │ │ ├── consumers/
│ │ │ │ │ │ ├── FailingProductCreateConsumer.kt
│ │ │ │ │ │ ├── JobTopicConfig.kt
│ │ │ │ │ │ └── ProductCreateConsumers.kt
│ │ │ │ │ └── interceptors/
│ │ │ │ │ ├── CustomConsumerInterceptor.kt
│ │ │ │ │ └── CustomProducerInterceptor.kt
│ │ │ │ └── postgres/
│ │ │ │ └── ExposedConfiguration.kt
│ │ │ └── resources/
│ │ │ └── application.yml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── spring/
│ │ │ └── example/
│ │ │ └── e2e/
│ │ │ ├── CreateProductsTableMigration.kt
│ │ │ ├── ExampleTest.kt
│ │ │ ├── StoveConfig.kt
│ │ │ ├── TestSystemInitializer.kt
│ │ │ ├── TracingValidationTest.kt
│ │ │ └── hierarchy/
│ │ │ ├── BehaviorSpecHierarchyTest.kt
│ │ │ ├── DescribeSpecHierarchyTest.kt
│ │ │ ├── FunSpecContextHierarchyTest.kt
│ │ │ ├── NestedJunitHierarchyTest.kt
│ │ │ └── StringSpecHierarchyTest.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-standalone-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── spring/
│ │ │ │ └── standalone/
│ │ │ │ └── example/
│ │ │ │ ├── ExampleApp.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── handlers/
│ │ │ │ │ │ └── ProductCreator.kt
│ │ │ │ │ └── services/
│ │ │ │ │ └── SupplierService.kt
│ │ │ │ ├── domain/
│ │ │ │ │ └── ProductTable.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── Constants.kt
│ │ │ │ ├── ObjectMapperConfig.kt
│ │ │ │ ├── api/
│ │ │ │ │ └── ProductController.kt
│ │ │ │ ├── http/
│ │ │ │ │ ├── SupplierHttpService.kt
│ │ │ │ │ ├── WebClientConfiguration.kt
│ │ │ │ │ └── WebClientConfigurationProperties.kt
│ │ │ │ ├── messaging/
│ │ │ │ │ └── kafka/
│ │ │ │ │ ├── KafkaProducer.kt
│ │ │ │ │ ├── configuration/
│ │ │ │ │ │ ├── ConsumerSettings.kt
│ │ │ │ │ │ ├── KafkaConsumerConfiguration.kt
│ │ │ │ │ │ ├── KafkaProducerConfiguration.kt
│ │ │ │ │ │ ├── KafkaProperties.kt
│ │ │ │ │ │ ├── MapBasedSettings.kt
│ │ │ │ │ │ └── ProducerSettings.kt
│ │ │ │ │ ├── consumers/
│ │ │ │ │ │ ├── FailingProductCreateConsumer.kt
│ │ │ │ │ │ ├── JobTopicConfig.kt
│ │ │ │ │ │ └── ProductCreateConsumers.kt
│ │ │ │ │ └── interceptors/
│ │ │ │ │ ├── CustomConsumerInterceptor.kt
│ │ │ │ │ └── CustomProducerInterceptor.kt
│ │ │ │ └── postgres/
│ │ │ │ └── ExposedConfiguration.kt
│ │ │ └── resources/
│ │ │ └── application.yml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── spring/
│ │ │ └── standalone/
│ │ │ └── example/
│ │ │ └── e2e/
│ │ │ ├── CreateProductsTableMigration.kt
│ │ │ ├── ExampleTest.kt
│ │ │ ├── ReportingIntegrationTest.kt
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ └── spring-streams-example/
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── kotlin/
│ │ │ └── stove/
│ │ │ └── spring/
│ │ │ └── streams/
│ │ │ └── example/
│ │ │ ├── ExampleApp.kt
│ │ │ └── kafka/
│ │ │ ├── CustomDeserializationExceptionHandler.kt
│ │ │ ├── CustomProductionExceptionHandler.kt
│ │ │ ├── CustomSerDe.kt
│ │ │ ├── StreamsConfig.kt
│ │ │ └── application/
│ │ │ └── processor/
│ │ │ └── ExampleJoin.kt
│ │ ├── proto/
│ │ │ ├── Input1-value.proto
│ │ │ ├── Input2-value.proto
│ │ │ └── Output-value.proto
│ │ └── resources/
│ │ └── application.properties
│ └── test/
│ ├── kotlin/
│ │ └── com/
│ │ └── stove/
│ │ └── spring/
│ │ └── streams/
│ │ └── example/
│ │ └── e2e/
│ │ ├── ExampleTest.kt
│ │ ├── StoveConfig.kt
│ │ └── TestHelper.kt
│ └── resources/
│ ├── kotest.properties
│ └── logback-test.xml
├── go/
│ └── stove-kafka/
│ ├── bridge.go
│ ├── bridge_test.go
│ ├── franz/
│ │ ├── hooks.go
│ │ └── hooks_test.go
│ ├── go.mod
│ ├── go.sum
│ ├── sarama/
│ │ ├── interceptors.go
│ │ └── interceptors_test.go
│ ├── segmentio/
│ │ ├── bridge.go
│ │ └── bridge_test.go
│ └── stoveobserver/
│ ├── messages.pb.go
│ └── messages_grpc.pb.go
├── gradle/
│ ├── libs.versions.toml
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── jreleaser.yml
├── lib/
│ ├── stove/
│ │ ├── api/
│ │ │ └── stove.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ ├── containers/
│ │ │ │ ├── ContainerOptions.kt
│ │ │ │ ├── ProvidedRegistry.kt
│ │ │ │ └── StoveContainer.kt
│ │ │ ├── database/
│ │ │ │ └── migrations/
│ │ │ │ ├── DatabaseMigration.kt
│ │ │ │ ├── MigrationCollection.kt
│ │ │ │ └── SupportsMigrations.kt
│ │ │ ├── functional/
│ │ │ │ ├── Extensions.kt
│ │ │ │ ├── Reflect.kt
│ │ │ │ └── Try.kt
│ │ │ ├── http/
│ │ │ │ └── StoveHttpResponse.kt
│ │ │ ├── messaging/
│ │ │ │ └── Observation.kt
│ │ │ ├── reporting/
│ │ │ │ ├── JsonReportRenderer.kt
│ │ │ │ ├── PrettyConsoleRenderer.kt
│ │ │ │ ├── ReportEntry.kt
│ │ │ │ ├── ReportEventListener.kt
│ │ │ │ ├── ReportRenderer.kt
│ │ │ │ ├── Reports.kt
│ │ │ │ ├── SpanEventListener.kt
│ │ │ │ ├── SpanListenerRegistry.kt
│ │ │ │ ├── StoveReporter.kt
│ │ │ │ ├── StoveTestContext.kt
│ │ │ │ ├── StoveTestContextHolder.kt
│ │ │ │ ├── StoveTestExceptions.kt
│ │ │ │ ├── SystemSnapshot.kt
│ │ │ │ ├── TestReport.kt
│ │ │ │ └── TraceProvider.kt
│ │ │ ├── serialization/
│ │ │ │ ├── gson.kt
│ │ │ │ ├── jackson.kt
│ │ │ │ ├── kotlinx.kt
│ │ │ │ └── serialization.kt
│ │ │ ├── system/
│ │ │ │ ├── BridgeSystem.kt
│ │ │ │ ├── PortFinder.kt
│ │ │ │ ├── PropertiesFile.kt
│ │ │ │ ├── ProvidedApplicationUnderTest.kt
│ │ │ │ ├── ReadinessChecker.kt
│ │ │ │ ├── ReadinessStrategy.kt
│ │ │ │ ├── Runner.kt
│ │ │ │ ├── Stove.kt
│ │ │ │ ├── StoveOptions.kt
│ │ │ │ ├── StoveOptionsDsl.kt
│ │ │ │ ├── ValidationDsl.kt
│ │ │ │ ├── WithDsl.kt
│ │ │ │ ├── abstractions/
│ │ │ │ │ ├── ApplicationUnderTest.kt
│ │ │ │ │ ├── Exceptions.kt
│ │ │ │ │ ├── ExposesConfiguration.kt
│ │ │ │ │ ├── PluggedSystem.kt
│ │ │ │ │ ├── ReadyStove.kt
│ │ │ │ │ ├── RunnableSystemWithContext.kt
│ │ │ │ │ ├── StateStorage.kt
│ │ │ │ │ ├── SystemKey.kt
│ │ │ │ │ ├── SystemOptions.kt
│ │ │ │ │ ├── SystemRuntime.kt
│ │ │ │ │ ├── ThenSystemContinuation.kt
│ │ │ │ │ └── ValidatedSystem.kt
│ │ │ │ ├── annotations/
│ │ │ │ │ └── StoveDsl.kt
│ │ │ │ └── application/
│ │ │ │ ├── ApplicationConfigurations.kt
│ │ │ │ ├── ArgsProvider.kt
│ │ │ │ └── EnvProvider.kt
│ │ │ └── tracing/
│ │ │ ├── SpanInfo.kt
│ │ │ ├── SpanTree.kt
│ │ │ ├── TraceContext.kt
│ │ │ ├── TraceTreeRenderer.kt
│ │ │ └── TraceVisualization.kt
│ │ ├── test/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ ├── containers/
│ │ │ │ │ ├── ContainerOptionsTest.kt
│ │ │ │ │ ├── ProvidedRegistryTest.kt
│ │ │ │ │ └── StoveContainerTest.kt
│ │ │ │ ├── database/
│ │ │ │ │ └── migrations/
│ │ │ │ │ ├── MigrationCollectionTest.kt
│ │ │ │ │ └── SupportsMigrationsTest.kt
│ │ │ │ ├── http/
│ │ │ │ │ └── StoveHttpResponseTest.kt
│ │ │ │ ├── messaging/
│ │ │ │ │ └── ObservationTest.kt
│ │ │ │ ├── reporting/
│ │ │ │ │ ├── JsonReportRendererTest.kt
│ │ │ │ │ ├── PrettyConsoleRendererTest.kt
│ │ │ │ │ ├── ReportEntryTest.kt
│ │ │ │ │ ├── ReportEventListenerTest.kt
│ │ │ │ │ ├── ReportsTest.kt
│ │ │ │ │ ├── StoveReporterTest.kt
│ │ │ │ │ ├── StoveTestContextTest.kt
│ │ │ │ │ ├── StoveTestExceptionsTest.kt
│ │ │ │ │ ├── SystemSnapshotTest.kt
│ │ │ │ │ ├── TestReportTest.kt
│ │ │ │ │ └── TraceProviderTest.kt
│ │ │ │ ├── serialization/
│ │ │ │ │ └── SerializationTests.kt
│ │ │ │ ├── system/
│ │ │ │ │ ├── BridgeSystemGuardTest.kt
│ │ │ │ │ ├── BridgeSystemTest.kt
│ │ │ │ │ ├── KeyedSystemTest.kt
│ │ │ │ │ ├── PortFinderTest.kt
│ │ │ │ │ ├── ProvidedApplicationUnderTestTest.kt
│ │ │ │ │ ├── ReadinessCheckerTest.kt
│ │ │ │ │ ├── StoveOptionsDslTest.kt
│ │ │ │ │ ├── StoveTest.kt
│ │ │ │ │ ├── ValidationDslTest.kt
│ │ │ │ │ └── abstractions/
│ │ │ │ │ ├── ProvidedSystemOptionsTest.kt
│ │ │ │ │ ├── StateStorageKeyTest.kt
│ │ │ │ │ └── SystemKeyTest.kt
│ │ │ │ └── tracing/
│ │ │ │ ├── SpanInfoTest.kt
│ │ │ │ ├── SpanTreeTest.kt
│ │ │ │ ├── TraceContextTest.kt
│ │ │ │ ├── TraceTreeRendererTest.kt
│ │ │ │ └── TraceVisualizationTest.kt
│ │ │ └── resources/
│ │ │ └── logback.xml
│ │ └── testFixtures/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── CapturedOutput.kt
│ ├── stove-bom/
│ │ └── build.gradle.kts
│ ├── stove-cassandra/
│ │ ├── api/
│ │ │ └── stove-cassandra.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── cassandra/
│ │ │ ├── CassandraDsl.kt
│ │ │ ├── CassandraSystem.kt
│ │ │ ├── CassandraSystemOptions.kt
│ │ │ └── Options.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── cassandra/
│ │ │ ├── CassandraOptionsTests.kt
│ │ │ └── CassandraSystemTests.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback.xml
│ ├── stove-couchbase/
│ │ ├── api/
│ │ │ └── stove-couchbase.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── couchbase/
│ │ │ ├── CouchbaseDsl.kt
│ │ │ ├── CouchbaseSystem.kt
│ │ │ ├── Options.kt
│ │ │ ├── StoveCouchbaseContainer.kt
│ │ │ └── util.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── couchbase/
│ │ │ ├── CouchbaseOptionsTest.kt
│ │ │ ├── CouchbaseTestSystemTests.kt
│ │ │ └── TestSystemConfig.kt
│ │ └── resources/
│ │ └── kotest.properties
│ ├── stove-dashboard/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── dashboard/
│ │ │ ├── DashboardDsl.kt
│ │ │ ├── DashboardEmitter.kt
│ │ │ ├── DashboardOptions.kt
│ │ │ └── DashboardSystem.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── dashboard/
│ │ ├── DashboardEmitterTest.kt
│ │ └── DashboardSystemTest.kt
│ ├── stove-dashboard-api/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── main/
│ │ └── proto/
│ │ └── stove/
│ │ └── dashboard/
│ │ └── v1/
│ │ ├── dashboard_events.proto
│ │ └── dashboard_service.proto
│ ├── stove-elasticsearch/
│ │ ├── api/
│ │ │ └── stove-elasticsearch.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── elasticsearch/
│ │ │ ├── ElasticsearchExposedCertificate.kt
│ │ │ ├── ElasticsearchSystem.kt
│ │ │ ├── Extensions.kt
│ │ │ ├── Options.kt
│ │ │ └── util.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── elasticsearch/
│ │ │ ├── ElasticsearchExposedCertificateTest.kt
│ │ │ ├── ElasticsearchOptionsTest.kt
│ │ │ └── ElasticsearchTestSystemTests.kt
│ │ └── resources/
│ │ └── kotest.properties
│ ├── stove-grpc/
│ │ ├── api/
│ │ │ └── stove-grpc.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── grpc/
│ │ │ ├── GrpcDsl.kt
│ │ │ ├── GrpcSystem.kt
│ │ │ └── GrpcSystemOptions.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── grpc/
│ │ │ ├── GrpcAuthInterceptorTest.kt
│ │ │ ├── GrpcSystemStubTest.kt
│ │ │ ├── GrpcSystemWireTest.kt
│ │ │ ├── StoveConfig.kt
│ │ │ └── TestGrpcServer.kt
│ │ ├── proto/
│ │ │ └── test_service.proto
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── stove-grpc-mock/
│ │ ├── api/
│ │ │ └── stove-grpc-mock.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── testing/
│ │ │ └── grpcmock/
│ │ │ ├── GrpcMockDsl.kt
│ │ │ ├── GrpcMockSystem.kt
│ │ │ ├── GrpcMockSystemOptions.kt
│ │ │ └── StubDefinition.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── testing/
│ │ │ └── grpcmock/
│ │ │ ├── GrpcMockSystemTest.kt
│ │ │ └── StoveConfig.kt
│ │ ├── proto/
│ │ │ └── test_service.proto
│ │ └── resources/
│ │ └── kotest.properties
│ ├── stove-http/
│ │ ├── api/
│ │ │ └── stove-http.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── http/
│ │ │ ├── HttpClientFactory.kt
│ │ │ ├── HttpDsl.kt
│ │ │ ├── HttpSystem.kt
│ │ │ ├── StoveMultiPartContent.kt
│ │ │ ├── streaming.kt
│ │ │ └── websocket.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── http/
│ │ │ ├── HttpSystemTests.kt
│ │ │ └── WebSocketTests.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── stove-kafka/
│ │ ├── api/
│ │ │ └── stove-kafka.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── kafka/
│ │ │ │ ├── Caching.kt
│ │ │ │ ├── Extensions.kt
│ │ │ │ ├── KafkaContainerOptions.kt
│ │ │ │ ├── KafkaContext.kt
│ │ │ │ ├── KafkaExposedConfiguration.kt
│ │ │ │ ├── KafkaSystem.kt
│ │ │ │ ├── KafkaSystemOptions.kt
│ │ │ │ ├── SerDe.kt
│ │ │ │ ├── coroutines.kt
│ │ │ │ ├── intercepting/
│ │ │ │ │ ├── CommonOps.kt
│ │ │ │ │ ├── GrpcUtils.kt
│ │ │ │ │ ├── MessageSinkOps.kt
│ │ │ │ │ ├── MessageSinkPublishOps.kt
│ │ │ │ │ ├── MessageStore.kt
│ │ │ │ │ ├── StoveKafkaBridge.kt
│ │ │ │ │ ├── StoveKafkaObserverGrpcServer.kt
│ │ │ │ │ └── StoveMessageSink.kt
│ │ │ │ └── messages.kt
│ │ │ └── proto/
│ │ │ └── messages.proto
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── kafka/
│ │ │ ├── setup/
│ │ │ │ ├── TestSystemConfig.kt
│ │ │ │ └── example/
│ │ │ │ ├── DomainEvents.kt
│ │ │ │ ├── KafkaTestShared.kt
│ │ │ │ ├── StoveListener.kt
│ │ │ │ └── consumers/
│ │ │ │ ├── ProductConsumer.kt
│ │ │ │ └── ProductFailingConsumer.kt
│ │ │ └── tests/
│ │ │ ├── CoroutineExecutorServiceTests.kt
│ │ │ ├── KafkaOptionsTests.kt
│ │ │ ├── KafkaSystemTests.kt
│ │ │ ├── MessageStoreTests.kt
│ │ │ └── TopicSuffixesTests.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── stove-mongodb/
│ │ ├── api/
│ │ │ └── stove-mongodb.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mongodb/
│ │ │ ├── MongoDsl.kt
│ │ │ ├── MongodbSystem.kt
│ │ │ ├── MongodbSystemOptions.kt
│ │ │ ├── ObjectIdJsonOperations.kt
│ │ │ └── Options.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mongodb/
│ │ │ ├── MongodbOptionsTests.kt
│ │ │ ├── MongodbTestSystemTests.kt
│ │ │ └── ObjectIdJsonOperationsTest.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── stove-mssql/
│ │ ├── api/
│ │ │ └── stove-mssql.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mssql/
│ │ │ ├── MsSqlOptions.kt
│ │ │ └── MsSqlSystem.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mssql/
│ │ │ ├── MsSqlOptionsTest.kt
│ │ │ └── MssqlSystemTest.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── stove-mysql/
│ │ ├── api/
│ │ │ └── stove-mysql.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mysql/
│ │ │ ├── MySqlSystem.kt
│ │ │ └── Options.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mysql/
│ │ │ ├── MySqlOptionsTest.kt
│ │ │ ├── MySqlSystemTests.kt
│ │ │ └── TestSystemConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback.xml
│ ├── stove-postgres/
│ │ ├── api/
│ │ │ └── stove-postgres.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── postgres/
│ │ │ ├── Options.kt
│ │ │ └── PostgresqlSystem.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── postgres/
│ │ │ ├── PostgresqlOptionsTest.kt
│ │ │ ├── PostgresqlSystemTest.kt
│ │ │ └── TestSystemConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback.xml
│ ├── stove-rdbms/
│ │ ├── api/
│ │ │ └── stove-rdbms.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── rdbms/
│ │ │ ├── NativeSqlOperations.kt
│ │ │ ├── RelationalDatabaseContext.kt
│ │ │ ├── RelationalDatabaseExposedConfiguration.kt
│ │ │ └── RelationalDatabaseSystem.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── rdbms/
│ │ ├── RelationalDatabaseContextTest.kt
│ │ └── RelationalDatabaseSystemTest.kt
│ ├── stove-redis/
│ │ ├── api/
│ │ │ └── stove-redis.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── redis/
│ │ │ ├── RedisOptions.kt
│ │ │ └── RedisSystem.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── redis/
│ │ │ ├── RedisOptionsTest.kt
│ │ │ └── RedisSystemTests.kt
│ │ └── resources/
│ │ └── kotest.properties
│ ├── stove-tracing/
│ │ ├── api/
│ │ │ └── stove-tracing.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── tracing/
│ │ │ ├── Constants.kt
│ │ │ ├── OTLPSpanReceiver.kt
│ │ │ ├── StoveTraceCollector.kt
│ │ │ ├── TraceReportBuilder.kt
│ │ │ ├── TraceValidation.kt
│ │ │ ├── TracingOptions.kt
│ │ │ └── TracingSystem.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── tracing/
│ │ ├── OtlpSpanReceiverTest.kt
│ │ ├── SpanEventListenerTest.kt
│ │ ├── SpanInfoTest.kt
│ │ ├── SpanTreeTest.kt
│ │ ├── StoveTraceCollectorTest.kt
│ │ ├── TraceReportBuilderTest.kt
│ │ ├── TraceTreeRendererTest.kt
│ │ ├── TraceValidationTest.kt
│ │ ├── TracingDslTest.kt
│ │ ├── TracingOptionsTest.kt
│ │ ├── TracingSystemTest.kt
│ │ └── TracingValidationScopeTest.kt
│ └── stove-wiremock/
│ ├── api/
│ │ └── stove-wiremock.api
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── wiremock/
│ │ ├── Extensions.kt
│ │ ├── Options.kt
│ │ ├── WireMockBodyMatching.kt
│ │ ├── WireMockCallJournal.kt
│ │ ├── WireMockReportConstants.kt
│ │ ├── WireMockRequestListener.kt
│ │ ├── WireMockSnapshot.kt
│ │ ├── WireMockSystem.kt
│ │ ├── WireMockVacuumCleaner.kt
│ │ ├── WireMockVerification.kt
│ │ ├── WiremockDsl.kt
│ │ └── stubbing.kt
│ └── test/
│ ├── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── wiremock/
│ │ ├── ExtensionsTest.kt
│ │ ├── StoveConfig.kt
│ │ ├── WireMockDeletionTest.kt
│ │ ├── WireMockExposedConfigurationTest.kt
│ │ ├── WireMockOperationsTest.kt
│ │ ├── WireMockPartialMockingTest.kt
│ │ ├── WireMockSystemTests.kt
│ │ └── WireMockVerificationTest.kt
│ └── resources/
│ └── kotest.properties
├── lint.sh
├── mkdocs.yml
├── plugins/
│ └── stove-tracing-gradle-plugin/
│ ├── build.gradle.kts
│ ├── gradle/
│ │ └── libs.versions.toml
│ ├── gradle.properties
│ ├── settings.gradle.kts
│ └── src/
│ ├── main/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── gradle/
│ │ ├── StoveTracingExtension.kt
│ │ ├── StoveTracingPlugin.kt
│ │ └── internal/
│ │ ├── JvmArgsBuilder.kt
│ │ ├── TestTaskConfigurator.kt
│ │ └── TracingDefaults.kt
│ └── test/
│ └── kotlin/
│ └── com/
│ └── trendyol/
│ └── stove/
│ └── gradle/
│ └── StoveTracingPluginFunctionalTest.kt
├── pre-commit.sh
├── recipes/
│ ├── jvm/
│ │ ├── .editorconfig
│ │ ├── .gitattributes
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ ├── buildSrc/
│ │ │ ├── build.gradle.kts
│ │ │ ├── settings.gradle.kts
│ │ │ └── src/
│ │ │ └── main/
│ │ │ └── kotlin/
│ │ │ └── TestFolders.kt
│ │ ├── detekt.yml
│ │ ├── gradle/
│ │ │ ├── libs.versions.toml
│ │ │ └── wrapper/
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ ├── java-recipes/
│ │ │ ├── build.gradle.kts
│ │ │ ├── quarkus-basic-recipe/
│ │ │ │ ├── build.gradle.kts
│ │ │ │ └── src/
│ │ │ │ ├── main/
│ │ │ │ │ ├── java/
│ │ │ │ │ │ └── com/
│ │ │ │ │ │ └── trendyol/
│ │ │ │ │ │ └── stove/
│ │ │ │ │ │ └── recipes/
│ │ │ │ │ │ └── quarkus/
│ │ │ │ │ │ ├── EnglishGreetingService.java
│ │ │ │ │ │ ├── GreetingResource.java
│ │ │ │ │ │ ├── GreetingService.java
│ │ │ │ │ │ ├── HelloService.java
│ │ │ │ │ │ ├── HelloServiceImpl.java
│ │ │ │ │ │ ├── InMemoryItemRepository.java
│ │ │ │ │ │ ├── Item.java
│ │ │ │ │ │ ├── ItemRepository.java
│ │ │ │ │ │ ├── QuarkusMainApp.java
│ │ │ │ │ │ ├── SpanishGreetingService.java
│ │ │ │ │ │ ├── StoveStartupSignal.java
│ │ │ │ │ │ └── TurkishGreetingService.java
│ │ │ │ │ └── resources/
│ │ │ │ │ └── application.properties
│ │ │ │ └── test-e2e/
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── com/
│ │ │ │ │ └── trendyol/
│ │ │ │ │ └── stove/
│ │ │ │ │ └── recipes/
│ │ │ │ │ └── quarkus/
│ │ │ │ │ └── e2e/
│ │ │ │ │ ├── setup/
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── StoveConfig.kt
│ │ │ │ │ └── tests/
│ │ │ │ │ └── IndexTests.kt
│ │ │ │ └── resources/
│ │ │ │ ├── kotest.properties
│ │ │ │ └── logback-test.xml
│ │ │ └── spring-boot-postgres-recipe/
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ ├── main/
│ │ │ │ ├── java/
│ │ │ │ │ └── com/
│ │ │ │ │ └── trendyol/
│ │ │ │ │ └── stove/
│ │ │ │ │ └── examples/
│ │ │ │ │ └── java/
│ │ │ │ │ └── spring/
│ │ │ │ │ ├── ExampleSpringBootApp.java
│ │ │ │ │ ├── application/
│ │ │ │ │ │ ├── external/
│ │ │ │ │ │ │ └── category/
│ │ │ │ │ │ │ ├── CategoryApiSpringConfiguration.java
│ │ │ │ │ │ │ ├── CategoryHttpApi.java
│ │ │ │ │ │ │ ├── CategoryHttpApiConfiguration.java
│ │ │ │ │ │ │ └── CategoryHttpApiImpl.java
│ │ │ │ │ │ └── product/
│ │ │ │ │ │ ├── command/
│ │ │ │ │ │ │ └── ProductApplicationService.java
│ │ │ │ │ │ └── messaging/
│ │ │ │ │ │ └── ProductEventHandlerListener.java
│ │ │ │ │ ├── domain/
│ │ │ │ │ │ └── ProductReactiveRepository.java
│ │ │ │ │ └── infra/
│ │ │ │ │ ├── boilerplate/
│ │ │ │ │ │ ├── http/
│ │ │ │ │ │ │ └── ControllerAdvice.java
│ │ │ │ │ │ ├── kafka/
│ │ │ │ │ │ │ ├── KafkaBeanConfiguration.java
│ │ │ │ │ │ │ ├── KafkaConfiguration.java
│ │ │ │ │ │ │ ├── KafkaDomainEventPublisher.java
│ │ │ │ │ │ │ ├── Topic.java
│ │ │ │ │ │ │ └── TopicResolver.java
│ │ │ │ │ │ ├── postgres/
│ │ │ │ │ │ │ └── PostgresConfiguration.java
│ │ │ │ │ │ └── serialization/
│ │ │ │ │ │ └── JacksonConfiguration.java
│ │ │ │ │ └── components/
│ │ │ │ │ ├── index/
│ │ │ │ │ │ └── IndexController.java
│ │ │ │ │ └── product/
│ │ │ │ │ ├── api/
│ │ │ │ │ │ ├── ProductController.java
│ │ │ │ │ │ └── ProductCreateRequest.java
│ │ │ │ │ └── persistency/
│ │ │ │ │ └── JdbcProductRepository.java
│ │ │ │ └── resources/
│ │ │ │ └── application.yml
│ │ │ └── test-e2e/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── example/
│ │ │ │ └── java/
│ │ │ │ └── spring/
│ │ │ │ └── e2e/
│ │ │ │ ├── setup/
│ │ │ │ │ ├── CreateProductsTableMigration.kt
│ │ │ │ │ ├── Stove.kt
│ │ │ │ │ └── TestData.kt
│ │ │ │ └── tests/
│ │ │ │ ├── IndexTests.kt
│ │ │ │ └── product/
│ │ │ │ └── CreateTests.kt
│ │ │ └── resources/
│ │ │ ├── kotest.properties
│ │ │ └── logback-test.xml
│ │ ├── kotlin-recipes/
│ │ │ ├── build.gradle.kts
│ │ │ ├── ktor-mongo-recipe/
│ │ │ │ ├── build.gradle.kts
│ │ │ │ └── src/
│ │ │ │ ├── main/
│ │ │ │ │ ├── kotlin/
│ │ │ │ │ │ └── com/
│ │ │ │ │ │ └── trendyol/
│ │ │ │ │ │ └── stove/
│ │ │ │ │ │ └── examples/
│ │ │ │ │ │ └── kotlin/
│ │ │ │ │ │ └── ktor/
│ │ │ │ │ │ ├── ExampleStoveKtorApp.kt
│ │ │ │ │ │ ├── application/
│ │ │ │ │ │ │ ├── RecipeAppConfig.kt
│ │ │ │ │ │ │ ├── external/
│ │ │ │ │ │ │ │ ├── CategoryHttpApi.kt
│ │ │ │ │ │ │ │ └── CategoryHttpApiImpl.kt
│ │ │ │ │ │ │ └── product/
│ │ │ │ │ │ │ └── command/
│ │ │ │ │ │ │ ├── ProductCommandHandler.kt
│ │ │ │ │ │ │ └── handling.kt
│ │ │ │ │ │ ├── domain/
│ │ │ │ │ │ │ └── product/
│ │ │ │ │ │ │ └── ProductRepository.kt
│ │ │ │ │ │ └── infra/
│ │ │ │ │ │ ├── boilerplate/
│ │ │ │ │ │ │ ├── http/
│ │ │ │ │ │ │ │ └── http.kt
│ │ │ │ │ │ │ ├── kafka/
│ │ │ │ │ │ │ │ ├── ConsumerEngine.kt
│ │ │ │ │ │ │ │ ├── ConsumerSupervisor.kt
│ │ │ │ │ │ │ │ ├── KafkaDomainEventPublisher.kt
│ │ │ │ │ │ │ │ ├── SerDe.kt
│ │ │ │ │ │ │ │ ├── Topic.kt
│ │ │ │ │ │ │ │ ├── TopicResolver.kt
│ │ │ │ │ │ │ │ └── kafka.kt
│ │ │ │ │ │ │ ├── kediatr/
│ │ │ │ │ │ │ │ └── kediatr.kt
│ │ │ │ │ │ │ ├── mongo/
│ │ │ │ │ │ │ │ └── mongo.kt
│ │ │ │ │ │ │ ├── serialization/
│ │ │ │ │ │ │ │ └── JacksonConfiguration.kt
│ │ │ │ │ │ │ └── util.kt
│ │ │ │ │ │ └── components/
│ │ │ │ │ │ ├── external/
│ │ │ │ │ │ │ └── category.kt
│ │ │ │ │ │ └── product/
│ │ │ │ │ │ ├── api/
│ │ │ │ │ │ │ └── routing.kt
│ │ │ │ │ │ ├── defs.kt
│ │ │ │ │ │ ├── messaging/
│ │ │ │ │ │ │ └── ProductAggregateRootEventsConsumer.kt
│ │ │ │ │ │ └── persistency/
│ │ │ │ │ │ └── MongoProductRepository.kt
│ │ │ │ │ └── resources/
│ │ │ │ │ └── application.yaml
│ │ │ │ └── test-e2e/
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── com/
│ │ │ │ │ └── trendyol/
│ │ │ │ │ └── stove/
│ │ │ │ │ └── examples/
│ │ │ │ │ └── kotlin/
│ │ │ │ │ └── ktor/
│ │ │ │ │ └── e2e/
│ │ │ │ │ ├── setup/
│ │ │ │ │ │ ├── StoveConfig.kt
│ │ │ │ │ │ └── TestData.kt
│ │ │ │ │ └── tests/
│ │ │ │ │ ├── IndexTests.kt
│ │ │ │ │ ├── configuration/
│ │ │ │ │ │ └── ConfigurationTests.kt
│ │ │ │ │ └── product/
│ │ │ │ │ └── CreateTests.kt
│ │ │ │ └── resources/
│ │ │ │ ├── kotest.properties
│ │ │ │ └── logback-test.xml
│ │ │ ├── ktor-postgres-recipe/
│ │ │ │ ├── build.gradle.kts
│ │ │ │ └── src/
│ │ │ │ ├── main/
│ │ │ │ │ ├── kotlin/
│ │ │ │ │ │ └── com/
│ │ │ │ │ │ └── trendyol/
│ │ │ │ │ │ └── stove/
│ │ │ │ │ │ └── examples/
│ │ │ │ │ │ └── kotlin/
│ │ │ │ │ │ └── ktor/
│ │ │ │ │ │ ├── ExampleStoveKtorApp.kt
│ │ │ │ │ │ ├── application/
│ │ │ │ │ │ │ ├── RecipeAppConfig.kt
│ │ │ │ │ │ │ ├── external/
│ │ │ │ │ │ │ │ ├── CategoryHttpApi.kt
│ │ │ │ │ │ │ │ └── CategoryHttpApiImpl.kt
│ │ │ │ │ │ │ └── product/
│ │ │ │ │ │ │ └── command/
│ │ │ │ │ │ │ ├── ProductCommandHandler.kt
│ │ │ │ │ │ │ └── handling.kt
│ │ │ │ │ │ ├── domain/
│ │ │ │ │ │ │ └── product/
│ │ │ │ │ │ │ └── ProductRepository.kt
│ │ │ │ │ │ └── infra/
│ │ │ │ │ │ ├── boilerplate/
│ │ │ │ │ │ │ ├── http/
│ │ │ │ │ │ │ │ └── http.kt
│ │ │ │ │ │ │ ├── kafka/
│ │ │ │ │ │ │ │ ├── ConsumerEngine.kt
│ │ │ │ │ │ │ │ ├── ConsumerSupervisor.kt
│ │ │ │ │ │ │ │ ├── KafkaDomainEventPublisher.kt
│ │ │ │ │ │ │ │ ├── SerDe.kt
│ │ │ │ │ │ │ │ ├── Topic.kt
│ │ │ │ │ │ │ │ ├── TopicResolver.kt
│ │ │ │ │ │ │ │ └── kafka.kt
│ │ │ │ │ │ │ ├── kediatr/
│ │ │ │ │ │ │ │ └── kediatr.kt
│ │ │ │ │ │ │ ├── serialization/
│ │ │ │ │ │ │ │ └── JacksonConfiguration.kt
│ │ │ │ │ │ │ └── util.kt
│ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ ├── external/
│ │ │ │ │ │ │ │ └── category.kt
│ │ │ │ │ │ │ └── product/
│ │ │ │ │ │ │ ├── api/
│ │ │ │ │ │ │ │ └── routing.kt
│ │ │ │ │ │ │ ├── defs.kt
│ │ │ │ │ │ │ ├── messaging/
│ │ │ │ │ │ │ │ └── ProductAggregateRootEventsConsumer.kt
│ │ │ │ │ │ │ └── persistency/
│ │ │ │ │ │ │ ├── PostgresProductRepository.kt
│ │ │ │ │ │ │ └── Product.kt
│ │ │ │ │ │ └── postgres/
│ │ │ │ │ │ ├── flyway.kt
│ │ │ │ │ │ └── postgres.kt
│ │ │ │ │ └── resources/
│ │ │ │ │ ├── application.yaml
│ │ │ │ │ └── db/
│ │ │ │ │ └── migration/
│ │ │ │ │ └── V1__init.sql
│ │ │ │ └── test-e2e/
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── com/
│ │ │ │ │ └── trendyol/
│ │ │ │ │ └── stove/
│ │ │ │ │ └── examples/
│ │ │ │ │ └── kotlin/
│ │ │ │ │ └── ktor/
│ │ │ │ │ └── e2e/
│ │ │ │ │ ├── setup/
│ │ │ │ │ │ ├── StoveConfig.kt
│ │ │ │ │ │ └── TestData.kt
│ │ │ │ │ └── tests/
│ │ │ │ │ ├── IndexTests.kt
│ │ │ │ │ ├── configuration/
│ │ │ │ │ │ └── ConfigurationTests.kt
│ │ │ │ │ └── product/
│ │ │ │ │ └── CreateTests.kt
│ │ │ │ └── resources/
│ │ │ │ ├── kotest.properties
│ │ │ │ └── logback-test.xml
│ │ │ └── spring-showcase/
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ ├── main/
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── com/
│ │ │ │ │ └── trendyol/
│ │ │ │ │ └── stove/
│ │ │ │ │ └── examples/
│ │ │ │ │ └── kotlin/
│ │ │ │ │ └── spring/
│ │ │ │ │ ├── ExampleStoveSpringBootApp.kt
│ │ │ │ │ ├── domain/
│ │ │ │ │ │ ├── order/
│ │ │ │ │ │ │ ├── Order.kt
│ │ │ │ │ │ │ ├── OrderController.kt
│ │ │ │ │ │ │ ├── OrderRepository.kt
│ │ │ │ │ │ │ └── OrderService.kt
│ │ │ │ │ │ └── statistics/
│ │ │ │ │ │ └── UserOrderStatistics.kt
│ │ │ │ │ ├── events/
│ │ │ │ │ │ ├── OrderCreatedEvent.kt
│ │ │ │ │ │ └── PaymentProcessedEvent.kt
│ │ │ │ │ └── infra/
│ │ │ │ │ ├── GlobalErrorHandler.kt
│ │ │ │ │ ├── clients/
│ │ │ │ │ │ ├── FraudDetectionClient.kt
│ │ │ │ │ │ ├── InventoryClient.kt
│ │ │ │ │ │ └── PaymentClient.kt
│ │ │ │ │ ├── grpc/
│ │ │ │ │ │ ├── GrpcErrorSpanInterceptor.kt
│ │ │ │ │ │ ├── GrpcServerConfig.kt
│ │ │ │ │ │ └── OrderQueryGrpcService.kt
│ │ │ │ │ ├── kafka/
│ │ │ │ │ │ ├── KafkaConfig.kt
│ │ │ │ │ │ ├── OrderCreatedEventListener.kt
│ │ │ │ │ │ └── OrderEventPublisher.kt
│ │ │ │ │ ├── persistence/
│ │ │ │ │ │ ├── DataSourceConfig.kt
│ │ │ │ │ │ ├── PostgresOrderRepository.kt
│ │ │ │ │ │ └── PostgresUserOrderStatisticsRepository.kt
│ │ │ │ │ └── scheduling/
│ │ │ │ │ ├── DbSchedulerConfig.kt
│ │ │ │ │ ├── EmailSchedulerService.kt
│ │ │ │ │ └── SendOrderEmailTask.kt
│ │ │ │ ├── proto/
│ │ │ │ │ ├── fraud_detection.proto
│ │ │ │ │ └── order_query.proto
│ │ │ │ └── resources/
│ │ │ │ └── application.yml
│ │ │ └── test-e2e/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── examples/
│ │ │ │ └── kotlin/
│ │ │ │ └── spring/
│ │ │ │ └── e2e/
│ │ │ │ ├── painful/
│ │ │ │ │ └── BaseIntegrationTest.kt
│ │ │ │ ├── setup/
│ │ │ │ │ ├── DbSchedulerSystem.kt
│ │ │ │ │ ├── OrderExampleInitialMigration.kt
│ │ │ │ │ └── StoveConfig.kt
│ │ │ │ └── tests/
│ │ │ │ ├── StreamingTests.kt
│ │ │ │ └── TheShowcase.kt
│ │ │ └── resources/
│ │ │ ├── kotest.properties
│ │ │ └── logback-test.xml
│ │ ├── scala-recipes/
│ │ │ ├── build.gradle.kts
│ │ │ └── spring-boot-basic-recipe/
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ ├── main/
│ │ │ │ └── scala/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── recipes/
│ │ │ │ └── scala/
│ │ │ │ └── spring/
│ │ │ │ └── SpringBootRecipeApp.scala
│ │ │ └── test-e2e/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── recipes/
│ │ │ │ └── scala/
│ │ │ │ └── spring/
│ │ │ │ └── e2e/
│ │ │ │ ├── setup/
│ │ │ │ │ └── StoveConfig.kt
│ │ │ │ └── tests/
│ │ │ │ └── IndexTests.kt
│ │ │ └── resources/
│ │ │ └── kotest.properties
│ │ ├── settings.gradle.kts
│ │ └── shared/
│ │ ├── application/
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ └── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── recipes/
│ │ │ └── shared/
│ │ │ └── application/
│ │ │ ├── BusinessException.java
│ │ │ ├── ErrorResponse.java
│ │ │ ├── ExternalApiConfiguration.java
│ │ │ └── category/
│ │ │ ├── CategoryApiConfiguration.java
│ │ │ └── CategoryApiResponse.java
│ │ └── domain/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── examples/
│ │ │ └── domain/
│ │ │ ├── ddd/
│ │ │ │ ├── AggregateRoot.java
│ │ │ │ ├── DomainEvent.java
│ │ │ │ ├── Entity.java
│ │ │ │ ├── EventPublisher.java
│ │ │ │ ├── EventRecorder.java
│ │ │ │ └── EventRouter.java
│ │ │ └── product/
│ │ │ ├── Product.java
│ │ │ └── events/
│ │ │ ├── ProductCreatedEvent.java
│ │ │ ├── ProductNameChangedEvent.java
│ │ │ └── ProductPriceChangedEvent.java
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── examples/
│ │ └── domain/
│ │ ├── ProductTests.kt
│ │ └── testing/
│ │ └── aggregateroot/
│ │ └── AggregateRootAssertion.kt
│ └── process/
│ └── golang/
│ └── go-showcase/
│ ├── .dockerignore
│ ├── .editorconfig
│ ├── .gitignore
│ ├── Dockerfile.container
│ ├── build.gradle.kts
│ ├── db.go
│ ├── go.mod
│ ├── go.sum
│ ├── gradle/
│ │ ├── libs.versions.toml
│ │ └── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── handlers.go
│ ├── kafka.go
│ ├── kafka_franz.go
│ ├── kafka_sarama.go
│ ├── kafka_segmentio.go
│ ├── main.go
│ ├── settings.gradle.kts
│ ├── stovetests/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── examples/
│ │ │ └── go/
│ │ │ └── e2e/
│ │ │ ├── setup/
│ │ │ │ ├── ProductMigration.kt
│ │ │ │ └── StoveConfig.kt
│ │ │ └── tests/
│ │ │ └── GoShowcaseTest.kt
│ │ └── resources/
│ │ └── kotest.properties
│ └── tracing.go
├── renovate.json
├── settings.gradle.kts
├── starters/
│ ├── container/
│ │ └── stove-container/
│ │ ├── api/
│ │ │ └── stove-container.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── container/
│ │ │ ├── ContainerApplicationUnderTest.kt
│ │ │ ├── ContainerDsl.kt
│ │ │ └── ContainerTarget.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── container/
│ │ ├── ContainerApplicationUnderTestTest.kt
│ │ ├── ContainerDslTest.kt
│ │ └── ContainerTargetTest.kt
│ ├── ktor/
│ │ ├── stove-ktor/
│ │ │ ├── api/
│ │ │ │ └── stove-ktor.api
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ ├── main/
│ │ │ │ └── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── ktor/
│ │ │ │ ├── DependencyResolvers.kt
│ │ │ │ ├── KtorApplicationUnderTest.kt
│ │ │ │ ├── KtorBridgeSystem.kt
│ │ │ │ └── KtorDiCheck.kt
│ │ │ └── test/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── ktor/
│ │ │ ├── DependencyResolversLinkageTest.kt
│ │ │ └── KtorDiCheckTest.kt
│ │ └── tests/
│ │ ├── ktor-di-tests/
│ │ │ ├── api/
│ │ │ │ └── ktor-di-tests.api
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ └── test/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── ktor/
│ │ │ │ ├── AutoDetectRuntimeStateTest.kt
│ │ │ │ ├── StoveConfig.kt
│ │ │ │ └── app.kt
│ │ │ └── resources/
│ │ │ └── simplelogger.properties
│ │ ├── ktor-koin-tests/
│ │ │ ├── api/
│ │ │ │ └── ktor-koin-tests.api
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ └── test/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── ktor/
│ │ │ │ ├── AutoDetectRuntimeSelectionTest.kt
│ │ │ │ ├── StoveConfig.kt
│ │ │ │ └── app.kt
│ │ │ └── resources/
│ │ │ └── simplelogger.properties
│ │ └── ktor-test-fixtures/
│ │ ├── api/
│ │ │ └── ktor-test-fixtures.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── testFixtures/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── ktor/
│ │ ├── BridgeSystemTests.kt
│ │ └── TestDomain.kt
│ ├── micronaut/
│ │ └── stove-micronaut/
│ │ ├── api/
│ │ │ └── stove-micronaut.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── micronaut/
│ │ │ │ ├── MicronautApplicationUnderTest.kt
│ │ │ │ └── MicronautBridgeSystem.kt
│ │ │ └── resources/
│ │ │ ├── application.properties
│ │ │ └── logback.xml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── BridgeSystemTestConfig.kt
│ │ └── resources/
│ │ └── kotest.properties
│ ├── process/
│ │ └── stove-process/
│ │ ├── api/
│ │ │ └── stove-process.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── process/
│ │ │ ├── ArgsProvider.kt
│ │ │ ├── EnvProvider.kt
│ │ │ ├── ProcessApplicationOptions.kt
│ │ │ ├── ProcessApplicationUnderTest.kt
│ │ │ └── ProcessDsl.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── process/
│ │ ├── ArgsProviderTest.kt
│ │ ├── EnvProviderTest.kt
│ │ └── ProcessApplicationUnderTestTest.kt
│ ├── quarkus/
│ │ └── stove-quarkus/
│ │ ├── api/
│ │ │ └── stove-quarkus.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── main/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── quarkus/
│ │ └── QuarkusApplicationUnderTest.kt
│ └── spring/
│ ├── stove-spring/
│ │ ├── api/
│ │ │ ├── stove-spring-common.api
│ │ │ └── stove-spring.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── spring/
│ │ │ ├── BridgeSystem.kt
│ │ │ ├── SpringApplicationUnderTest.kt
│ │ │ ├── SpringBootVersionCheck.kt
│ │ │ └── registrar.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ ├── SpringApplicationUnderTestTests.kt
│ │ ├── SpringBridgeSystemTests.kt
│ │ └── spring/
│ │ └── SpringBootVersionCheckTest.kt
│ ├── stove-spring-kafka/
│ │ ├── api/
│ │ │ ├── stove-spring-kafka-common.api
│ │ │ └── stove-spring-kafka.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── kafka/
│ │ │ ├── Caching.kt
│ │ │ ├── Extensions.kt
│ │ │ ├── KafkaDsl.kt
│ │ │ ├── KafkaSystem.kt
│ │ │ ├── KafkaTemplateCompatibility.kt
│ │ │ ├── MessageStore.kt
│ │ │ ├── Options.kt
│ │ │ ├── SpringKafkaVersionCheck.kt
│ │ │ ├── StoveMessage.kt
│ │ │ └── TestSystemKafkaInterceptor.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── kafka/
│ │ ├── CachingTests.kt
│ │ ├── ExtensionsTests.kt
│ │ ├── KafkaOptionsTest.kt
│ │ ├── MessageStoreTests.kt
│ │ ├── SpringKafkaVersionCheckTest.kt
│ │ └── StoveMessageTests.kt
│ └── tests/
│ ├── spring-2x-kafka-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── kafka/
│ │ │ ├── protobufserde/
│ │ │ │ ├── ProtobufSerdeKafkaSystemTest.kt
│ │ │ │ └── app.kt
│ │ │ ├── shared.kt
│ │ │ └── stringserde/
│ │ │ ├── StringSerdeKafkaSystemTest.kt
│ │ │ └── app.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-2x-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-3x-kafka-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── kafka/
│ │ │ ├── protobufserde/
│ │ │ │ ├── ProtobufSerdeKafkaSystemTest.kt
│ │ │ │ └── app.kt
│ │ │ ├── shared.kt
│ │ │ └── stringserde/
│ │ │ ├── StringSerdeKafkaSystemTest.kt
│ │ │ └── app.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-3x-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-4x-kafka-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── kafka/
│ │ │ ├── protobufserde/
│ │ │ │ ├── ProtobufSerdeKafkaSystemTest.kt
│ │ │ │ └── app.kt
│ │ │ ├── shared.kt
│ │ │ └── stringserde/
│ │ │ ├── StringSerdeKafkaSystemTest.kt
│ │ │ └── app.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-4x-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ └── spring-test-fixtures/
│ ├── build.gradle.kts
│ └── src/
│ └── testFixtures/
│ ├── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ ├── BridgeSystemTests.kt
│ │ ├── TestDomain.kt
│ │ └── kafka/
│ │ ├── KafkaTestDomain.kt
│ │ ├── ProtobufSerdeKafkaSystemTests.kt
│ │ ├── ProtobufTestUtils.kt
│ │ └── StringSerdeKafkaSystemTests.kt
│ └── proto/
│ └── example.proto
├── test-extensions/
│ ├── stove-extensions-junit/
│ │ ├── api/
│ │ │ └── stove-extensions-junit.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── extensions/
│ │ │ └── junit/
│ │ │ └── StoveJUnitExtension.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── extensions/
│ │ │ └── junit/
│ │ │ └── StoveJUnitExtensionTest.kt
│ │ └── resources/
│ │ └── logback-test.xml
│ └── stove-extensions-kotest/
│ ├── api/
│ │ └── stove-extensions-kotest.api
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── extensions/
│ │ └── kotest/
│ │ └── StoveKotestExtension.kt
│ └── test/
│ ├── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── extensions/
│ │ └── kotest/
│ │ ├── KotestHierarchyExplorationTest.kt
│ │ └── StoveKotestExtensionTest.kt
│ └── resources/
│ ├── kotest.properties
│ └── logback-test.xml
└── tools/
└── stove-cli/
├── .gitignore
├── .idea/
│ ├── .gitignore
│ ├── copilot.data.migration.ask2agent.xml
│ ├── inspectionProfiles/
│ │ └── Project_Default.xml
│ ├── modules.xml
│ └── vcs.xml
├── Cargo.toml
├── Formula/
│ └── stove.rb
├── build.rs
├── clippy.toml
├── install.sh
├── rustfmt.toml
├── spa/
│ ├── biome.json
│ ├── index.html
│ ├── package.json
│ ├── postcss.config.js
│ ├── src/
│ │ ├── App.tsx
│ │ ├── api/
│ │ │ ├── client.ts
│ │ │ ├── live-cache.ts
│ │ │ ├── sse.ts
│ │ │ └── types.ts
│ │ ├── components/
│ │ │ ├── Badge.tsx
│ │ │ ├── CapturedStateLane.tsx
│ │ │ ├── Detail.tsx
│ │ │ ├── DurationEdge.tsx
│ │ │ ├── EntryDetails.tsx
│ │ │ ├── EntryRow.tsx
│ │ │ ├── FlowDag.tsx
│ │ │ ├── FlowTab.tsx
│ │ │ ├── GapNode.tsx
│ │ │ ├── JsonTree.tsx
│ │ │ ├── NodePopup.tsx
│ │ │ ├── ResultIcon.tsx
│ │ │ ├── SnapshotCards.tsx
│ │ │ ├── SnapshotMetricTiles.tsx
│ │ │ ├── SnapshotStateDialog.tsx
│ │ │ ├── SpanTree.tsx
│ │ │ ├── SysBadge.tsx
│ │ │ ├── SystemNode.tsx
│ │ │ └── VersionMismatchBanner.tsx
│ │ ├── hooks/
│ │ │ ├── useAppData.ts
│ │ │ └── useTheme.tsx
│ │ ├── index.css
│ │ ├── layout/
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ ├── TestDetail.tsx
│ │ │ ├── detail/
│ │ │ │ ├── TabBar.tsx
│ │ │ │ └── TestHeader.tsx
│ │ │ └── sidebar/
│ │ │ ├── AppPicker.tsx
│ │ │ ├── RunSummary.tsx
│ │ │ ├── TestFilters.tsx
│ │ │ ├── TestListItem.tsx
│ │ │ └── TestTree.tsx
│ │ ├── main.tsx
│ │ ├── utils/
│ │ │ ├── filters.ts
│ │ │ ├── flow.ts
│ │ │ ├── format.ts
│ │ │ ├── json.ts
│ │ │ ├── result.ts
│ │ │ ├── snapshot-state.ts
│ │ │ ├── status.ts
│ │ │ ├── systems.ts
│ │ │ └── version-mismatch.ts
│ │ └── vite-env.d.ts
│ ├── test/
│ │ ├── api-client.test.mjs
│ │ ├── flow.test.mjs
│ │ ├── json.test.mjs
│ │ ├── live-cache.test.mjs
│ │ ├── snapshot-state.test.mjs
│ │ └── version-mismatch.test.mjs
│ ├── tsconfig.json
│ └── vite.config.ts
├── src/
│ ├── config.rs
│ ├── error.rs
│ ├── grpc/
│ │ ├── mod.rs
│ │ └── service.rs
│ ├── http/
│ │ ├── mod.rs
│ │ ├── routes/
│ │ │ ├── meta.rs
│ │ │ ├── mod.rs
│ │ │ ├── runs.rs
│ │ │ ├── sse.rs
│ │ │ ├── static_files.rs
│ │ │ ├── tests.rs
│ │ │ └── traces.rs
│ │ └── server.rs
│ ├── ingest.rs
│ ├── lib.rs
│ ├── main.rs
│ ├── mcp/
│ │ ├── analysis/
│ │ │ └── evidence.rs
│ │ ├── analysis.rs
│ │ ├── args.rs
│ │ ├── contract.rs
│ │ ├── mod.rs
│ │ ├── protocol.rs
│ │ ├── security.rs
│ │ └── tools.rs
│ ├── skills.rs
│ ├── sse/
│ │ ├── manager.rs
│ │ └── mod.rs
│ └── storage/
│ ├── database.rs
│ ├── migrations/
│ │ ├── V1__initial_schema.sql
│ │ ├── V2__run_stove_version.sql
│ │ └── V3__test_path.sql
│ ├── mod.rs
│ ├── models.rs
│ └── repository.rs
└── tests/
├── api_e2e.rs
├── common/
│ └── mod.rs
└── mcp_e2e.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .claude/skills/stove/SKILL.md
================================================
---
name: stove
description: Use when adding Stove e2e tests to a project, configuring Stove systems (HTTP, PostgreSQL, MySQL, MSSQL, Cassandra, MongoDB, Redis, Elasticsearch, Couchbase, Kafka, WireMock, gRPC, Dashboard), writing tests with the stove {} DSL, enabling OpenTelemetry tracing, writing AbstractProjectConfig, extending Stove with custom systems, setting up smoke tests against remote/deployed applications (providedApplication), registering multiple instances of the same system type (keyed systems with SystemKey), testing non-JVM applications (Go, Python, Rust, Node.js) with processApp()/goApp() from stove-process or containerApp() from stove-container (target/readiness, env/args mapping, image-based AUT), the Stove Kafka bridge library (stove-kafka for Go with IBM/sarama, twmb/franz-go, or segmentio/kafka-go), or collecting Go code coverage from Stove black-box tests (go build -cover, GOCOVERDIR, SIGPIPE handling).
---
# Setting Up Stove E2E Tests
Copy this checklist and track progress:
```
Setup Progress:
- [ ] Step 1: Create test-e2e source set layout
- [ ] Step 2: Configure Gradle (BOM, source set, e2eTest task)
- [ ] Step 3: Extract run() function from application entry point
- [ ] Step 4: Create StoveConfig (AbstractProjectConfig)
- [ ] Step 5: Create kotest.properties (Kotest only)
- [ ] Step 6: Configure systems inside Stove().with { }
- [ ] Step 7: Configure tracing (optional)
- [ ] Step 8: Write tests using stove {} DSL
```
Important: Stove e2e tests are Kotlin-first. Even if your application is Java/Scala, keep e2e tests under `src/test-e2e/kotlin` and write Stove setup/tests in Kotlin.
## Step 1: Project structure
```
your-module/src/
main/(kotlin|java)/
test/(kotlin|java)/
test-e2e/
kotlin/com/yourcompany/yourapp/e2e/
setup/
StoveConfig.kt
InitialMigration.kt
tests/
OrderE2ETest.kt
resources/
kotest.properties
```
## Step 2: Gradle configuration
For source set registration, e2eTest task, and IDE integration details, see [gradle-config.md](gradle-config.md).
Add dependencies using the BOM:
```kotlin
dependencies {
testImplementation(platform("com.trendyol:stove-bom:$stoveVersion"))
testImplementation("com.trendyol:stove")
testImplementation("com.trendyol:stove-spring")
testImplementation("com.trendyol:stove-extensions-kotest")
// Add only what you need:
testImplementation("com.trendyol:stove-http")
testImplementation("com.trendyol:stove-postgres")
testImplementation("com.trendyol:stove-mysql")
testImplementation("com.trendyol:stove-mssql")
testImplementation("com.trendyol:stove-cassandra")
testImplementation("com.trendyol:stove-mongodb")
testImplementation("com.trendyol:stove-redis")
testImplementation("com.trendyol:stove-elasticsearch")
testImplementation("com.trendyol:stove-couchbase")
testImplementation("com.trendyol:stove-kafka")
testImplementation("com.trendyol:stove-spring-kafka")
testImplementation("com.trendyol:stove-wiremock")
testImplementation("com.trendyol:stove-grpc")
testImplementation("com.trendyol:stove-grpc-mock")
testImplementation("com.trendyol:stove-tracing")
testImplementation("com.trendyol:stove-dashboard")
testImplementation("com.trendyol:stove-process") // non-JVM apps (Go, Python, etc.)
testImplementation("com.trendyol:stove-container") // non-JVM apps as Docker images
}
```
For Ktor, replace `stove-spring` with `stove-ktor`. For Quarkus, use `stove-quarkus`. For Micronaut, use `stove-micronaut`. For JUnit, replace `stove-extensions-kotest` with `stove-extensions-junit` and skip Step 5.
If you are unsure about Stove API names/signatures, verify from local downloaded artifacts (Gradle cache or Maven local repo) before writing code. See [gradle-config.md](gradle-config.md#resolve-api-ambiguity-from-local-artifacts).
## Step 3: Extract run()
Stove starts your application from tests. Extract the entry point:
```kotlin
// src/main/kotlin/.../MyApp.kt
@SpringBootApplication
class MyApp
fun main(args: Array<String>) = run(args)
fun run(
args: Array<String>,
init: SpringApplication.() -> Unit = {}
): ConfigurableApplicationContext =
runApplication<MyApp>(*args) { init() }
```
## Step 4: StoveConfig
```kotlin
class StoveConfig : AbstractProjectConfig() {
override val extensions: List<Extension> = listOf(StoveKotestExtension())
override suspend fun beforeProject() {
Stove()
.with {
// Systems go here — see Step 6
}.run()
}
override suspend fun afterProject() {
Stove.stop()
}
}
```
For JUnit, see [gradle-config.md](gradle-config.md) for the `BaseE2ETest` pattern.
## Step 5: kotest.properties (Kotest only)
Create `src/test-e2e/resources/kotest.properties`:
```properties
kotest.framework.config.fqn=com.yourcompany.yourapp.e2e.setup.StoveConfig
```
## Step 6: Configure systems
Configure inside `Stove().with { }`. For all options per system, see [system-setup.md](system-setup.md).
```kotlin
Stove()
.with {
httpClient {
HttpClientSystemOptions(baseUrl = "http://localhost:8080")
}
bridge()
// Optional (requires com.trendyol:stove-tracing)
tracing { enableSpanReceiver() }
wiremock {
WireMockSystemOptions(
configureExposedConfiguration = { cfg ->
listOf("payment.url=${cfg.baseUrl}")
}
)
}
postgresql {
PostgresqlOptions(
databaseName = "testdb",
configureExposedConfiguration = { cfg ->
listOf(
"spring.datasource.url=${cfg.jdbcUrl}",
"spring.datasource.username=${cfg.username}",
"spring.datasource.password=${cfg.password}"
)
}
).migrations { register<InitialMigration>() }
}
kafka {
KafkaSystemOptions(
serde = StoveSerde.jackson.anyByteArraySerde(),
configureExposedConfiguration = { cfg ->
listOf(
"spring.kafka.bootstrap-servers=${cfg.bootstrapServers}",
"spring.kafka.producer.properties.interceptor.classes=${cfg.interceptorClass}",
"spring.kafka.consumer.properties.interceptor.classes=${cfg.interceptorClass}"
)
}
)
}
mongodb {
MongodbSystemOptions(
databaseOptions = DatabaseOptions(
default = DefaultDatabase(name = "testdb", collection = "orders")
),
configureExposedConfiguration = { cfg ->
listOf("spring.data.mongodb.uri=${cfg.connectionString}")
}
)
}
// Optional: streams test events to stove CLI dashboard
dashboard {
DashboardSystemOptions(appName = "my-service")
}
// Application runner goes last
springBoot(
runner = { params ->
com.yourcompany.yourapp.run(params) {
addTestDependencies {
bean<TestSystemKafkaInterceptor<*, *>>(isPrimary = true)
bean { StoveSerde.jackson.anyByteArraySerde() }
}
}
},
withParameters = listOf("server.port=8080")
)
}.run()
```
For Spring Boot 4.x, use:
```kotlin
addTestDependencies4x {
registerBean<TestSystemKafkaInterceptor<*, *>>(primary = true)
registerBean { StoveSerde.jackson.anyByteArraySerde() }
}
```
For Ktor:
```kotlin
ktor(
runner = { params -> com.yourcompany.yourapp.run(params, wait = false) },
withParameters = listOf("server.port=8080")
)
```
For Quarkus:
```kotlin
quarkus(
runner = { params -> com.yourcompany.yourapp.main(params) },
withParameters = listOf("quarkus.http.port=8080")
)
```
For Micronaut:
```kotlin
micronaut(
runner = { params -> com.yourcompany.yourapp.run(params) },
withParameters = listOf("micronaut.server.port=8080")
)
```
## Step 7: Tracing (optional)
For full plugin options, buildSrc alternative, and trace validation DSL, see [tracing.md](tracing.md).
```kotlin
plugins { id("com.trendyol.stove.tracing") version "$stoveVersion" }
stoveTracing {
serviceName.set("my-service")
testTaskNames.set(listOf("e2eTest"))
}
```
## Step 8: Write tests
For the complete DSL reference (HTTP, PostgreSQL, MySQL, MSSQL, Cassandra, MongoDB, Redis, Elasticsearch, Couchbase, Kafka, WireMock, gRPC Mock, gRPC Client, Bridge, multi-system examples), see [writing-tests.md](writing-tests.md).
```kotlin
class OrderE2ETest : FunSpec({
test("should create order and publish event") {
stove {
val userId = "user-${UUID.randomUUID()}"
wiremock {
mockGet("/inventory/item-1", 200, InventoryResponse(true).some())
}
http {
postAndExpectBody<OrderResponse>(
uri = "/orders",
body = CreateOrderRequest(userId, 99.99).some()
) { response ->
response.status shouldBe 201
}
}
postgresql {
shouldQuery<OrderRow>(
query = "SELECT * FROM orders WHERE user_id = '$userId'",
mapper = { row -> OrderRow(row.string("id"), row.string("status")) }
) { it.size shouldBe 1 }
}
kafka {
shouldBePublished<OrderCreatedEvent>(10.seconds) {
actual.userId == userId
}
}
}
}
})
```
## Smoke testing with providedApplication
Stove can test against **already-deployed** applications — any language, any framework. Use `providedApplication()` instead of a JVM runner. See [system-setup.md](system-setup.md#provided-application-smoke-testing) for full details.
```kotlin
Stove().with {
httpClient { HttpClientSystemOptions(baseUrl = "https://staging.myapp.com") }
postgresql(AppDb) {
PostgresqlOptions.provided(
jdbcUrl = "jdbc:postgresql://staging-db:5432/myapp",
cleanup = { ops -> ops.execute("DELETE FROM orders WHERE test = true") },
configureExposedConfiguration = { listOf() } // no AUT to configure
)
}
providedApplication {
ProvidedApplicationOptions(
readiness = ReadinessStrategy.HttpGet(url = "https://staging.myapp.com/health")
)
}
}.run()
```
Key points:
- No JVM runner needed — the application is already running
- Works with any language (Go, Python, .NET, Rust, Node.js, etc.)
- `Bridge` (DI access) is **not available** — there's no local DI container
- Use `cleanup` lambdas to manage test data on external infrastructure
- Optional health check waits for the app to be ready before running tests
## Keyed systems (multiple instances)
Register multiple instances of the same system type using `SystemKey`. See [system-setup.md](system-setup.md#keyed-systems-multiple-instances) and [writing-tests.md](writing-tests.md#keyed-system-tests) for examples.
```kotlin
// Define keys as singleton objects
object AppDb : SystemKey
object AnalyticsDb : SystemKey
object PaymentService : SystemKey
object InventoryService : SystemKey
Stove().with {
postgresql(AppDb) {
PostgresqlOptions(configureExposedConfiguration = { cfg ->
listOf("app.datasource.url=${cfg.jdbcUrl}")
})
}
postgresql(AnalyticsDb) {
PostgresqlOptions(configureExposedConfiguration = { cfg ->
listOf("analytics.datasource.url=${cfg.jdbcUrl}")
})
}
httpClient(PaymentService) {
HttpClientSystemOptions(baseUrl = "https://pay.internal")
}
springBoot(runner = { params -> run(params) })
}.run()
```
All systems support keyed registration: PostgreSQL, MySQL, MSSQL, Cassandra, MongoDB, Redis, Elasticsearch, Couchbase, Kafka (core), WireMock, gRPC, gRPC Mock, HTTP. Each keyed instance gets its own container, port, state storage, and configuration.
## Writing custom Stove systems
Stove is extensible. For the complete pattern with a working db-scheduler example, see [custom-systems.md](custom-systems.md).
## Best practices
- Generate unique IDs per test: `UUID.randomUUID()`
- Configure Stove once in `AbstractProjectConfig`, never per-test
- Keep e2e tests in `src/test-e2e/kotlin` (also for Java/Scala applications)
- If API is ambiguous, inspect local `stove-*.jar` / `stove-*-sources.jar` in Gradle/Maven caches and confirm class/method names before coding
- Use `port = 0` for WireMock and gRPC Mock (dynamic ports, CI-safe)
- Test through HTTP endpoints; verify DB state and events as side effects
- Use `shouldBePublished<Event>(atLeastIn = 10.seconds) { ... }` — never `Thread.sleep`
- Use `cleanup` lambdas in system options to wipe test data on teardown — essential for provided (external) instances
- Use `Stove { keepDependenciesRunning() }` locally for faster iteration; disable in CI
- **AI agent feedback loop**: Enable tracing + reporting. When tests fail, the execution report contains the full call chain, system snapshots, and timeline. AI agents can parse this structured output to understand exactly what went wrong inside the application and iterate on fixes with precise feedback.
- **Kafka test-friendly settings**: Default Kafka producer/consumer settings are tuned for production throughput, not test speed. Configure `linger.ms=0`, `batch.size=1`, `auto.commit.interval.ms=100`, `auto-offset-reset=earliest`, and enable broker-level auto-topic creation. Without these, `shouldBePublished`/`shouldBeConsumed` assertions will timeout or flake. See [system-setup.md](system-setup.md#test-friendly-kafka-settings) for JVM details and [go-setup.md](go-setup.md) for Go libraries.
## Running tests
```bash
./gradlew e2eTest
./gradlew e2eTest --tests "com.myapp.e2e.OrderE2ETest"
```
## Additional resources
- [gradle-config.md](gradle-config.md) — Source set, e2eTest task, IDE integration, artifact list
- [system-setup.md](system-setup.md) — All system configuration options
- [writing-tests.md](writing-tests.md) — Complete test DSL reference with examples
- [tracing.md](tracing.md) — Tracing plugin options and validation DSL
- [custom-systems.md](custom-systems.md) — Writing your own Stove system
- [other-languages.md](other-languages.md) — Testing non-JVM apps (Go, Python, Rust, Node.js)
- [go-setup.md](go-setup.md) — Go-specific setup (process mode focus): HTTP, PostgreSQL, Kafka bridge, OTel tracing, code coverage
- [container.md](container.md) — Language-agnostic `containerApp(...)` AUT (image source is the user's responsibility, not Stove's)
- [mcp.md](mcp.md) — Stove CLI MCP endpoint for agent-driven failed-test triage
================================================
FILE: .claude/skills/stove/container.md
================================================
# Container AUT — `stove-container`
Use `stove-container` when the application under test should run as a Docker image. Works for **any language** — Go, Python, Node.js, Rust, .NET, JVM, anything that can ship in a container. Same Stove DSL, same systems, same envMapper / argsMapper model — only the runner changes.
If you want fast iteration without an image, use `stove-process` (`processApp` / `goApp`). See [other-languages.md](other-languages.md).
## Image source: not Stove's job
`containerApp(...)` only needs an **image reference**. Where that image comes from is up to the user / CI:
- **Pre-built in CI** — most common. CI publishes an image tag (e.g. `ghcr.io/acme/app:sha-abc123`); the test reads it from a system property or env var.
- **Pulled from a registry** — Testcontainers handles the pull lazily.
- **Locally built** — optionally wire a Gradle `Exec` task (`docker build`) and `dependsOn` it from your test task. This is one valid path, not a requirement.
Lead with the pre-built path. Show local-build as an optional convenience. Never frame "Stove builds your image" — Stove launches images, it does not own the build pipeline.
## When to recommend container mode
| Need | Use |
|------|-----|
| Fastest local iteration loop | `stove-process` |
| CI parity with the production image | `stove-container` |
| Catch glibc/musl, base image, locale, CA cert regressions | `stove-container` |
| Inner debug loop, breakpoints in IDE | `stove-process` |
| One repo runs both modes, branched on a system property | Both — single StoveConfig |
A common pattern: `e2eTest` uses process mode for local development; `e2eTest-container` runs container mode in CI before merge using the image CI just built and tagged.
## Setup checklist
```
- [ ] Step 1: Add stove-container dependency
- [ ] Step 2: Decide image source (CI artifact, registry pull, or optional local build)
- [ ] Step 3: Add an e2eTest-container Test task; pass the image tag as a system property
- [ ] Step 4: Wire containerApp(...) into StoveConfig
- [ ] Step 5: Pick a networking model (host network or port binding)
- [ ] Step 6: (Optional) Bind-mount data / coverage directories
```
## Step 1: Dependency
```kotlin
dependencies {
testImplementation(platform("com.trendyol:stove-bom:$stoveVersion"))
testImplementation("com.trendyol:stove-container")
// ... other stove dependencies as needed
}
```
## Step 2 + 3: Image source and Gradle wiring
The default and recommended pattern: CI (or another build step) produces an image tag, and the test task receives it via a system property.
```kotlin title="build.gradle.kts"
val containerImage = providers.environmentVariable("APP_IMAGE")
.orElse(providers.gradleProperty("app.image"))
.orElse("my-app:local") // local fallback only
tasks.register<Test>("e2eTest-container") {
useJUnitPlatform()
systemProperty("app.container.image", containerImage.get())
}
```
If you also want a Gradle-driven local build (optional), add an `Exec` task and depend on it explicitly:
```kotlin
val dockerExecutable = providers.environmentVariable("DOCKER_EXECUTABLE").getOrElse("docker")
tasks.register<Exec>("buildContainerImage") {
description = "Optional convenience: builds the AUT image locally."
commandLine(
dockerExecutable, "build",
"--file", projectDir.resolve("Dockerfile").absolutePath,
"--tag", "my-app:local",
projectDir.absolutePath
)
outputs.upToDateWhen { false }
}
// Only depend on it for the local-build path:
tasks.named<Test>("e2eTest-container-local") {
dependsOn("buildContainerImage")
systemProperty("app.container.image", "my-app:local")
}
```
The CI path uses the image already produced by the upstream build; the local path opts into building. The Stove test code does not change.
## Step 4: StoveConfig
```kotlin
import com.trendyol.stove.container.ContainerTarget
import com.trendyol.stove.container.containerApp
import com.trendyol.stove.system.application.envMapper
containerApp(
image = System.getProperty("app.container.image")
?: error("app.container.image system property not set"),
target = ContainerTarget.Server(
hostPort = 8090,
internalPort = 8090,
portEnvVar = "APP_PORT",
bindHostPort = false // host network → no need to bind
),
envProvider = envMapper {
"database.host" to "DB_HOST"
"database.port" to "DB_PORT"
"database.name" to "DB_NAME"
"kafka.bootstrapServers" to "KAFKA_BROKERS"
env("OTEL_EXPORTER_OTLP_ENDPOINT", "localhost:4317")
},
configureContainer = {
withNetworkMode("host")
// bind mounts, log consumers, capabilities — anything Testcontainers exposes
},
beforeStarted = { configurations ->
// optional pre-start hook with resolved configs
}
)
```
### `containerApp` parameters
| Parameter | Purpose |
|-----------|---------|
| `image` | Image reference, e.g. `ghcr.io/acme/app:sha-abc` or `my-app:local` |
| `target` | `ContainerTarget.Server` or `ContainerTarget.Worker` (carries readiness) |
| `registry` | Image registry override (defaults to `DEFAULT_REGISTRY`) |
| `compatibleSubstitute` | Substitute image for arch/OS compatibility |
| `command` | Override container command (appended with argsMapper output) |
| `envProvider` | `envMapper { ... }` mapping Stove configs to env vars |
| `argsProvider` | `argsMapper(prefix, separator) { ... }` for CLI-flag-driven apps |
| `beforeStarted` | Async hook with resolved configs, runs before container start |
| `configureContainer` | `GenericContainer<*>.()` — bind mounts, network mode, etc. |
| `gracefulShutdownTimeout` | Defaults to 5 seconds |
### `ContainerTarget` variants
| Variant | Use case | Default readiness |
|---------|----------|-------------------|
| `ContainerTarget.Server(hostPort, internalPort, portEnvVar, bindHostPort)` | HTTP / gRPC / TCP servers | HTTP GET `http://localhost:$hostPort/health` |
| `ContainerTarget.Worker()` | Kafka consumers, batch jobs | 2-second fixed delay |
## Step 5: Networking strategies
**Host network (Linux only)** — container shares the host network namespace. Reach Postgres / Kafka on `localhost`. Set `bindHostPort = false`:
```kotlin
target = ContainerTarget.Server(hostPort = 8090, internalPort = 8090, portEnvVar = "APP_PORT", bindHostPort = false),
configureContainer = { withNetworkMode("host") }
```
**Port binding (cross-platform)** — Stove binds `hostPort → internalPort`. The app must reach databases / brokers via shared network aliases or `host.docker.internal`:
```kotlin
target = ContainerTarget.Server(hostPort = 8090, internalPort = 8090, portEnvVar = "APP_PORT", bindHostPort = true),
configureContainer = { withNetwork(Network.SHARED) }
```
Docker Desktop on macOS / Windows does **not** support host networking — use port binding there.
## Step 6: Bind mounts (optional)
Use for any data the container or the test needs to share with the host: coverage directories, fixture seeds, read-only configs, etc. Anything Testcontainers exposes is available inside `configureContainer`.
```kotlin
configureContainer = {
withFileSystemBind(hostDir, "/inside/container")
withLogConsumer(Slf4jLogConsumer(LoggerFactory.getLogger("app")))
}
```
For Go integration coverage specifically, see [go-setup.md](go-setup.md#code-coverage).
## Running
```bash
# CI/registry image — image tag passed in
./gradlew e2eTest-container -Papp.image=ghcr.io/acme/app:sha-abc123
# or
APP_IMAGE=ghcr.io/acme/app:sha-abc123 ./gradlew e2eTest-container
# Optional local-build path (only if you wired buildContainerImage)
./gradlew e2eTest-container-local
```
## Single StoveConfig, both modes
The recipe pattern: branch on a system property to switch between starters within one config file.
```kotlin
when (resolveAutMode()) {
AutMode.Process -> processApp { /* ... */ }
AutMode.Container -> containerApp(/* ... */)
}
private fun resolveAutMode(): AutMode =
when ((System.getProperty("aut.mode") ?: "process").lowercase()) {
"process" -> AutMode.Process
"container" -> AutMode.Container
else -> error("Unsupported aut.mode")
}
```
Drive the choice from Gradle:
```kotlin
tasks.register<Test>("e2eTest") { systemProperty("aut.mode", "process") /* ... */ }
tasks.register<Test>("e2eTest-container") { systemProperty("aut.mode", "container") /* ... */ }
```
## Common pitfalls
| Symptom | Cause | Fix |
|---------|-------|-----|
| `connection refused` to Postgres / Kafka inside container | Container can't reach Testcontainers on `localhost` | `withNetworkMode("host")` (Linux) or shared network + aliases |
| Stove never sees `/health` | Wrong port / binding | Confirm `bindHostPort` matches network mode; verify app listens on `internalPort` |
| `Failed to start container application` | Image missing or unauthorized pull | Verify the image exists locally / in the registry; check `docker images` and registry credentials |
| Slow inner loop | Image build dominates | Use `stove-process` for daily dev; container mode in CI |
## Reference
- Module source: `starters/container/stove-container/`
- DSL: `starters/container/stove-container/src/main/kotlin/com/trendyol/stove/container/ContainerDsl.kt`
- Showcase (process + container in one repo): `recipes/process/golang/go-showcase/`
- Docs: `docs/other-languages/go-container.md` (Go-specific walkthrough)
================================================
FILE: .claude/skills/stove/custom-systems.md
================================================
# Writing Your Own Stove System
## Contents
- [Implement PluggedSystem](#1-implement-pluggedsystem)
- [Create a listener](#2-create-a-listener)
- [Write DSL extensions](#3-write-dsl-extensions)
- [Register the listener](#4-register-the-listener)
- [Use in tests](#5-use-in-tests)
Complete working example: `recipes/kotlin-recipes/spring-showcase/src/test-e2e/kotlin/.../setup/DbSchedulerSystem.kt`
## 1. Implement PluggedSystem
```kotlin
class DbSchedulerSystem(
override val stove: Stove
) : PluggedSystem,
AfterRunAwareWithContext<ApplicationContext>,
Reports {
lateinit var listener: StoveDbSchedulerListener
override val reportSystemName: String = "DbScheduler"
override suspend fun afterRun(context: ApplicationContext) {
listener = context.getBean()
}
override fun snapshot(): SystemSnapshot = SystemSnapshot(
system = reportSystemName,
state = mapOf("completedExecutions" to listener.getCompletedExecutionsSnapshot()),
summary = "Completed: ${listener.getCompletedExecutionsSnapshot().size} task(s)"
)
suspend inline fun <reified T : Any> shouldBeExecuted(
atLeastIn: Duration = 5.seconds,
noinline condition: T.() -> Boolean
): DbSchedulerSystem = report(
action = "Assert task executed: ${T::class.simpleName}",
expected = "Task with ${T::class.simpleName} payload executed".some(),
metadata = mapOf("timeout" to atLeastIn.toString())
) {
listener.waitUntilObservedSuccessfully(atLeastIn, T::class, condition)
}.let { this }
override fun close() = Unit
}
```
### Lifecycle interfaces
| Interface | When Called | Use Case |
|---|---|---|
| `PluggedSystem` | Always (required) | Base interface, provides `close()` |
| `RunAware` | Before app starts | System needs to do setup before the app |
| `AfterRunAware<T>` | After app starts | Receives the test system instance |
| `AfterRunAwareWithContext<T>` | After app starts | Receives app DI container (e.g., `ApplicationContext`) |
| `ExposesConfiguration` | During setup | System exposes config to the application (like containers) |
| `Reports` | On test failure | Contributes to failure reports via `snapshot()` |
## 2. Create a listener
Observes what happens inside the running application:
```kotlin
class StoveDbSchedulerListener : AbstractSchedulerListener() {
private val completedExecutions: ConcurrentMap<String, ExecutionComplete> = ConcurrentHashMap()
override fun onExecutionComplete(executionComplete: ExecutionComplete) {
completedExecutions[executionComplete.execution.taskInstance.id] = executionComplete
}
suspend fun <T : Any> waitUntilObservedSuccessfully(
atLeastIn: Duration, clazz: KClass<T>, condition: (T) -> Boolean
): Collection<ExecutionComplete> { /* poll until match or timeout */ }
}
```
## 3. Write DSL extensions
```kotlin
// Registration — used in Stove().with { }
@StoveDsl
fun WithDsl.dbScheduler(): Stove =
this.stove.getOrRegister(DbSchedulerSystem(this.stove)).let { this.stove }
// Validation — used in stove { }
@StoveDsl
suspend fun ValidationDsl.tasks(validation: suspend DbSchedulerSystem.() -> Unit): Unit =
validation(this.stove.getOrNone<DbSchedulerSystem>().getOrElse {
throw SystemNotRegisteredException(DbSchedulerSystem::class)
})
```
## 4. Register the listener
```kotlin
Stove().with {
dbScheduler()
springBoot(
runner = { params ->
com.myapp.run(params) {
addTestDependencies {
bean<StoveDbSchedulerListener>(isPrimary = true)
}
}
}
)
}.run()
```
## 5. Use in tests
```kotlin
stove {
http { postAndExpectBody<OrderResponse>("/api/orders", body = request.some()) { /* ... */ } }
tasks {
shouldBeExecuted<OrderEmailPayload> {
this.orderId == orderId && this.userId == userId
}
}
}
```
**Pattern**: listener captures events -> system exposes assertions -> DSL extensions make it ergonomic -> `report()` integrates with reporting.
## 6. Extending built-in systems
Add domain-specific helpers to existing systems without creating new ones:
```kotlin
@StoveDsl
suspend fun KafkaSystem.publishWithCorrelationId(
topic: String,
message: Any,
correlationId: String = UUID.randomUUID().toString()
) {
publish(topic, message, headers = mapOf("X-Correlation-ID" to correlationId))
}
// Usage
kafka { publishWithCorrelationId("orders", event, "corr-123") }
```
================================================
FILE: .claude/skills/stove/go-setup.md
================================================
# Go Application Setup with Stove
Complete guide for testing Go applications with Stove. Covers HTTP, PostgreSQL, Kafka (with bridge), OpenTelemetry tracing, dashboard, MCP triage, and integration coverage.
This skill focuses on **process mode** (`stove-process` / `goApp`) — fastest local iteration. For container-image AUT (`stove-container` / `containerApp`) — language-agnostic, image source is your responsibility — see [container.md](container.md). For agent-driven failure triage via the `stove` CLI MCP endpoint, see [mcp.md](mcp.md).
The same `StoveConfig.kt` can serve both modes by branching on a system property like `-Daut.mode=process|container` (see the showcase recipe).
## Setup Checklist
```
- [ ] Step 1: Create Go app with env var config + health endpoint + SIGTERM handling
- [ ] Step 2: Add OpenTelemetry instrumentation (otelhttp, otelsql)
- [ ] Step 3: Add Kafka with Stove bridge interceptors (optional)
- [ ] Step 4: Add stove-process dependency (provides goApp() DSL)
- [ ] Step 5: Create test-e2e source set + StoveConfig
- [ ] Step 6: Configure Gradle build (go build + e2eTest)
- [ ] Step 7: Write tests
```
## Step 1: Go app requirements
The Go app must:
- Read config from **environment variables**
- Expose **GET /health** returning 200
- Handle **SIGTERM** for graceful shutdown
Key env vars:
| Variable | Purpose |
|----------|---------|
| `APP_PORT` | HTTP listen port |
| `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASS` | PostgreSQL |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP gRPC endpoint |
| `KAFKA_BROKERS` | Kafka broker addresses |
| `KAFKA_LIBRARY` | Kafka client: `sarama`, `franz`, or `segmentio` (default: `sarama`) |
| `STOVE_KAFKA_BRIDGE_PORT` | Stove bridge gRPC port (test-only) |
| `GOCOVERDIR` | Directory for Go integration test coverage data (test-only) |
## Step 2: OpenTelemetry
```go
// HTTP: wrap mux with otelhttp
handler := otelhttp.NewHandler(mux, "http.request")
// DB: use otelsql instead of database/sql
db, _ := otelsql.Open("postgres", connStr, otelsql.WithAttributes(semconv.DBSystemPostgreSQL))
// Tracing: use WithSyncer for tests (not WithBatcher)
tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exporter), ...)
// Propagation: must set W3C TraceContext for Stove trace correlation
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{}, propagation.Baggage{},
))
```
## Step 3: Kafka bridge
The Stove Kafka bridge library lives at `go/stove-kafka/`. It has a library-agnostic core and client-specific subpackages.
### Architecture
```
go/stove-kafka/
bridge.go # Core: Bridge, PublishedMessage, ConsumedMessage (library-agnostic)
sarama/ # IBM/sarama interceptors
interceptors.go
franz/ # twmb/franz-go hooks
hooks.go
segmentio/ # segmentio/kafka-go helpers
bridge.go
stoveobserver/ # Generated gRPC code
```
### Add the dependency
```bash
go get github.com/trendyol/stove/go/stove-kafka
```
### Initialize bridge + wire into your Kafka client
**IBM/sarama:**
```go
import (
stovekafka "github.com/trendyol/stove/go/stove-kafka"
stovesarama "github.com/trendyol/stove/go/stove-kafka/sarama"
)
bridge, _ := stovekafka.NewBridgeFromEnv()
defer bridge.Close()
config := sarama.NewConfig()
config.Producer.Interceptors = []sarama.ProducerInterceptor{
&stovesarama.ProducerInterceptor{Bridge: bridge},
}
config.Consumer.Interceptors = []sarama.ConsumerInterceptor{
&stovesarama.ConsumerInterceptor{Bridge: bridge},
}
```
**twmb/franz-go:**
```go
import (
stovekafka "github.com/trendyol/stove/go/stove-kafka"
"github.com/trendyol/stove/go/stove-kafka/franz"
)
bridge, _ := stovekafka.NewBridgeFromEnv()
defer bridge.Close()
client, _ := kgo.NewClient(
kgo.SeedBrokers("localhost:9092"),
kgo.WithHooks(&franz.Hook{Bridge: bridge}),
)
```
**segmentio/kafka-go:**
```go
import (
stovekafka "github.com/trendyol/stove/go/stove-kafka"
"github.com/trendyol/stove/go/stove-kafka/segmentio"
)
bridge, _ := stovekafka.NewBridgeFromEnv()
defer bridge.Close()
// After producing
_ = writer.WriteMessages(ctx, msgs...)
segmentio.ReportWritten(ctx, bridge, msgs...)
// After consuming
msg, _ := reader.ReadMessage(ctx)
segmentio.ReportRead(ctx, bridge, msg)
```
### Other libraries (e.g. confluent-kafka-go)
The core bridge has no Kafka client dependency. For any unsupported library, use the core types directly:
```go
import stovekafka "github.com/trendyol/stove/go/stove-kafka"
_ = bridge.ReportPublished(ctx, &stovekafka.PublishedMessage{
Topic: msg.Topic, Key: string(msg.Key), Value: msg.Value, Headers: myHeaders(msg),
})
_ = bridge.ReportConsumed(ctx, &stovekafka.ConsumedMessage{
Topic: msg.Topic, Key: string(msg.Key), Value: msg.Value,
Partition: msg.Partition, Offset: msg.Offset, Headers: myHeaders(msg),
})
_ = bridge.ReportCommitted(ctx, msg.Topic, msg.Partition, msg.Offset+1)
```
### How it works
- All subpackages convert client-specific types to core `PublishedMessage`/`ConsumedMessage` and call bridge methods
- Consumer interceptors/helpers pre-report commit at `offset+1` (needed for `shouldBeConsumed`)
- All Bridge methods are nil-safe: `(*Bridge)(nil).ReportPublished(...)` is a no-op
- All interceptors/hooks/helpers check for nil bridge first — zero overhead in production
### Test-friendly Kafka settings (Go side)
When running against Testcontainers (Stove e2e tests), configure Kafka clients for **fast feedback**. Default production settings (large batches, long commit intervals, no auto-topic creation) cause timeouts, missed messages, and flaky tests.
**Key principles:**
1. **Auto-create topics** — test containers may not have topics pre-created; without this, produces fail silently or block
2. **Small batch size / low batch timeout** — flush produces immediately so `shouldBePublished` sees them
3. **Short auto-commit interval** — make consumed offsets visible to Stove bridge quickly so `shouldBeConsumed` passes
4. **Unique consumer groups per test run** — prevent offset carryover between runs (e.g. `"myapp-" + library`)
**IBM/sarama:**
```go
config := sarama.NewConfig()
config.Producer.Return.Successes = true
config.Consumer.Offsets.Initial = sarama.OffsetOldest
config.Consumer.Offsets.AutoCommit.Interval = 100 * time.Millisecond
// sarama relies on broker-side auto.create.topics.enable (no client-side setting)
```
**twmb/franz-go:**
```go
client, _ := kgo.NewClient(
kgo.SeedBrokers(brokerList...),
kgo.AllowAutoTopicCreation(), // client-side topic creation
kgo.AutoCommitInterval(100 * time.Millisecond), // fast offset commits
kgo.ConsumeResetOffset(kgo.NewOffset().AtStart()),
kgo.WithHooks(&franz.Hook{Bridge: bridge}),
)
```
**segmentio/kafka-go:**
```go
// Writer — flush immediately, auto-create topics
writer := &kafka.Writer{
Addr: kafka.TCP(brokerList...),
BatchSize: 1,
BatchTimeout: 10 * time.Millisecond,
RequiredAcks: kafka.RequireAll,
AllowAutoTopicCreation: true,
}
// Reader — fast commits, low wait
reader := kafka.NewReader(kafka.ReaderConfig{
Brokers: brokerList,
GroupID: groupID,
Topic: topic,
MinBytes: 1,
MaxBytes: 10e6,
CommitInterval: 100 * time.Millisecond,
MaxWait: 500 * time.Millisecond,
})
```
**franz-go: separate producer and consumer clients.** Using a single `kgo.Client` for both produce and consume causes consumer group coordination to block `ProduceSync`, leading to 10-30s delays. Always create two clients:
```go
// Producer — no consumer group overhead
producerClient, _ := kgo.NewClient(
kgo.SeedBrokers(brokerList...),
kgo.AllowAutoTopicCreation(),
kgo.WithHooks(hook),
)
// Consumer — consumer group coordination won't block produces
consumerClient, _ := kgo.NewClient(
kgo.SeedBrokers(brokerList...),
kgo.ConsumeTopics(topic),
kgo.ConsumerGroup(groupID),
kgo.ConsumeResetOffset(kgo.NewOffset().AtStart()),
kgo.AutoCommitInterval(100 * time.Millisecond),
kgo.AllowAutoTopicCreation(),
kgo.WithHooks(hook),
)
```
**Common pitfall — consumer group offset carryover:** If running the same tests against multiple Kafka libraries sequentially (e.g. sarama → franz → segmentio), use a unique consumer group per library. Otherwise the second run sees committed offsets from the first and skips messages:
```go
groupID := "myapp-" + library // e.g. "myapp-sarama", "myapp-franz"
```
## Step 4: Add stove-process dependency
The `stove-process` module provides `goApp()` out of the box — no custom `ApplicationUnderTest` needed. It supports passing configs as environment variables (`envMapper`) or CLI arguments (`argsMapper`). Go apps typically use env vars.
```kotlin
dependencies {
testImplementation(stoveLibs.stoveProcess) // or "com.trendyol:stove-process"
}
```
Source: `starters/process/stove-process/`
## Step 5: StoveConfig
```kotlin
Stove().with {
httpClient { HttpClientSystemOptions(baseUrl = "http://localhost:$APP_PORT") }
dashboard { DashboardSystemOptions(appName = "go-showcase") }
tracing { enableSpanReceiver(port = OTLP_PORT) }
kafka {
KafkaSystemOptions(
configureExposedConfiguration = { cfg ->
listOf("kafka.bootstrapServers=${cfg.bootstrapServers}")
}
)
}
postgresql {
PostgresqlOptions(
databaseName = "stove",
configureExposedConfiguration = { cfg ->
listOf(
"database.host=${cfg.host}", "database.port=${cfg.port}",
"database.name=stove",
"database.username=${cfg.username}", "database.password=${cfg.password}"
)
}
).migrations { register<ProductMigration>() }
}
goApp(
target = ProcessTarget.Server(port = APP_PORT, portEnvVar = "APP_PORT"),
envProvider = envMapper {
"database.host" to "DB_HOST"
"database.port" to "DB_PORT"
"database.name" to "DB_NAME"
"database.username" to "DB_USER"
"database.password" to "DB_PASS"
"kafka.bootstrapServers" to "KAFKA_BROKERS"
env("OTEL_EXPORTER_OTLP_ENDPOINT", "localhost:$OTLP_PORT")
env("KAFKA_LIBRARY") { System.getProperty("kafka.library") ?: "sarama" }
env("STOVE_KAFKA_BRIDGE_PORT", stoveKafkaBridgePortDefault)
}
)
}.run()
```
## Step 6: Gradle
```kotlin
val goBinary = layout.buildDirectory.file("go-app").get().asFile
tasks.register<Exec>("buildGoApp") {
commandLine("go", "build", "-o", goBinary.absolutePath, ".")
inputs.files(fileTree(".") { include("*.go", "go.mod", "go.sum") })
outputs.file(goBinary)
}
// Per-library e2e test tasks — each passes KAFKA_LIBRARY to the Go app
val kafkaLibraries = listOf("sarama", "franz", "segmentio")
val kafkaE2eTasks = kafkaLibraries.mapIndexed { index, lib ->
tasks.register<Test>("e2eTest_$lib") {
dependsOn("buildGoApp")
systemProperty("go.app.binary", goBinary.absolutePath)
systemProperty("kafka.library", lib)
if (index > 0) mustRunAfter("e2eTest_${kafkaLibraries[index - 1]}")
}
}
tasks.named<Test>("e2eTest") { dependsOn(kafkaE2eTasks); enabled = false }
dependencies {
testImplementation(stoveLibs.stove)
testImplementation(stoveLibs.stoveProcess)
testImplementation(stoveLibs.stovePostgres)
testImplementation(stoveLibs.stoveHttp)
testImplementation(stoveLibs.stoveTracing)
testImplementation(stoveLibs.stoveDashboard)
testImplementation(stoveLibs.stoveKafka)
testImplementation(stoveLibs.stoveExtensionsKotest)
}
```
## Step 7: Write tests
```kotlin
class GoShowcaseTest : FunSpec({
test("create product, verify DB + Kafka + traces") {
stove {
var productId: String? = null
http {
postAndExpectBody<ProductResponse>(
uri = "/api/products",
body = CreateProductRequest(name = "Test", price = 42.99).some()
) { actual ->
actual.status shouldBe 201
productId = actual.body().id
}
}
postgresql {
shouldQuery<ProductRow>(
query = "SELECT id, name, price FROM products WHERE id = '$productId'",
mapper = { row -> ProductRow(row.string("id"), row.string("name"), row.double("price")) }
) { rows -> rows.size shouldBe 1 }
}
kafka {
shouldBePublished<ProductCreatedEvent>(10.seconds) {
actual.name == "Test"
}
}
tracing {
waitForSpans(4, 5000)
shouldContainSpan("http.request")
shouldNotHaveFailedSpans()
}
}
}
test("consume Kafka events") {
stove {
var productId: String? = null
http {
postAndExpectBody<ProductResponse>(
uri = "/api/products",
body = CreateProductRequest(name = "Original", price = 10.0).some()
) { actual -> productId = actual.body().id }
}
kafka {
publish("product.update", ProductUpdateEvent(id = productId!!, name = "Updated", price = 99.99))
shouldBeConsumed<ProductUpdateEvent>(10.seconds) {
actual.id == productId && actual.name == "Updated"
}
}
postgresql {
shouldQuery<ProductRow>(
query = "SELECT id, name, price FROM products WHERE id = '$productId'",
mapper = { row -> ProductRow(row.string("id"), row.string("name"), row.double("price")) }
) { rows -> rows.first().name shouldBe "Updated" }
}
}
}
})
```
## Code Coverage
Go 1.20+ supports integration test coverage for binaries not run via `go test`. Build with `go build -cover`, set `GOCOVERDIR`, and coverage data is written on graceful shutdown — fits perfectly with Stove's lifecycle.
### Gradle setup
Enable with `-Pgo.coverage=true`:
```kotlin
val coverageEnabled = providers.gradleProperty("go.coverage")
.map { it.toBoolean() }.getOrElse(false)
val goCoverDirPath = layout.buildDirectory.dir("go-coverage").get().asFile.absolutePath
// Build with -cover when enabled
tasks.register<Exec>("buildGoApp") {
val args = mutableListOf("go", "build")
if (coverageEnabled) args.add("-cover")
args.addAll(listOf("-o", goBinary.absolutePath, "."))
commandLine(args)
}
// Pass GOCOVERDIR to test JVM, disable build cache for coverage runs
tasks.register<Test>("e2eTest_sarama") {
if (coverageEnabled) {
systemProperty("go.cover.dir", goCoverDirPath)
outputs.cacheIf { false } // Coverage data is a side effect
}
}
// Coverage report tasks (register only when coverage is enabled)
if (coverageEnabled) {
tasks.register<Exec>("goCoverageReport") {
mustRunAfter(kafkaE2eTasks)
commandLine("go", "tool", "covdata", "textfmt", "-i=$goCoverDirPath", "-o=$goCoverOutPath")
}
tasks.register<Exec>("goCoverageSummary") { dependsOn("goCoverageReport"); /* go tool cover -func */ }
tasks.register<Exec>("goCoverageHtml") { dependsOn("goCoverageReport"); /* go tool cover -html */ }
tasks.register("e2eTestWithCoverage") {
dependsOn(kafkaE2eTasks)
finalizedBy("goCoverageSummary", "goCoverageHtml")
}
}
```
### StoveConfig
Pass `GOCOVERDIR` via `envMapper` — empty when disabled, Go ignores it:
```kotlin
env("GOCOVERDIR") {
System.getProperty("go.cover.dir")?.also { java.io.File(it).mkdirs() } ?: ""
}
```
### SIGPIPE handling
When Go runs under Java's `ProcessBuilder`, stdout pipe can close before process exit. Log writes trigger SIGPIPE (exit 141), killing the process before coverage flush. Fix:
```go
func main() {
signal.Ignore(syscall.SIGPIPE) // Ensures clean shutdown + coverage flush
// ...
}
```
### Running with coverage
```bash
./gradlew e2eTestWithCoverage -Pgo.coverage=true
# Output: per-function coverage + HTML report at build/go-coverage/coverage.html
```
## Running
```bash
# From the go-showcase directory — runs all three Kafka libraries
cd recipes/process/golang/go-showcase
./gradlew e2eTest
# Run a specific library only
./gradlew e2eTest_sarama
./gradlew e2eTest_franz
./gradlew e2eTest_segmentio
# With Go code coverage
./gradlew e2eTestWithCoverage -Pgo.coverage=true
```
## Go dependencies
```
github.com/trendyol/stove/go/stove-kafka # Stove Kafka bridge (core)
github.com/trendyol/stove/go/stove-kafka/sarama # IBM/sarama interceptors
github.com/trendyol/stove/go/stove-kafka/franz # twmb/franz-go hooks
github.com/trendyol/stove/go/stove-kafka/segmentio # segmentio/kafka-go helpers
github.com/XSAM/otelsql # database/sql instrumentation
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp # HTTP instrumentation
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc # OTLP exporter
google.golang.org/grpc # gRPC
```
## Reference
- Process module (goApp DSL): `starters/process/stove-process/`
- Container module (containerApp DSL): `starters/container/stove-container/`
- Full working example (process + container in one repo): `recipes/process/golang/go-showcase/`
- Bridge library source: `go/stove-kafka/`
- Docs:
- `docs/other-languages/go.md` — overview / mode picker
- `docs/other-languages/go-process.md` — process mode walkthrough
- `docs/other-languages/go-container.md` — container mode walkthrough
- Sibling skills:
- [container.md](container.md) — language-agnostic container AUT
- [mcp.md](mcp.md) — MCP triage on failed runs
- [other-languages.md](other-languages.md) — non-JVM overview
================================================
FILE: .claude/skills/stove/gradle-config.md
================================================
# Gradle Configuration
## Contents
- [Dependencies (BOM)](#dependencies-bom)
- [Register test-e2e source set](#register-test-e2e-source-set)
- [Register e2eTest task](#register-e2etest-task)
- [IDE integration](#ide-integration)
- [JUnit base test class](#junit-base-test-class)
- [Available artifacts](#available-artifacts)
## Dependencies (BOM)
Stove e2e tests are Kotlin-first. Even for Java/Scala projects, keep e2e test sources in `src/test-e2e/kotlin`.
```kotlin
dependencies {
testImplementation(platform("com.trendyol:stove-bom:$stoveVersion"))
testImplementation("com.trendyol:stove")
testImplementation("com.trendyol:stove-spring") // or stove-ktor / stove-quarkus / stove-micronaut
testImplementation("com.trendyol:stove-extensions-kotest") // or stove-extensions-junit
// Add only what you need:
testImplementation("com.trendyol:stove-http")
testImplementation("com.trendyol:stove-postgres")
testImplementation("com.trendyol:stove-mysql")
testImplementation("com.trendyol:stove-mssql")
testImplementation("com.trendyol:stove-cassandra")
testImplementation("com.trendyol:stove-mongodb")
testImplementation("com.trendyol:stove-redis")
testImplementation("com.trendyol:stove-elasticsearch")
testImplementation("com.trendyol:stove-couchbase")
testImplementation("com.trendyol:stove-kafka") // standalone Kafka assertions
testImplementation("com.trendyol:stove-spring-kafka") // Spring Kafka assertions + interceptor
testImplementation("com.trendyol:stove-wiremock")
testImplementation("com.trendyol:stove-grpc")
testImplementation("com.trendyol:stove-grpc-mock")
testImplementation("com.trendyol:stove-tracing")
testImplementation("com.trendyol:stove-dashboard")
testImplementation("com.trendyol:stove-process") // non-JVM process AUT
testImplementation("com.trendyol:stove-container") // non-JVM container AUT
}
```
## Register test-e2e source set
```kotlin
sourceSets {
@Suppress("LocalVariableName")
val `test-e2e` by creating {
compileClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.main.get().output
}
val testE2eImplementation by configurations.getting {
extendsFrom(configurations.testImplementation.get())
}
configurations["testE2eRuntimeOnly"].extendsFrom(configurations.runtimeOnly.get())
}
```
## Register e2eTest task
```kotlin
tasks.register<Test>("e2eTest") {
description = "Runs e2e tests."
group = "verification"
testClassesDirs = sourceSets["test-e2e"].output.classesDirs
classpath = sourceSets["test-e2e"].runtimeClasspath
useJUnitPlatform()
reports {
junitXml.required.set(true)
html.required.set(true)
}
}
```
## IDE integration
```kotlin
idea {
module {
testSources.from(sourceSets["test-e2e"].allSource.sourceDirectories)
testResources.from(sourceSets["test-e2e"].resources.sourceDirectories)
}
}
```
## Resolve API ambiguity from local artifacts
When API names/signatures are unclear, inspect locally downloaded Stove artifacts instead of guessing.
```bash
# Find Stove artifacts in Gradle cache
find ~/.gradle/caches/modules-2/files-2.1 -path "*com.trendyol/stove-*/*/*.jar" | head -n 20
# Find Stove artifacts in Maven local repo
find ~/.m2/repository/com/trendyol -name "stove-*.jar" | head -n 20
# List classes to locate exact type names
jar tf ~/.m2/repository/com/trendyol/stove-spring-kafka/<version>/stove-spring-kafka-<version>.jar | rg "TestSystem|Kafka"
# Inspect method signatures quickly
javap -classpath ~/.m2/repository/com/trendyol/stove-spring-kafka/<version>/stove-spring-kafka-<version>.jar \
com.trendyol.stove.kafka.TestSystemKafkaInterceptor
```
Prefer `*-sources.jar` when available for more accurate reading of function names, generic types, and usage patterns.
## JUnit base test class
Use this instead of `AbstractProjectConfig` when using JUnit:
```kotlin
@ExtendWith(StoveJUnitExtension::class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
abstract class BaseE2ETest {
companion object {
@JvmStatic @BeforeAll
fun setup() = runBlocking {
Stove().with { /* systems */ }.run()
}
@JvmStatic @AfterAll
fun teardown() = runBlocking { Stove.stop() }
}
}
```
## Available artifacts
| Artifact | Description |
|---|---|
| `stove` | Core framework |
| `stove-spring` | Spring Boot starter |
| `stove-ktor` | Ktor starter |
| `stove-quarkus` | Quarkus starter |
| `stove-micronaut` | Micronaut starter |
| `stove-http` | HTTP client system |
| `stove-postgres` | PostgreSQL system |
| `stove-mysql` | MySQL system |
| `stove-mssql` | MSSQL system |
| `stove-cassandra` | Cassandra system |
| `stove-mongodb` | MongoDB system |
| `stove-redis` | Redis system |
| `stove-elasticsearch` | Elasticsearch system |
| `stove-couchbase` | Couchbase system |
| `stove-kafka` | Standalone Kafka system |
| `stove-spring-kafka` | Spring Kafka (adds `shouldBeConsumed`, `shouldBeFailed`, `shouldBeRetried`) |
| `stove-wiremock` | WireMock system |
| `stove-grpc` | gRPC client system |
| `stove-grpc-mock` | gRPC mock server system |
| `stove-tracing` | Tracing system |
| `stove-dashboard` | Dashboard system (streams events to stove CLI) |
| `stove-process` | Process-based AUT starter (`processApp`, `goApp`) |
| `stove-container` | Container-based AUT starter (`containerApp`) |
| `stove-extensions-kotest` | Kotest reporting integration |
| `stove-extensions-junit` | JUnit reporting integration |
================================================
FILE: .claude/skills/stove/mcp.md
================================================
# Stove MCP — Agent Triage
The Stove CLI exposes a local **Model Context Protocol** endpoint at `http://localhost:4040/mcp`. Agents use it to inspect failed end-to-end tests through compact, structured tools instead of loading raw logs into context.
Use MCP as an optimization, not a dependency. If MCP is unavailable, fall back to normal test output, Stove failure reports, and logs.
## When to use this skill
- The user is testing with Stove and a recent run has failures
- The user mentions "MCP", "stove failures", or asks for triage of a Stove run
- An agent task instruction says to prefer the local Stove MCP endpoint
## Discovery
When `stove` is running, the startup banner prints the endpoint:
```text
Stove CLI v0.24.0 running
UI: http://localhost:4040
REST: http://localhost:4040/api/v1
MCP: http://localhost:4040/mcp
gRPC: localhost:4041
```
Or query metadata:
```bash
curl -s http://localhost:4040/api/v1/meta
```
```json
{
"stove_cli_version": "0.24.0",
"mcp": {
"enabled": true,
"transport": "streamable-http",
"endpoint": "http://localhost:4040/mcp",
"scope": "read-only-test-observability"
}
}
```
## MCP client config (generic)
```json
{
"mcpServers": {
"stove": {
"transport": "streamable-http",
"url": "http://localhost:4040/mcp"
}
}
}
```
Exact keys vary by agent runtime. The endpoint URL is the load-bearing value.
## Agent Workflow (the only correct order)
1. Call `stove_failures` first.
2. Pick a specific `run_id` and `test_id` from the result. **Never infer a test selector from names alone** — multiple apps and runs can contain duplicate test names.
3. Call `stove_failure_detail` with that exact `run_id + test_id` for the compact failure packet.
4. Drill into `stove_timeline`, `stove_trace`, or `stove_snapshot` only when needed.
5. Use `stove_raw_evidence` for one specific entry / span / snapshot when the compact view isn't enough.
6. If MCP is missing data, fall back to normal test output and logs.
Every failure result includes ready-to-use next tool calls — use them, don't guess.
## Data hierarchy
```
database
-> apps by app_name
-> runs by run_id
-> tests by test_id
-> entries, spans, snapshots
```
`app_name` is the label set in `DashboardSystemOptions(appName = "...")` on the test side. `run_id + test_id` is the only authoritative selector.
## Tools
| Tool | Purpose |
|------|---------|
| `stove_apps` | Apps recorded in the dashboard database |
| `stove_runs` | Runs, filterable by app and status |
| `stove_failures` | Default entrypoint — failed tests grouped by app and run |
| `stove_failure_detail` | Compact detail for one exact failed test |
| `stove_timeline` | Ordered test actions, failure-focused by default |
| `stove_trace` | Critical path and exception evidence from correlated spans |
| `stove_snapshot` | System snapshot summaries with targeted JSON drill-down |
| `stove_raw_evidence` | Capped raw lookup for one entry, span, or snapshot |
## Token Budgeting
Tools default to compact output. Large payloads are truncated deterministically and include omitted counts or follow-up tool calls. Sensitive keys (`authorization`, `cookie`, `password`, `secret`, `token`, `apiKey`, `credential`) are redacted before return.
Use `budget` to dial detail:
```json
{ "budget": "tiny" } // tiny | compact | full
```
Tools that expose raw evidence also accept `max_chars`.
## Security
- **Read-only**: no tools to clear data, retry tests, delete runs, or mutate snapshots.
- **Local-only**: `/mcp` accepts loopback clients and localhost `Host`/`Origin` headers. Non-local hosts are rejected (mitigates DNS rebinding).
- Safe to run on a dev machine; do not expose externally.
## Troubleshooting
If MCP is unreachable:
- confirm `stove` is running (`brew install Trendyol/trendyol-tap/stove` then `stove`)
- check the startup banner for the actual port (some installs use a custom one)
- open `http://localhost:4040/api/v1/meta` and verify `mcp.enabled` is `true`
- make sure the agent runtime is configured with `http://localhost:4040/mcp`
- fall back to normal test output and logs if the endpoint cannot be reached
If MCP returns no failures:
- the latest recorded runs may have passed
- the test config may not register `stove-dashboard` (no data is being recorded)
- the test run may still be in progress
## Recommended agent instruction
Add to your project's agent rules / system prompt:
```text
When Stove is running, prefer the local Stove MCP endpoint for failed-test triage.
Start with stove_failures, then use the returned run_id + test_id with
stove_failure_detail. Drill into stove_timeline, stove_trace, or stove_snapshot
only when needed. If MCP is unavailable, ambiguous, or incomplete, fall back to
normal test output, Stove reports, and logs.
```
## Reference
- Component docs: `docs/Components/21-mcp.md`
- Dashboard component (data source): `docs/Components/18-dashboard.md`
================================================
FILE: .claude/skills/stove/other-languages.md
================================================
# Testing Non-JVM Applications with Stove
Stove can test any application that speaks HTTP, databases, and messaging --- regardless of the language. Two starters:
- **`stove-process`** — host binary, fastest iteration loop (`processApp` / `goApp`)
- **`stove-container`** — Docker image, CI parity with the production artifact (`containerApp`). See [container.md](container.md) for the full container guide.
Same Stove DSL, same systems, same env/args mapping. The only difference is *how* the AUT starts.
For Stove + AI agent triage on failed runs, see [mcp.md](mcp.md).
## Requirements
Your application must:
1. **Accept configuration** --- via environment variables, CLI arguments, or both
2. **Handle SIGTERM** --- for clean test teardown
3. **Optional: expose a readiness endpoint** --- HTTP health check, TCP port, or custom probe
## Setup Checklist
```
- [ ] Step 1: Add `stove-process` or `stove-container` dependency
- [ ] Step 2: Create test-e2e source set layout
- [ ] Step 3: Configure Gradle (build app + e2eTest task)
- [ ] Step 4: Create StoveConfig with systems + processApp/goApp
- [ ] Step 5: Instrument app with OpenTelemetry (optional)
- [ ] Step 6: Add Kafka bridge (optional, Go only for now)
- [ ] Step 7: Write tests using stove {} DSL
```
## Step 1: Add dependency
```kotlin
dependencies {
testImplementation(platform("com.trendyol:stove-bom:$stoveVersion"))
testImplementation("com.trendyol:stove-process")
testImplementation("com.trendyol:stove-container") // if AUT runs as Docker image
// ... other stove dependencies as needed
}
```
## Step 2-3: Project structure, Gradle
Same as JVM setup (see SKILL.md). Build your app binary before tests:
```kotlin
val appSourceDir = project.file("my-app")
val appBinary = project.layout.buildDirectory.file("my-app").get().asFile
tasks.register<Exec>("buildApp") {
workingDir = appSourceDir
commandLine("go", "build", "-o", appBinary.absolutePath, ".") // or npm, cargo, etc.
inputs.files(fileTree(appSourceDir) { include("*.go", "go.mod", "go.sum") })
outputs.file(appBinary)
}
tasks.named<Test>("e2eTest") {
dependsOn("buildApp")
systemProperty("app.binary", appBinary.absolutePath)
}
```
## Step 4: StoveConfig with processApp / goApp / containerApp
Use `processApp()` for any language binary, `goApp()` as a Go convenience, or `containerApp()` when tests should launch an image directly.
```kotlin
Stove().with {
httpClient { HttpClientSystemOptions(baseUrl = "http://localhost:$APP_PORT") }
tracing { enableSpanReceiver(port = OTLP_PORT) }
dashboard { DashboardSystemOptions(appName = "my-app") }
postgresql {
PostgresqlOptions(
databaseName = "mydb",
configureExposedConfiguration = { cfg ->
listOf(
"database.host=${cfg.host}",
"database.port=${cfg.port}",
"database.name=mydb",
"database.username=${cfg.username}",
"database.password=${cfg.password}"
)
}
).migrations { register<SchemaMigration>() }
}
kafka {
KafkaSystemOptions(
configureExposedConfiguration = { cfg ->
listOf("kafka.bootstrapServers=${cfg.bootstrapServers}")
}
)
}
// For Go apps — uses go.app.binary system property by default
goApp(
target = ProcessTarget.Server(port = APP_PORT, portEnvVar = "APP_PORT"),
envProvider = envMapper {
"database.host" to "DB_HOST"
"database.port" to "DB_PORT"
"database.name" to "DB_NAME"
"database.username" to "DB_USER"
"database.password" to "DB_PASS"
"kafka.bootstrapServers" to "KAFKA_BROKERS"
env("OTEL_EXPORTER_OTLP_ENDPOINT", "localhost:$OTLP_PORT")
}
)
// For any other language — specify the full command
// processApp {
// ProcessApplicationOptions(
// command = listOf("python3", "server.py"),
// target = ProcessTarget.Server(port = APP_PORT, portEnvVar = "PORT"),
// envProvider = envMapper { "database.host" to "DB_HOST" }
// )
// }
// For apps that prefer CLI arguments instead of env vars
// processApp {
// ProcessApplicationOptions(
// command = listOf("/path/to/rust-server"),
// target = ProcessTarget.Server(port = APP_PORT),
// argsProvider = argsMapper(prefix = "--", separator = "=") {
// "database.host" to "db-host" // --db-host=localhost
// "database.port" to "db-port" // --db-port=5432
// }
// )
// }
}.run()
```
### ProcessTarget variants
| Variant | Use case | Default readiness |
|---------|----------|-------------------|
| `ProcessTarget.Server(port, portEnvVar)` | HTTP APIs, gRPC servers, TCP servers | HTTP GET `/health` |
| `ProcessTarget.Worker()` | Kafka consumers, batch jobs, CLI tools | 2-second fixed delay |
### ReadinessStrategy variants
| Strategy | Use case |
|----------|----------|
| `ReadinessStrategy.HttpGet(url, timeout, retries, retryDelay, expectedStatusCodes)` | REST APIs with health endpoint |
| `ReadinessStrategy.TcpPort(port)` | gRPC servers, raw TCP (no HTTP) |
| `ReadinessStrategy.Probe { ... }` | Custom readiness (file, DB query, etc.) |
| `ReadinessStrategy.FixedDelay(duration)` | Simple workers with no readiness signal |
### Configuration passing: envMapper and argsMapper
Two mechanisms to pass Stove configs to the process — use one or both:
**envMapper** — environment variables:
```kotlin
envMapper {
"stove.config.key" to "ENV_VAR_NAME" // map Stove config → env var
env("STATIC_VAR", "value") // static env var
env("COMPUTED_VAR") { computeValue() } // computed env var
}
```
**argsMapper** — CLI arguments (appended to the command):
```kotlin
// --db-host=localhost --db-port=5432
argsMapper(prefix = "--", separator = "=") {
"database.host" to "db-host" // map Stove config → CLI flag
arg("verbose") // boolean flag
arg("log-level", "debug") // static flag
}
// -h localhost -p 5432 (space separator → two args per flag)
argsMapper(prefix = "-", separator = " ") {
"database.host" to "h"
"database.port" to "p"
}
```
## Step 5: OpenTelemetry (optional)
Use your language's OTel SDK. Key points:
- Use **sync exporter** (`WithSyncer`) for tests, not batched
- Set **W3C Trace Context propagation** so spans share the test's trace ID
- Stove's HTTP client sends `traceparent` headers automatically
## Step 6: Kafka bridge (Go only)
For Go apps using IBM/sarama, twmb/franz-go, or segmentio/kafka-go, add the `stove-kafka` bridge library. See [go-setup.md](go-setup.md) for details.
The bridge intercepts produced/consumed messages and forwards them via gRPC to Stove's observer, enabling `shouldBePublished` and `shouldBeConsumed` assertions.
## Code Coverage (Go)
Go 1.20+ supports integration test coverage: build with `go build -cover`, set `GOCOVERDIR` env var, and coverage data is written on graceful shutdown. This fits Stove's lifecycle (SIGTERM → graceful shutdown → coverage files).
Key pieces:
- **Gradle**: `-Pgo.coverage=true` adds `-cover` to build, sets `go.cover.dir` system property, disables build cache for coverage runs
- **StoveConfig**: `env("GOCOVERDIR") { System.getProperty("go.cover.dir")?.also { File(it).mkdirs() } ?: "" }`
- **Go app**: `signal.Ignore(syscall.SIGPIPE)` in `main()` — prevents SIGPIPE (exit 141) from killing the process before coverage flush when stdout pipe closes under `ProcessBuilder`
- **Report tasks**: `goCoverageReport` (textfmt), `goCoverageSummary` (per-function), `goCoverageHtml` (visual)
- **Umbrella task**: `e2eTestWithCoverage` runs tests + generates reports
```bash
./gradlew e2eTestWithCoverage -Pgo.coverage=true
./gradlew e2eTest-containerWithCoverage -Pgo.coverage=true
```
No Stove framework changes needed — uses existing `envMapper`, Gradle tasks, and SIGTERM shutdown.
See [go-setup.md](go-setup.md#code-coverage) for full details.
## What you can't do
- **No `bridge()` / `using<T> {}`** --- no access to app's DI container
- Everything else works: HTTP, databases, Kafka, tracing, WireMock, gRPC, dashboard
## Container mode (`containerApp`)
Use `containerApp(...)` from `stove-container` when the AUT should run as a Docker image. Same envMapper/argsMapper model as processApp, plus a `configureContainer { ... }` block for Testcontainers-level customization (network mode, bind mounts, log consumers).
```kotlin
import com.trendyol.stove.container.ContainerTarget
import com.trendyol.stove.container.containerApp
import com.trendyol.stove.system.application.envMapper
containerApp(
image = "my-app:local",
target = ContainerTarget.Server(
hostPort = 8090, internalPort = 8090,
portEnvVar = "APP_PORT", bindHostPort = false
),
envProvider = envMapper {
"database.host" to "DB_HOST"
"kafka.bootstrapServers" to "KAFKA_BROKERS"
env("OTEL_EXPORTER_OTLP_ENDPOINT", "localhost:4317")
},
configureContainer = {
withNetworkMode("host") // Linux only; use port binding + shared network on macOS/Windows
}
)
```
`ContainerTarget.Server(hostPort, internalPort, portEnvVar, bindHostPort)` for HTTP/gRPC servers, `ContainerTarget.Worker()` for jobs. See [container.md](container.md) for the full guide (Dockerfile, Gradle wiring, networking strategies, coverage volume mounts, common pitfalls).
A common pattern: one `StoveConfig.kt` branches on `-Dgo.aut.mode=process|container` to switch between starters. The infrastructure systems and tests stay identical.
## MCP triage on failures
When `stove` (the CLI) is running, agents can triage failed runs through the local MCP endpoint at `http://localhost:4040/mcp` instead of scraping logs. See [mcp.md](mcp.md) for the workflow.
## Reference
- Process module source: `starters/process/stove-process/`
- Container module source: `starters/container/stove-container/`
- Container DSL: `starters/container/stove-container/src/main/kotlin/com/trendyol/stove/container/ContainerDsl.kt`
- Full Go example (process + container in one repo): `recipes/process/golang/go-showcase/`
- Docs:
- `docs/other-languages/go.md` — overview / mode picker
- `docs/other-languages/go-process.md` — process mode walkthrough
- `docs/other-languages/go-container.md` — container mode walkthrough
- `docs/other-languages/index.md`
- `docs/Components/21-mcp.md` — MCP triage
================================================
FILE: .claude/skills/stove/system-setup.md
================================================
# System Setup Reference
## Contents
- [Process Application (non-JVM apps)](#process-application-non-jvm-apps)
- [Provided Application (smoke testing)](#provided-application-smoke-testing)
- [Keyed systems (multiple instances)](#keyed-systems-multiple-instances)
- [HTTP Client](#http-client)
- [PostgreSQL](#postgresql)
- [MySQL](#mysql)
- [MSSQL](#mssql)
- [Cassandra](#cassandra)
- [MongoDB](#mongodb)
- [Redis](#redis)
- [Elasticsearch](#elasticsearch)
- [Couchbase](#couchbase)
- [Kafka](#kafka)
- [WireMock](#wiremock)
- [gRPC Mock](#grpc-mock)
- [gRPC Client](#grpc-client)
- [Bridge](#bridge)
- [Dashboard](#dashboard)
- [Reporting](#reporting)
- [Application runner](#application-runner)
- [Migrations](#migrations)
- [Container customization](#container-customization)
- [Fault injection (pause/unpause)](#fault-injection-pauseunpause)
- [Serde configuration](#serde-configuration)
- [Cleanup](#cleanup)
- [Keep dependencies running](#keep-dependencies-running)
All systems are configured inside `Stove().with { }`. The application runner goes last.
## Process Application (non-JVM apps)
Use `processApp()` or `goApp()` from the `stove-process` module to test applications written in any language (Go, Python, Rust, Node.js, etc.) as OS processes.
```kotlin
dependencies {
testImplementation("com.trendyol:stove-process")
}
```
### ProcessTarget variants
| Variant | Use case | Default readiness |
|---------|----------|-------------------|
| `ProcessTarget.Server(port, portEnvVar)` | HTTP/gRPC/TCP servers | HTTP GET `/health` |
| `ProcessTarget.Worker()` | Kafka consumers, batch jobs | 2s fixed delay |
### ReadinessStrategy variants
| Strategy | Use case |
|----------|----------|
| `ReadinessStrategy.HttpGet(url, timeout, retries, retryDelay, expectedStatusCodes)` | REST APIs with health endpoint |
| `ReadinessStrategy.TcpPort(port)` | gRPC/TCP servers (no HTTP) |
| `ReadinessStrategy.Probe { ... }` | Custom readiness (file, DB, etc.) |
| `ReadinessStrategy.FixedDelay(duration)` | Simple workers |
### Configuration passing
Stove collects all system configurations (`configureExposedConfiguration` from each system) as `key=value` strings and passes them to the process. Two mechanisms are available — use one or both:
#### envMapper — environment variables
Maps Stove config keys to OS environment variables:
```kotlin
envMapper {
"stove.config.key" to "ENV_VAR_NAME" // map Stove config → env var
env("STATIC_VAR", "value") // static env var
env("COMPUTED_VAR") { computeValue() } // computed env var
}
```
#### argsMapper — CLI arguments
Maps Stove config keys to command-line arguments, appended to the process command:
```kotlin
// GNU-style: --db-host=localhost --db-port=5432
argsMapper(prefix = "--", separator = "=") {
"database.host" to "db-host"
"database.port" to "db-port"
arg("verbose") // boolean flag: --verbose
arg("log-level", "debug") // static: --log-level=debug
arg("config-file") { "/tmp/test.yaml" } // computed: --config-file=/tmp/test.yaml
}
// POSIX-style: -h localhost -p 5432 (space separator → two separate args)
argsMapper(prefix = "-", separator = " ") {
"database.host" to "h"
"database.port" to "p"
}
// No prefix: db-host=localhost
argsMapper(prefix = "", separator = "=") {
"database.host" to "db-host"
}
```
#### Using both together
```kotlin
processApp {
ProcessApplicationOptions(
command = listOf("/path/to/server"),
target = ProcessTarget.Server(port = 8090),
envProvider = envMapper {
"database.host" to "DB_HOST" // passed as env var
},
argsProvider = argsMapper(prefix = "--", separator = "=") {
"database.port" to "db-port" // passed as --db-port=5432
arg("verbose")
}
)
}
```
### Examples
```kotlin
// HTTP API (Go) — env vars
goApp(
target = ProcessTarget.Server(port = 8090, portEnvVar = "APP_PORT"),
envProvider = envMapper {
"database.host" to "DB_HOST"
"database.port" to "DB_PORT"
env("LOG_LEVEL", "debug")
}
)
// Rust CLI server — CLI args
processApp {
ProcessApplicationOptions(
command = listOf("/path/to/rust-server"),
target = ProcessTarget.Server(port = 8090),
argsProvider = argsMapper(prefix = "--", separator = "=") {
"database.host" to "db-host"
"database.port" to "db-port"
}
)
}
// gRPC server (any language)
processApp {
ProcessApplicationOptions(
command = listOf("/path/to/grpc-server"),
target = ProcessTarget.Server(
port = 50051,
portEnvVar = "GRPC_PORT",
readiness = ReadinessStrategy.TcpPort(port = 50051),
),
envProvider = envMapper { "database.host" to "DB_HOST" }
)
}
// Kafka consumer (no port)
goApp(
target = ProcessTarget.Worker(readiness = ReadinessStrategy.FixedDelay(3.seconds)),
envProvider = envMapper { "kafka.bootstrapServers" to "KAFKA_BROKERS" }
)
```
Key points:
- `goApp()` defaults binary path from `go.app.binary` system property
- Port env var is injected automatically for `Server` targets
- `envProvider` and `argsProvider` can be used independently or together
- No `bridge()` — the app runs as a separate process, no DI access
- See [other-languages.md](other-languages.md) and [go-setup.md](go-setup.md) for full setup guides
## Provided Application (smoke testing)
Use `providedApplication()` instead of a JVM runner to test against an already-deployed application. The application can be written in **any language** — Go, Python, .NET, Rust, Node.js, etc.
```kotlin
Stove().with {
httpClient {
HttpClientSystemOptions(baseUrl = "https://staging.myapp.com")
}
providedApplication {
ProvidedApplicationOptions(
readiness = ReadinessStrategy.HttpGet(
url = "https://staging.myapp.com/health",
retries = 10,
retryDelay = 1.seconds,
timeout = 30.seconds,
expectedStatusCodes = setOf(200)
)
)
}
}.run()
```
Without health check (fire-and-forget):
```kotlin
providedApplication() // No health check, no options
```
Combine with `.provided()` system options to connect to existing infrastructure:
```kotlin
Stove().with {
httpClient {
HttpClientSystemOptions(baseUrl = "https://staging.myapp.com")
}
postgresql(AppDb) {
PostgresqlOptions.provided(
jdbcUrl = "jdbc:postgresql://staging-db:5432/myapp",
cleanup = { ops -> ops.execute("DELETE FROM orders WHERE test_data = true") },
configureExposedConfiguration = { listOf() }
)
}
redis(CacheCluster) {
RedisOptions.provided(
host = "staging-redis", port = 6379,
configureExposedConfiguration = { listOf() }
)
}
providedApplication {
ProvidedApplicationOptions(
readiness = ReadinessStrategy.HttpGet(url = "https://staging.myapp.com/health")
)
}
}.run()
```
**Important**: `Bridge` (DI access via `using<T>`) is **not available** with `providedApplication()` — there is no local DI container. Use `cleanup` lambdas to manage test data on external infrastructure.
## Keyed systems (multiple instances)
Register multiple instances of the same system type using `SystemKey`. Define keys as singleton objects:
```kotlin
object AppDb : SystemKey
object AnalyticsDb : SystemKey
object PaymentService : SystemKey
object InventoryService : SystemKey
```
Use keys in registration:
```kotlin
Stove().with {
// Two separate PostgreSQL containers with independent configs
postgresql(AppDb) {
PostgresqlOptions(
databaseName = "appdb",
configureExposedConfiguration = { cfg ->
listOf("app.datasource.url=${cfg.jdbcUrl}")
}
).migrations { register<AppSchemaMigration>() }
}
postgresql(AnalyticsDb) {
PostgresqlOptions(
databaseName = "analyticsdb",
configureExposedConfiguration = { cfg ->
listOf("analytics.datasource.url=${cfg.jdbcUrl}")
}
).migrations { register<AnalyticsSchemaMigration>() }
}
// Two HTTP clients pointing to different services
httpClient(PaymentService) {
HttpClientSystemOptions(baseUrl = "https://pay.internal")
}
httpClient(InventoryService) {
HttpClientSystemOptions(baseUrl = "https://inventory.internal")
}
// Two WireMock instances for different external APIs
wiremock(PaymentService) {
WireMockSystemOptions(port = 0, configureExposedConfiguration = { cfg ->
listOf("payment.url=${cfg.baseUrl}")
})
}
wiremock(InventoryService) {
WireMockSystemOptions(port = 0, configureExposedConfiguration = { cfg ->
listOf("inventory.url=${cfg.baseUrl}")
})
}
springBoot(runner = { params -> run(params) })
}.run()
```
All systems support keyed registration: PostgreSQL, MySQL, MSSQL, Cassandra, MongoDB, Redis, Elasticsearch, Couchbase, Kafka (core), WireMock, gRPC, gRPC Mock, HTTP.
Each keyed instance gets:
- Its own container with a unique dynamic port (no conflicts)
- Independent `configureExposedConfiguration` — all configs are aggregated and passed to the AUT
- Isolated state storage (separate lock files per key)
- Its own `cleanup` lambda
- Distinct reporting name (e.g., `"PostgreSQL [AppDb]"`)
A single key can be shared across protocol types:
```kotlin
object PaymentService : SystemKey
httpClient(PaymentService) { HttpClientSystemOptions(baseUrl = "...") }
grpc(PaymentService) { GrpcSystemOptions(host = "...", port = 50051) }
wiremock(PaymentService) { WireMockSystemOptions(...) }
```
Keyed systems work with both Testcontainers and `.provided()` (external) instances:
```kotlin
postgresql(AppDb) {
PostgresqlOptions.provided(
jdbcUrl = "jdbc:postgresql://staging-db:5432/app",
configureExposedConfiguration = { listOf() }
)
}
```
## HTTP Client
```kotlin
httpClient {
HttpClientSystemOptions(baseUrl = "http://localhost:8080")
}
```
Advanced options:
```kotlin
httpClient {
HttpClientSystemOptions(
baseUrl = "http://localhost:8080",
timeout = 60.seconds, // Request timeout (default: 30s)
contentConverter = JacksonConverter(myObjectMapper), // Custom JSON converter
configureClient = { // Ktor HttpClient config
install(Logging) { level = LogLevel.ALL }
},
configureWebSocket = { // WebSocket config
pingIntervalMillis = 10_000
},
createClient = { url -> myCustomHttpClient(url) } // Full client factory override
)
}
```
## PostgreSQL
```kotlin
postgresql {
PostgresqlOptions(
databaseName = "testdb",
configureExposedConfiguration = { cfg ->
listOf(
"spring.datasource.url=${cfg.jdbcUrl}",
"spring.datasource.username=${cfg.username}",
"spring.datasource.password=${cfg.password}"
)
}
).migrations {
register<InitialMigration>()
}
}
```
Migration class:
```kotlin
class InitialMigration : DatabaseMigration<PostgresSqlMigrationContext> {
override val order: Int = 1
override suspend fun execute(connection: PostgresSqlMigrationContext) {
connection.operations.execute(
"""
CREATE TABLE IF NOT EXISTS orders (
id VARCHAR(255) PRIMARY KEY,
user_id VARCHAR(255) NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
status VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
""".trimIndent()
)
}
}
```
For R2DBC:
```kotlin
configureExposedConfiguration = { cfg ->
listOf(
"spring.r2dbc.url=r2dbc:postgresql://${cfg.host}:${cfg.port}/testdb",
"spring.r2dbc.username=${cfg.username}",
"spring.r2dbc.password=${cfg.password}"
)
}
```
## MySQL
```kotlin
mysql {
MySqlSystemOptions(
databaseName = "testdb",
configureExposedConfiguration = { cfg ->
listOf(
"spring.datasource.url=${cfg.jdbcUrl}",
"spring.datasource.username=${cfg.username}",
"spring.datasource.password=${cfg.password}"
)
}
).migrations {
register<InitialMigration>()
}
}
```
Same migration pattern as PostgreSQL, using `MySqlMigrationContext`.
## MSSQL
```kotlin
mssql {
MsSqlSystemOptions(
databaseName = "testdb",
configureExposedConfiguration = { cfg ->
listOf(
"spring.datasource.url=${cfg.jdbcUrl}",
"spring.datasource.username=${cfg.username}",
"spring.datasource.password=${cfg.password}"
)
}
).migrations {
register<InitialMigration>()
}
}
```
Same migration pattern, using `MsSqlMigrationContext`.
## Cassandra
```kotlin
cassandra {
CassandraSystemOptions(
keyspace = "my_keyspace",
datacenter = "datacenter1",
container = CassandraContainerOptions(tag = "4.1"),
configureExposedConfiguration = { cfg ->
listOf(
"cassandra.host=${cfg.host}",
"cassandra.port=${cfg.port}",
"cassandra.keyspace=${cfg.keyspace}",
"cassandra.datacenter=${cfg.datacenter}"
)
}
).migrations {
register<CreateTableMigration>()
}
}
```
Migration class:
```kotlin
class CreateTableMigration : CassandraMigration {
override val order: Int = 1
override suspend fun execute(connection: CassandraMigrationContext) {
connection.session.execute(
"""
CREATE TABLE IF NOT EXISTS orders (
id text PRIMARY KEY,
user_id text,
amount double,
status text
);
""".trimIndent()
)
}
}
```
For an externally managed Cassandra:
```kotlin
cassandra {
CassandraSystemOptions.provided(
host = "localhost",
port = 9042,
datacenter = "dc1",
keyspace = "my_keyspace",
configureExposedConfiguration = { cfg ->
listOf("cassandra.contact-points=${cfg.host}:${cfg.port}")
}
)
}
```
Container operations: `pause()` / `unpause()` to simulate Cassandra downtime.
## MongoDB
```kotlin
mongodb {
MongodbSystemOptions(
databaseOptions = DatabaseOptions(
default = DefaultDatabase(name = "testdb", collection = "orders")
),
container = MongoContainerOptions(tag = "7.0"),
configureExposedConfiguration = { cfg ->
listOf(
"mongodb.connection-string=${cfg.connectionString}",
"mongodb.host=${cfg.host}",
"mongodb.port=${cfg.port}"
)
}
).migrations {
register<SeedDataMigration>()
}
}
```
For an externally managed MongoDB:
```kotlin
mongodb {
MongodbSystemOptions.provided(
connectionString = "mongodb://localhost:27017",
host = "localhost",
port = 27017,
configureExposedConfiguration = { cfg ->
listOf("spring.data.mongodb.uri=${cfg.connectionString}")
}
)
}
```
Migration class uses `MongodbMigrationContext` with access to `client: MongoClient`.
Container operations: `pause()` / `unpause()`.
## Redis
```kotlin
redis {
RedisOptions(
database = 0,
password = "redis-password",
container = RedisContainerOptions(tag = "7-alpine"),
configureExposedConfiguration = { cfg ->
listOf(
"redis.host=${cfg.host}",
"redis.port=${cfg.port}",
"redis.password=${cfg.password}"
)
}
)
}
```
For an externally managed Redis:
```kotlin
redis {
RedisOptions.provided(
host = "localhost",
port = 6379,
password = "secret",
database = 0,
configureExposedConfiguration = { cfg ->
listOf("spring.redis.url=${cfg.redisUri}")
}
)
}
```
Container operations: `pause()` / `unpause()`.
## Elasticsearch
```kotlin
elasticsearch {
ElasticsearchSystemOptions(
container = ElasticContainerOptions(
tag = "8.15.0",
password = "elastic-password",
disableSecurity = true
),
configureExposedConfiguration = { cfg ->
listOf(
"elasticsearch.host=${cfg.host}",
"elasticsearch.port=${cfg.port}"
)
}
).migrations {
register<CreateIndexMigration>()
}
}
```
For an externally managed Elasticsearch:
```kotlin
elasticsearch {
ElasticsearchSystemOptions.provided(
host = "localhost",
port = 9200,
configureExposedConfiguration = { cfg ->
listOf("es.url=http://${cfg.host}:${cfg.port}")
}
)
}
```
Migration class uses `ElasticsearchClient` as context directly.
Container operations: `pause()` / `unpause()`.
## Couchbase
```kotlin
couchbase {
CouchbaseSystemOptions(
defaultBucket = "test-bucket",
containerOptions = CouchbaseContainerOptions(tag = "7.6.1"),
configureExposedConfiguration = { cfg ->
listOf(
"couchbase.connection-string=${cfg.connectionString}",
"couchbase.username=${cfg.username}",
"couchbase.password=${cfg.password}"
)
}
).migrations {
register<CreateBucketMigration>()
}
}
```
For an externally managed Couchbase:
```kotlin
couchbase {
CouchbaseSystemOptions.provided(
connectionString = "couchbase://localhost",
username = "admin",
password = "password",
defaultBucket = "test-bucket",
configureExposedConfiguration = { cfg ->
listOf("couchbase.hosts=${cfg.hostsWithPort}")
}
)
}
```
Migration class uses `Cluster` as context. Container operations: `pause()` / `unpause()`.
## Kafka
Use `stove-kafka` for standalone. Use `stove-spring-kafka` for Spring Boot Kafka listeners (`shouldBeConsumed`, `shouldBeFailed`, `shouldBeRetried`).
```kotlin
kafka {
KafkaSystemOptions(
serde = StoveSerde.jackson.anyByteArraySerde(),
valueSerializer = JsonSerializer(),
containerOptions = KafkaContainerOptions(tag = "8.0.3") {
withStartupAttempts(3)
},
configureExposedConfiguration = { cfg ->
listOf(
"spring.kafka.bootstrap-servers=${cfg.bootstrapServers}",
"spring.kafka.producer.properties.interceptor.classes=${cfg.interceptorClass}",
"spring.kafka.consumer.properties.interceptor.classes=${cfg.interceptorClass}"
)
}
)
}
```
For embedded Kafka (no Docker container):
```kotlin
kafka {
KafkaSystemOptions(
useEmbeddedKafka = true,
configureExposedConfiguration = { cfg ->
listOf("spring.kafka.bootstrap-servers=${cfg.bootstrapServers}")
}
)
}
```
**Application-side requirements (Spring Boot Kafka)**:
- Inject `RecordInterceptor<String, String>` into your `ConcurrentKafkaListenerContainerFactory` and call `factory.setRecordInterceptor(interceptor)`.
- Register `TestSystemKafkaInterceptor<*, *>` and a `StoveSerde` bean in test dependencies.
### Test-friendly Kafka settings
Default Kafka producer/consumer settings are tuned for production throughput, not test speed. In e2e tests, this causes timeouts, flaky assertions, and slow feedback. Configure for **immediate delivery and fast commits**:
**Container-level** — enable auto-topic creation so topics exist when producers/consumers first connect:
```kotlin
kafka {
KafkaSystemOptions(
containerOptions = KafkaContainerOptions(tag = "8.0.3") {
withEnv("KAFKA_AUTO_CREATE_TOPICS_ENABLE", "true")
},
configureExposedConfiguration = { cfg ->
listOf("spring.kafka.bootstrap-servers=${cfg.bootstrapServers}")
}
)
}
```
**Producer settings** — flush immediately, don't batch:
```properties
# Spring Boot application.yml or exposed via Stove config
spring.kafka.producer.properties.linger.ms=0 # Send immediately, don't wait to batch
spring.kafka.producer.properties.batch.size=1 # Single-message batches
spring.kafka.producer.acks=all # Wait for all replicas (reliable in single-broker test)
```
**Consumer settings** — commit fast, start from beginning, short timeouts:
```properties
spring.kafka.consumer.auto-offset-reset=earliest # Start from beginning (don't miss messages)
spring.kafka.consumer.properties.auto.commit.interval.ms=100 # Commit offsets every 100ms (default: 5000ms)
spring.kafka.consumer.properties.max.poll.interval.ms=10000 # Shorter poll timeout (default: 300000ms)
spring.kafka.consumer.properties.session.timeout.ms=10000 # Faster rebalance on failure (default: 45000ms)
spring.kafka.consumer.properties.heartbeat.interval.ms=3000 # Faster heartbeat (default: 3000ms, keep ≤ session/3)
```
**Why this matters for Stove assertions:**
- `shouldBePublished` checks the Stove interceptor sink — messages must reach it promptly. `linger.ms=0` and `batch.size=1` prevent the producer from holding messages.
- `shouldBeConsumed` checks that the message was consumed AND its offset committed. `auto.commit.interval.ms=100` makes committed offsets visible within 100ms instead of the 5-second default.
- `shouldBeFailed` / `shouldBeRetried` check error and retry sinks — short `max.poll.interval.ms` prevents long waits before Kafka considers a consumer dead.
- Without `auto-offset-reset=earliest`, consumers joining after a message is produced will never see it, causing `shouldBeConsumed` to timeout.
**Passing these via Stove's `configureExposedConfiguration`:**
```kotlin
kafka {
KafkaSystemOptions(
containerOptions = KafkaContainerOptions {
withEnv("KAFKA_AUTO_CREATE_TOPICS_ENABLE", "true")
},
configureExposedConfiguration = { cfg ->
listOf(
"spring.kafka.bootstrap-servers=${cfg.bootstrapServers}",
"spring.kafka.producer.properties.interceptor.classes=${cfg.interceptorClass}",
"spring.kafka.consumer.properties.interceptor.classes=${cfg.interceptorClass}",
// Test-friendly overrides
"spring.kafka.producer.properties.linger.ms=0",
"spring.kafka.producer.properties.batch.size=1",
"spring.kafka.consumer.auto-offset-reset=earliest",
"spring.kafka.consumer.properties.auto.commit.interval.ms=100"
)
}
)
}
```
**Non-Spring JVM apps** — the same principles apply. Pass equivalent properties through your app's configuration mechanism:
| Setting | Production default | Test-friendly value | Why |
|---------|-------------------|--------------------|----|
| `linger.ms` | 5-100 | `0` | Immediate send |
| `batch.size` | 16384 | `1` | No batching |
| `auto.commit.interval.ms` | 5000 | `100` | Fast offset visibility |
| `auto.offset.reset` | `latest` | `earliest` | Don't miss messages |
| `max.poll.interval.ms` | 300000 | `10000` | Faster failure detection |
| `auto.create.topics.enable` (broker) | `true` | `true` | Topics exist on first use |
**Go applications** — see [go-setup.md](go-setup.md) for per-library settings (sarama, franz-go, segmentio/kafka-go) including auto-topic creation, batch timeouts, commit intervals, and the separate producer/consumer client pattern for franz-go.
## WireMock
```kotlin
wiremock {
WireMockSystemOptions(
port = 0, // Dynamic port — recommended for CI
serde = StoveSerde.jackson.anyByteArraySerde(),
configureExposedConfiguration = { cfg ->
listOf(
"payment.service.url=${cfg.baseUrl}",
"inventory.service.url=${cfg.baseUrl}"
)
}
)
}
```
All external service URLs must be configurable so they can be pointed to WireMock.
## gRPC Mock
```kotlin
grpcMock {
GrpcMockSystemOptions(
port = 0, // Dynamic port
configureExposedConfiguration = { cfg ->
listOf(
"grpcService.host=${cfg.host}",
"grpcService.port=${cfg.port}"
)
}
)
}
```
## gRPC Client
For testing your own gRPC server (not external mocks):
```kotlin
grpc {
GrpcSystemOptions(host = "localhost", port = 50051)
}
```
## Bridge
Direct access to DI container from tests. Built into `stove-spring` / `stove-ktor` / `stove-micronaut`.
```kotlin
bridge() // Auto-detects DI framework
```
### Ktor DI support
Bridge auto-detects your DI framework:
```kotlin
// Koin — add io.insert-koin:koin-ktor to classpath
bridge() // auto-detects Koin
// Ktor-DI — add io.ktor:ktor-server-di to classpath
bridge() // auto-detects Ktor-DI
// Custom resolver (Kodein, Dagger, etc.)
bridge { application, type ->
myDiContainer.resolve(type)
}
```
## Dashboard
Streams test events to the stove CLI for real-time visualization. Requires `stove-dashboard` and `stove-extensions-kotest` or `stove-extensions-junit` — see [Reporting](#reporting).
```kotlin
dashboard {
DashboardSystemOptions(
appName = "my-service",
cliHost = "localhost", // default
cliPort = 4041 // default
)
}
```
Run `stove` CLI separately, then run your tests — the dashboard at `http://localhost:4040` shows a live tree of specs, test hierarchy, timeline entries, traces, and snapshots.
## Reporting
Reporting and test hierarchy tracking require the framework extension. This is mandatory for Dashboard, tracing, and structured failure reports.
### Kotest
Register `StoveKotestExtension` in your `AbstractProjectConfig`:
```kotlin
class StoveConfig : AbstractProjectConfig() {
override val extensions: List<Extension> = listOf(StoveKotestExtension())
override suspend fun beforeProject() {
Stove().with { /* systems */ }.run()
}
override suspend fun afterProject() {
Stove.stop()
}
}
```
Requires `stove-extensions-kotest` dependency and a `kotest.properties` file pointing to this config class.
### JUnit
Annotate your base test class with `@ExtendWith(StoveJUnitExtension::class)`:
```kotlin
@ExtendWith(StoveJUnitExtension::class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
abstract class BaseE2ETest {
companion object {
@JvmStatic @BeforeAll
fun setup() = runBlocking {
Stove().with { /* systems */ }.run()
}
@JvmStatic @AfterAll
fun teardown() = runBlocking { Stove.stop() }
}
}
```
Requires `stove-extensions-junit` dependency. Supports `@Nested` class hierarchy.
## Application runner
Goes last, after all systems. Systems inject configuration via `configureExposedConfiguration`.
### Spring Boot
```kotlin
springBoot(
runner = { params ->
com.yourcompany.yourapp.run(params) {
addTestDependencies {
bean<TestSystemKafkaInterceptor<*, *>>(isPrimary = true)
bean { StoveSerde.jackson.anyByteArraySerde() }
}
}
},
withParameters = listOf(
"server.port=8080",
"grpc.server.port=$GRPC_SERVER_PORT"
)
)
```
For Spring Boot 4.x, use `addTestDependencies4x` with `registerBean<>()`:
```kotlin
addTestDependencies4x {
registerBean<TestSystemKafkaInterceptor<*, *>>(primary = true)
registerBean { StoveSerde.jackson.anyByteArraySerde() }
}
```
### Ktor
```kotlin
ktor(
runner = { params ->
com.yourcompany.yourapp.run(params, wait = false)
},
withParameters = listOf("server.port=8080")
)
```
### Quarkus
```kotlin
quarkus(
runner = { params ->
com.yourcompany.yourapp.main(params)
},
withParameters = listOf("quarkus.http.port=8080")
)
```
Supports both direct main runner and packaged runtime. Configurable startup timeout via `stove.quarkus.startup.timeout.ms` system property (default: 120s).
### Micronaut
```kotlin
micronaut(
runner = { params ->
com.yourcompany.yourapp.run(params)
},
withParameters = listOf("micronaut.server.port=8080")
)
```
Returns `ApplicationContext`, enabling bridge/DI access.
## Migrations
Database and infrastructure systems support migrations that run after the system starts and before tests execute. Use for schema creation, indexing, and seed data.
```kotlin
postgresql {
PostgresqlOptions(
configureExposedConfiguration = { cfg -> listOf("spring.datasource.url=${cfg.jdbcUrl}") }
).migrations {
register<CreateTablesMigration>()
register<SeedDataMigration>()
}
}
```
Migration class:
```kotlin
class CreateTablesMigration : PostgresqlMigration {
override val order: Int = MigrationPriority.HIGHEST.value // Runs first
override suspend fun execute(connection: PostgresSqlMigrationContext) {
connection.operations.execute("CREATE TABLE IF NOT EXISTS orders (...)")
}
}
class SeedDataMigration : PostgresqlMigration {
override val order: Int = 100 // After schema
override suspend fun execute(connection: PostgresSqlMigrationContext) {
connection.operations.execute("INSERT INTO orders ...")
}
}
```
Ordering: migrations execute in ascending `order`. Use `MigrationPriority.HIGHEST` (schema), `MigrationPriority.LOWEST` (final setup), or custom integers.
Type aliases per system (use instead of `DatabaseMigration<XyzContext>`):
| Module | Type Alias | Context Type |
|---|---|---|
| stove-postgres | `PostgresqlMigration` | `PostgresSqlMigrationContext` |
| stove-mysql | `MySqlMigration` | `MySqlMigrationContext` |
| stove-mssql | `MsSqlMigration` | `SqlMigrationContext` |
| stove-mongodb | `MongodbMigration` | `MongodbMigrationContext` |
| stove-couchbase | `CouchbaseMigration` | `Cluster` |
| stove-elasticsearch | `ElasticsearchMigration` | `ElasticsearchClient` |
| stove-redis | `RedisMigration` | `RedisMigrationContext` |
| stove-kafka | `KafkaMigration` | `KafkaMigrationContext` |
| stove-cassandra | `CassandraMigration` | `CassandraMigrationContext` |
Advanced patterns:
```kotlin
// Factory function for migrations with parameters
.migrations {
register<ConfigurableMigration> {
ConfigurableMigration(batchSize = 1000)
}
}
// Replace a migration with a test-specific override
.migrations {
register<ProductionSeedMigration>()
replace<ProductionSeedMigration, TestSeedMigration>()
// Or replace with a factory
replace<ProductionSeedMigration> {
MinimalSeedMigration()
}
}
```
Notes: migrations must have no-arg constructors (unless using factory registration). Use idempotent statements (`IF NOT EXISTS`). Don't close the connection — Stove manages it.
## Container customization
All container-backed systems accept a `ContainerOptions` with customization hooks:
```kotlin
postgresql {
PostgresqlOptions(
container = PostgresqlContainerOptions(
registry = "my-registry.example.com", // Custom Docker registry
image = "postgres", // Image name
tag = "16-alpine", // Image tag
compatibleSubstitute = "my-registry.example.com/postgres", // Alternative image
containerFn = { // Customize container before startup
withEnv("POSTGRES_INITDB_ARGS", "--encoding=UTF-8")
withCommand("postgres", "-c", "max_connections=200")
}
),
configureExposedConfiguration = { cfg -> listOf("spring.datasource.url=${cfg.jdbcUrl}") }
)
}
kafka {
KafkaSystemOptions(
containerOptions = KafkaContainerOptions(tag = "8.0.3") {
withStartupAttempts(3)
withEnv("KAFKA_AUTO_CREATE_TOPICS_ENABLE", "true")
},
configureExposedConfiguration = { cfg -> listOf("kafka.bootstrap=${cfg.bootstrapServers}") }
)
}
```
The `containerFn` lambda receives the container instance before startup, so you can call any Testcontainers method (env vars, commands, exposed ports, volume mounts, etc.).
## Fault injection (pause/unpause)
All container-backed systems support `pause()` / `unpause()` for simulating outages. Both are idempotent.
```kotlin
stove {
postgresql { pause() }
http {
getResponse<Any>("/health") { response ->
response.status shouldBe 503
}
}
postgresql { unpause() }
http {
getResponse<Any>("/health") { response ->
response.status shouldBe 200
}
}
}
```
Supported: PostgreSQL, MySQL, MSSQL, Cassandra, MongoDB, Redis, Elasticsearch, Couchbase, Kafka.
## Serde configuration
Stove uses `StoveSerde` for JSON handling across systems (Kafka, MongoDB, WireMock, HTTP). Three implementations are built in:
```kotlin
// Jackson (default) — configure ObjectMapper
val serde = StoveSerde.jackson.anyByteArraySerde()
val stringSerde = StoveSerde.jackson.anyJsonStringSerde()
// With custom ObjectMapper
val customSerde = StoveSerde.jackson.anyByteArraySerde(
StoveSerde.jackson.byConfiguring {
disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
}
)
// Kotlinx Serialization (requires @Serializable)
val kotlinxSerde = StoveSerde.kotlinx.anyByteArraySerde()
val customKotlinx = StoveSerde.kotlinx.anyByteArraySerde(
StoveSerde.kotlinx.byConfiguring {
ignoreUnknownKeys = true
isLenient = true
}
)
// Gson
val gsonSerde = StoveSerde.gson.anyByteArraySerde()
val customGson = StoveSerde.gson.anyByteArraySerde(
StoveSerde.gson.byConfiguring {
setPrettyPrinting()
serializeNulls()
}
)
```
**Important: align Stove's serde with your application's serialization.** If your application uses a custom ObjectMapper (e.g., with snake_case naming, custom date formats, or extra modules), Stove must use the same configuration. Otherwise, Stove will fail to deserialize responses from your HTTP endpoints, Kafka messages produced by your app, or documents written to MongoDB/Elasticsearch/Couchbase. The same applies if your application uses Kotlinx Serialization or Gson — use the matching `StoveSerde` variant.
```kotlin
// Reuse your application's ObjectMapper so ser/de behavior matches
val appObjectMapper = MyApp.objectMapper()
httpClient {
HttpClientSystemOptions(
baseUrl = "http://localhost:8080",
contentConverter = JacksonConverter(appObjectMapper)
)
}
kafka {
KafkaSystemOptions(
serde = StoveSerde.jackson.anyByteArraySerde(appObjectMapper),
configureExposedConfiguration = { cfg -> listOf("kafka.bootstrap=${cfg.bootstrapServers}") }
)
}
mongodb {
MongodbSystemOptions(
serde = StoveSerde.jackson.anyJsonStringSerde(appObjectMapper),
configureExposedConfiguration = { cfg -> listOf("mongodb.uri=${cfg.connectionString}") }
)
}
wiremock {
WireMockSystemOptions(
serde = StoveSerde.jackson.anyByteArraySerde(appObjectMapper),
configureExposedConfiguration = { cfg -> listOf("service.url=${cfg.baseUrl}") }
)
}
```
## Cleanup
Every system accepts a `cleanup` lambda in its options. This runs during `Stove.stop()` (after all tests complete) and receives the system's native client. Use it to wipe test data — especially important for provided (external) instances that persist between runs.
```kotlin
postgresql {
PostgresqlOptions(
databaseName = "testdb",
cleanup = { ops -> ops.execute("TRUNCATE orders, users") },
configureExposedConfiguration = { cfg -> listOf("spring.datasource.url=${cfg.jdbcUrl}") }
)
}
mongodb {
MongodbSystemOptions(
cleanup = { client -> client.getDatabase("testdb").drop() },
configureExposedConfiguration = { cfg -> listOf("mongodb.uri=${cfg.connectionString}") }
)
}
kafka {
KafkaSystemOptions(
cleanup = { admin ->
val topics = admin.listTopics().names().get().filter { it.startsWith("test-") }
if (topics.isNotEmpty()) admin.deleteTopics(topics).all().get()
},
configureExposedConfiguration = { cfg -> listOf("kafka.bootstrap=${cfg.bootstrapServers}") }
)
}
redis {
RedisOptions(
cleanup = { client -> client.connect().sync().flushdb() },
configureExposedConfiguration = { cfg -> listOf("redis.host=${cfg.host}") }
)
}
elasticsearch {
ElasticsearchSystemOptions(
cleanup = { client -> client.indices().delete { it.index("test-*") } },
configureExposedConfiguration = { cfg -> listOf("es.host=${cfg.host}") }
)
}
couchbase {
CouchbaseSystemOptions(
cleanup = { cluster -> cluster.query("DELETE FROM `test-bucket`") },
configureExposedConfiguration = { cfg -> listOf("cb.conn=${cfg.connectionString}") }
)
}
cassandra {
CassandraSystemOptions(
cleanup = { session -> session.execute("TRUNCATE my_keyspace.orders") },
configureExposedConfiguration = { cfg -> listOf("cassandra.host=${cfg.host}") }
)
}
mysql {
MySqlSystemOptions(
cleanup = { ops -> ops.execute("TRUNCATE orders") },
configureExposedConfiguration = { cfg -> listOf("spring.datasource.url=${cfg.jdbcUrl}") }
)
}
mssql {
MsSqlSystemOptions(
cleanup = { ops -> ops.execute("TRUNCATE TABLE orders") },
configureExposedConfiguration = { cfg -> listOf("spring.datasource.url=${cfg.jdbcUrl}") }
)
}
```
Cleanup client types per system:
| System | Cleanup parameter type |
|---|---|
| PostgreSQL | `NativeSqlOperations` |
| MySQL | `NativeSqlOperations` |
| MSSQL | `NativeSqlOperations` |
| Cassandra | `CqlSession` |
| MongoDB | `MongoClient` |
| Redis | `RedisClient` |
| Elasticsearch | `ElasticsearchClient` |
| Couchbase | `Cluster` |
| Kafka | `Admin` |
WireMock uses event-driven cleanup instead:
```kotlin
wiremock {
WireMockSystemOptions(
removeStubAfterRequestMatched = true, // Auto-remove stubs after match
afterStubRemoved = { serveEvent, stubLog -> /* optional callback */ },
configureExposedConfiguration = { cfg -> listOf("service.url=${cfg.baseUrl}") }
)
}
```
The `cleanup` lambda also works with `.provided()` (external instances):
```kotlin
postgresql {
PostgresqlOptions.provided(
jdbcUrl = "jdbc:postgresql://localhost:5432/testdb",
host = "localhost",
port = 5432,
cleanup = { ops -> ops.execute("TRUNCATE orders, users") },
configureExposedConfiguration = { cfg -> listOf("spring.datasource.url=${cfg.jdbcUrl}") }
)
}
```
## Keep dependencies running
Keeps containers alive between test runs for faster local iteration. Disable in CI.
```kotlin
Stove {
keepDependenciesRunning()
}.with { /* systems */ }.run()
```
================================================
FILE: .claude/skills/stove/tracing.md
================================================
# Tracing Configuration
Tracing captures the full execution call chain inside your application, shown on test failure. Requires `stove-tracing` and `stove-extensions-kotest` or `stove-extensions-junit`.
## 1. Enable span receiver
Add inside `Stove().with { }`:
```kotlin
tracing { enableSpanReceiver() }
```
## 2. Attach the OpenTelemetry agent
### Gradle Plugin (default)
```kotlin
plugins { id("com.trendyol.stove.tracing") version "$stoveVersion" }
stoveTracing {
serviceName.set("my-service")
testTaskNames.set(listOf("e2eTest"))
}
```
### buildSrc alternative
Copy `StoveTracingConfiguration.kt` from the Stove repo to `buildSrc/src/main/kotlin/`, then use direct assignment:
```kotlin
import com.trendyol.stove.gradle.stoveTracing
stoveTracing {
serviceName = "my-service"
testTaskNames = listOf("e2eTest")
}
```
## 3. Plugin options
| Option | Default | Description |
|---|---|---|
| `serviceName` | `"stove-traced-app"` | Service name in traces |
| `enabled` | `true` | Toggle tracing |
| `testTaskNames` | `[]` | Apply to specific tasks (empty = all) |
| `otelAgentVersion` | `"2.24.0"` | OTel Java Agent version |
| `disabledInstrumentations` | `[]` | Instrumentations to disable (e.g., `jdbc`, `hibernate`) |
| `additionalInstrumentations` | `[]` | Extra instrumentations |
| `customAnnotations` | `[]` | Custom annotation classes to instrument |
| `protocol` | `"grpc"` | OTLP protocol |
| `captureHttpHeaders` | `true` | Capture HTTP headers in spans |
| `captureExperimentalTelemetry` | `true` | Enable experimental HTTP telemetry |
| `bspScheduleDelay` | `100` | Batch span processor delay in ms (lower = faster export) |
| `bspMaxBatchSize` | `1` | Batch size for span export (1 = immediate) |
## 4. Runtime tracing config
Configure inside `Stove().with { }`:
```kotlin
tracing {
enableSpanReceiver() // Required
spanCollectionTimeout(10.seconds) // Wait time for spans (default: 5s)
maxSpansPerTrace(2000) // Cap per trace (default: 1000)
spanFilter { span -> // Filter collected spans
!span.operationName.contains("health-check")
}
}
```
## 5. Trace validation DSL
```kotlin
tracing {
// Span assertions
shouldContainSpan("OrderService.processOrder")
shouldContainSpanMatching { it.operationName.contains("Repository") }
shouldNotContainSpan("AdminService.delete")
shouldNotHaveFailedSpans()
shouldHaveFailedSpan("PaymentGateway.charge")
shouldHaveSpanWithAttribute("http.method", "GET")
shouldHaveSpanWithAttributeContaining("http.url", "/api/users")
// Performance
executionTimeShouldBeLessThan(500.milliseconds)
executionTimeShouldBeGreaterThan(10.milliseconds)
spanCountShouldBe(10)
spanCountShouldBeAtLeast(5)
spanCountShouldBeAtMost(20)
// Debugging helpers
println(renderTree()) // Hierarchical tree view
println(renderSummary()) // Compact summary
val failed = getFailedSpans()
val duration = getTotalDuration()
val span = findSpanByName("OrderService.process")
// Wait for async spans
waitForSpans(expectedCount = 5, timeoutMs = 3000)
}
```
================================================
FILE: .claude/skills/stove/writing-tests.md
================================================
# Writing Tests Reference
## Contents
- [HTTP requests](#http-requests)
- [HTTP streaming](#http-streaming)
- [PostgreSQL queries](#postgresql-queries)
- [MySQL queries](#mysql-queries)
- [MSSQL queries](#mssql-queries)
- [Cassandra assertions](#cassandra-assertions)
- [MongoDB assertions](#mongodb-assertions)
- [Redis assertions](#redis-assertions)
- [Elasticsearch assertions](#elasticsearch-assertions)
- [Couchbase assertions](#couchbase-assertions)
- [Kafka assertions](#kafka-assertions)
- [WireMock mocking](#wiremock-mocking)
- [gRPC Mock](#grpc-mock)
- [gRPC Client](#grpc-client)
- [Bridge (DI access)](#bridge-di-access)
- [Trace validation](#trace-validation)
- [Keyed system tests](#keyed-system-tests)
- [Smoke testing (providedApplication)](#smoke-testing-providedapplication)
- [Multi-system test](#multi-system-test)
- [Anti-patterns](#anti-patterns)
All tests use the `stove { }` entry point.
## HTTP requests
```kotlin
// POST with typed response
http {
postAndExpectBody<OrderResponse>(
uri = "/orders",
body = CreateOrderRequest(userId = "u1", amount = 99.99).some()
) { response ->
response.status shouldBe 201
response.body().orderId shouldNotBe null
}
}
// POST expecting JSON directly
http {
postAndExpectJson<OrderResponse>("/orders") {
CreateOrderRequest(userId = "u1", amount = 99.99)
} { order ->
order.id shouldNotBe null
}
}
// GET
http {
get<UserResponse>("/users/123") { user ->
user.name shouldBe "John"
}
}
// GET with full response (status + headers + body)
http {
getResponse<UserResponse>("/users/123") { response ->
response.status shouldBe 200
response.headers["Content-Type"] shouldContain "application/json"
response.body().id shouldBe 123
}
}
// GET list
http {
getMany<ProductResponse>("/products", queryParams = mapOf("page" to "1")) { products ->
products.size shouldBe 10
}
}
// PUT
http {
putAndExpectBody<ProductResponse>(
uri = "/products/456",
body = UpdateProductRequest(price = 899.99).some()
) { response ->
response.status shouldBe 200
response.body().price shouldBe 899.99
}
}
// DELETE
http {
deleteAndExpectBodilessResponse("/users/123") { response ->
response.status shouldBe 204
}
}
// Multipart upload
http {
postMultipartAndExpectResponse<UploadResponse>(
uri = "/products/import",
body = listOf(
StoveMultiPartContent.Text("name", "Laptop"),
StoveMultiPartContent.File("file", "data.csv", csvBytes, MediaType.APPLICATION_OCTET_STREAM_VALUE)
)
) { response ->
response.status shouldBe 200
}
}
// WebSocket
http {
webSocket("/chat") {
send("Hello!")
val response = receiveText()
response shouldBe "Echo: Hello!"
}
// Collect multiple messages
webSocket("/events") {
val messages = collectTexts(count = 5, timeout = 10.seconds)
messages.size shouldBe 5
}
// Streaming with Flow
webSocket("/stream") {
incomingTexts().take(10).toList().size shouldBe 10
}
}
```
## HTTP streaming
For JSON streaming (NDJSON) endpoints, use Flow-based extensions on `HttpStatement`:
```kotlin
http {
// Read NDJSON stream line by line, transform each line
val items = client().prepareGet("/api/events/stream").readJsonTextStream { line ->
StoveSerde.jackson.default.readValue(line, EventResponse::class.java)
}.toList()
items.size shouldBeGreaterThan 0
// Read stream as ByteReadChannel for binary processing
client().prepareGet("/api/binary/stream").readJsonContentStream { channel ->
channel.readRemaining().readText()
}.toList().shouldNotBeEmpty()
}
// Serialize items to NDJSON for request body
val body = StoveSerde.jackson.anyByteArraySerde().serializeToStreamJson(
listOf(Event("e1"), Event("e2"), Event("e3"))
)
```
## PostgreSQL queries
```kotlin
postgresql {
// Execute DDL/DML
shouldExecute(
"""
INSERT INTO products (name, price) VALUES ('Laptop', 999.99)
""".trimIndent()
)
// Query with typed mapper
shouldQuery<OrderRow>(
query = "SELECT * FROM orders WHERE user_id = '$userId'",
mapper = { row ->
OrderRow(
id = row.string("id"),
userId = row.string("user_id"),
amount = row.double("amount"),
status = row.string("status")
)
}
) { orders ->
orders.size shouldBe 1
orders.first().status shouldBe "CONFIRMED"
}
}
```
## MySQL queries
Same API as PostgreSQL — uses `shouldExecute` and `shouldQuery` with a row mapper:
```kotlin
mysql {
shouldExecute("INSERT INTO products (name, price) VALUES ('Laptop', 999.99)")
shouldQuery<ProductRow>(
query = "SELECT * FROM products WHERE name = 'Laptop'",
mapper = { row -> ProductRow(row.string("name"), row.double("price")) }
) { products ->
products.size shouldBe 1
}
}
```
## MSSQL queries
Same API as PostgreSQL/MySQL:
```kotlin
mssql {
shouldExecute("INSERT INTO orders (id, status) VALUES ('o1', 'NEW')")
shouldQuery<OrderRow>(
query = "SELECT * FROM orders WHERE id = 'o1'",
mapper = { row -> OrderRow(row.string("id"), row.string("status")) }
) { orders ->
orders.first().status shouldBe "NEW"
}
}
```
## Cassandra assertions
```kotlin
cassandra {
// Execute CQL
shouldExecute("INSERT INTO orders (id, user_id, status) VALUES ('o1', 'u1', 'NEW')")
// Query with ResultSet assertion
shouldQuery("SELECT * FROM orders WHERE id = 'o1'") { resultSet ->
val row = resultSet.one()!!
row.getString("status") shouldBe "NEW"
}
// Execute with BoundStatement
shouldExecute(session().prepare("DELETE FROM orders WHERE id = ?").bind("o1"))
// Query with BoundStatement
shouldQuery(session().prepare("SELECT * FROM orders WHERE id = ?").bind("o1")) { rs ->
rs.one() shouldBe null
}
// Simulate downtime
pause()
// ... test resilience ...
unpause()
}
// Direct session access
cassandra {
session().execute("TRUNCATE orders")
}
```
## MongoDB assertions
```kotlin
mongodb {
// Save a document
save(Order(id = "o1", userId = "u1", amount = 99.99))
// Save to specific collection
save(Order(id = "o2", userId = "u2", amount = 50.0), collection = "archived_orders")
// Get by ObjectId
shouldGet<Order>(objectId = "o1") { order ->
order.amount shouldBe 99.99
}
// Query with filter string
shouldQuery<Order>(query = """{ "userId": "u1" }""") { orders ->
orders.size shouldBe 1
orders.first().status shouldBe "NEW"
}
// Delete
shouldDelete(objectId = "o1")
// Verify deletion
shouldNotExist(objectId = "o1")
// Simulate downtime
pause()
unpause()
}
// Direct client access
mongodb {
client().getDatabase("testdb").getCollection("orders").drop()
}
```
## Redis assertions
Redis uses the Lettuce client directly via `client()`:
```kotlin
redis {
// All operations via the Lettuce RedisClient
val connection = client().connect()
val commands = connection.sync()
commands.set("order:o1", """{"status":"NEW"}""")
commands.get("order:o1") shouldNotBe null
commands.del("order:o1")
connection.close()
}
// Simulate downtime
redis {
pause()
// ... test resilience ...
unpause()
}
```
## Elasticsearch assertions
```kotlin
elasticsearch {
// Save a document
save(id = "p1", instance = Product("p1", "Laptop", 999.99), index = "products")
// Get by key
shouldGet<Product>(index = "products", key = "p1") { product ->
product.name shouldBe "Laptop"
}
// Query with JSON string
shouldQuery<Product>(
query = """{ "match": { "name": "Laptop" } }""",
index = "products"
) { products ->
products.size shouldBe 1
}
// Query with Elasticsearch Query DSL object
shouldQuery<Product>(
query = Query.of { q -> q.match { m -> m.field("name").query("Laptop") } }
) { products ->
products.shouldNotBeEmpty()
}
// Delete
shouldDelete(key = "p1", index = "products")
// Verify deletion
shouldNotExist(key = "p1", index = "products")
// Simulate downtime
pause()
unpause()
}
// Direct client access
elasticsearch {
client().indices().create { it.index("new-index") }
}
```
## Couchbase assertions
```kotlin
couchbase {
// Save to default collection
saveToDefaultCollection(id = "o1", instance = Order("o1", "u1", 99.99))
// Save to specific collection
save(collection = "archived", id = "o2", instance = Order("o2", "u2", 50.0))
// Get by key (default collection)
shouldGet<Order>(key = "o1") { order ->
order.amount shouldBe 99.99
}
// Get from specific collection
shouldGet<Order>(collection = "archived", key = "o2") { order ->
order.userId shouldBe "u2"
}
// N1QL query
shouldQuery<Order>(query = "SELECT * FROM `test-bucket` WHERE userId = 'u1'") { orders ->
orders.size shouldBe 1
}
// Delete (default collection)
shouldDelete(key = "o1")
// Delete from specific collection
shouldDelete(collection = "archived", key = "o2")
// Verify deletion
shouldNotExist(key = "o1")
shouldNotExist(collection = "archived", key = "o2")
// Simulate downtime
pause()
unpause()
}
// Direct cluster/bucket access
couchbase {
cluster().queryIndexes().createPrimaryIndex("test-bucket")
bucket().defaultCollection().upsert("doc1", JsonObject.create())
}
```
## Kafka assertions
```kotlin
// Verify published
kafka {
shouldBePublished<OrderCreatedEvent>(atLeastIn = 10.seconds) {
actual.orderId == orderId && actual.amount == 99.99
}
}
// Verify consumed (stove-spring-kafka only)
kafka {
shouldBeConsumed<OrderCreatedEvent>(atLeastIn = 20.seconds) {
actual.orderId == orderId
}
}
// Publish a message
kafka {
publish(
topic = "order-events",
message = OrderCreated(orderId = "456", amount = 100.0),
key = "order-456".some()
)
}
// Verify failed handling
kafka {
shouldBeFailed<FailingEvent>(atLeastIn = 10.seconds) {
actual.id == 5L && reason is BusinessException
}
}
// Verify retries (stove-spring-kafka only)
kafka {
shouldBeRetried<FailingEvent>(atLeastIn = 1.minutes, times = 3) {
actual.id == "789"
}
}
// Access message metadata
kafka {
shouldBePublished<OrderCreatedEvent>(atLeastIn = 10.seconds) {
actual.orderId == orderId &&
metadata.topic == "order-events" &&
metadata.headers["correlation-id"] != null
}
}
```
## WireMock mocking
```kotlin
wiremock {
mockGet(
url = "/inventory/$productId",
statusCode = 200,
responseBody = InventoryResponse(available = true).some()
)
mockPost(
url = "/payments/charge",
statusCode = 200,
responseBody = PaymentResult(success = true).some()
)
}
// PUT, PATCH, DELETE mocks
wiremock {
mockPut(url = "/products/123", statusCode = 200,
responseBody = Product("123", "Updated", 899.99).some())
mockPatch(url = "/users/123", statusCode = 200,
requestBody = mapOf("email" to "new@example.com").some())
mockDelete(url = "/products/123", statusCode = 204)
mockHead(url = "/products/exists/123", statusCode = 200)
}
// Partial body matching — match specific fields, ignore the rest
wiremock {
mockPostContaining(
url = "/api/orders",
requestContaining = mapOf("productId" to 123),
statusCode = 201,
responseBody = OrderResponse(orderId = "order-123").some()
)
// Deep nested matching with dot notation
mockPostContaining(
url = "/api/checkout",
requestContaining = mapOf(
"order.customer.id" to "cust-123",
"order.payment.method" to "credit_card"
),
statusCode = 200
)
}
// Sequential responses (behavioral mocking)
wiremock {
behaviourFor("/api/service", WireMock::get) {
initially { aResponse().withStatus(503) }
then { aResponse().withStatus(503) }
then { aResponse().withStatus(200).withBody(it.serialize(result)) }
}
}
```
## gRPC Mock
```kotlin
grpcMock {
// Unary
mockUnary(
serviceName = "frauddetection.FraudDetectionService",
methodName = "CheckFraud",
response = CheckFraudResponse.newBuilder()
.setIsFraudulent(false).setRiskScore(0.15).build()
)
// With request matching
mockUnary(
serviceName = "users.UserService",
methodName = "GetUser",
requestMatcher = RequestMatcher.ExactMessage(
GetUserRequest.newBuilder().setUserId("123").build()
),
response = GetUserResponse.newBuilder().setName("John").build()
)
// With authentication
mockUnary(
serviceName = "secure.SecureService",
methodName = "GetSecret",
metadataMatcher = MetadataMatcher.BearerToken("valid-token"),
response = SecretResponse.newBuilder().setData("confidential").build()
)
// Server streaming
mockServerStream(
serviceName = "streaming.ItemService",
methodName = "ListItems",
responses = listOf(item1, item2, item3)
)
// Bidirectional streaming
mockBidiStream(
serviceName = "chat.ChatService",
methodName = "Chat"
) { requestFlow ->
requestFlow.map { bytes ->
val req = ChatMessage.parseFrom(bytes)
ChatMessage.newBuilder().setMessage("Echo: ${req.message}").build()
}
}
// Error
mockError(
serviceName = "users.UserService",
methodName = "GetUser",
status = Status.Code.NOT_FOUND,
message = "User not found"
)
}
```
## gRPC Client
For testing your own gRPC server:
```kotlin
grpc {
channel<OrderQueryServiceGrpcKt.OrderQueryServiceCoroutineStub> {
val response = getOrder(
GetOrderRequest.newBuilder().setOrderId(orderId).build()
)
response.found shouldBe true
response.order.status shouldBe "CONFIRMED"
}
}
```
## Bridge (DI access)
```kotlin
// Single bean
using<OrderService> {
val order = getOrderByUserId(userId)
order shouldNotBe null
order!!.status shouldBe OrderStatus.CONFIRMED
}
// Multiple beans (up to 5 supported)
using<UserService, OrderService> { userService, orderService ->
val user = userService.findById(123)
val orders = orderService.findByUserId(123)
orders.size shouldBeGreaterThan 0
}
using<A, B, C> { a, b, c -> /* ... */ }
```
## Trace validation
```kotlin
tracing {
// Span assertions
shouldContainSpan("OrderService.processOrder")
shouldContainSpanMatching { it.operationName.contains("Repository") }
shouldNotContainSpan("AdminService.delete")
shouldNotHaveFailedSpans()
shouldHaveFailedSpan("PaymentGateway.charge")
shouldHaveSpanWithAttribute("http.method", "GET")
// Performance assertions
executionTimeShouldBeLessThan(500.milliseconds)
spanCountShouldBeAtLeast(5)
// Debugging
println(renderTree()) // Hierarchical trace view
println(renderSummary()) // Compact summary
}
```
## Keyed system tests
Access keyed systems by passing the `SystemKey` to the validation DSL:
```kotlin
// Given keys defined in setup:
// object AppDb : SystemKey
// object AnalyticsDb : SystemKey
// object PaymentService : SystemKey
test("should write to both databases") {
stove {
http {
postAndExpectBody<OrderResponse>(
uri = "/orders",
body = CreateOrderRequest("u1", 99.99).some()
) { it.status shouldBe 201 }
}
// Assert against the app database
postgresql(AppDb) {
shouldQuery<OrderRow>(
query = "SELECT * FROM orders WHERE user_id = 'u1'",
mapper = { row -> OrderRow(row.string("id"), row.string("status")) }
) { it.size shouldBe 1 }
}
// Assert against the analytics database
postgresql(AnalyticsDb) {
shouldQuery<AnalyticsRow>(
query = "SELECT * FROM events WHERE user_id = 'u1'",
mapper = { row -> AnalyticsRow(row.string("event_type")) }
) { it.first().eventType shouldBe "ORDER_CREATED" }
}
}
}
// Keyed WireMock and HTTP
test("should call payment and inventory services") {
stove {
wiremock(PaymentService) {
mockPost("/charge", 200, PaymentResult(true).some())
}
wiremock(InventoryService) {
mockGet("/stock/item-1", 200, StockResponse(10).some())
}
http {
postAndExpectBody<OrderResponse>("/orders", body = order.some()) {
it.status shouldBe 201
}
}
}
}
```
All systems support keyed access: `postgresql(key)`, `mysql(key)`, `mssql(key)`, `cassandra(key)`, `mongodb(key)`, `redis(key)`, `elasticsearch(key)`, `couchbase(key)`, `kafka(key)`, `wiremock(key)`, `grpcMock(key)`, `grpc(key)`, `http(key)`.
## Smoke testing (providedApplication)
With `providedApplication()`, test a remote/deployed application without starting it locally. No `Bridge`/`using<T>` — only infrastructure assertions:
```kotlin
test("staging smoke test — order flow") {
stove {
val userId = "smoke-${UUID.randomUUID()}"
// Hit the remote API
http {
postAndExpectBody<OrderResponse>(
uri = "/api/orders",
body = CreateOrderRequest(userId, 49.99).some()
) { response ->
response.status shouldBe 201
}
}
// Verify side effects in the remote database
postgresql(AppDb) {
shouldQuery<OrderRow>(
query = "SELECT * FROM orders WHERE user_id = '$userId'",
mapper = { row -> OrderRow(row.string("id"), row.string("status")) }
) { orders ->
orders.size shouldBe 1
orders.first().status shouldBe "CONFIRMED"
}
}
// Verify Kafka event on the remote cluster
kafka {
shouldBePublished<OrderCreatedEvent>(10.seconds) {
actual.userId == userId
}
}
}
}
```
## Multi-system test
```kotlin
test("complete order flow") {
stove {
val userId = "user-${UUID.randomUUID()}"
var orderId: String? = null
grpcMock {
mockUnary(
serviceName = "frauddetection.FraudDetectionService",
methodName = "CheckFraud",
response = CheckFraudResponse.newBuilder()
.setIsFraudulent(false).build()
)
}
wiremock {
mockGet("/inventory/macbook", 200,
responseBody = InventoryResponse("macbook", true, 10).some())
mockPost("/payments/charge", 200,
responseBody = PaymentResult(true, "txn-123", 2499.99).some())
}
http {
postAndExpectBody<OrderResponse>(
uri = "/api/orders",
body = CreateOrderRequest(userId, "macbook", 2499.99).some()
) { response ->
response.status shouldBe 201
orderId = response.body().orderId
}
}
postgresql {
shouldQuery<OrderRow>(
query = "SELECT * FROM orders WHERE user_id = '$userId'",
mapper = { row ->
OrderRow(row.string("id"), row.string("user_id"),
row.string("product_id"), row.double("amount"),
row.string("status"))
}
) { orders ->
orders.size shouldBe 1
orders.first().status shouldBe "CONFIRMED"
}
}
kafka {
shouldBePublished<OrderCreatedEvent>(10.seconds) {
actual.userId == userId
}
}
grpc {
channel<OrderQueryServiceGrpcKt.OrderQueryServiceCoroutineStub> {
val response = getOrder(
GetOrderRequest.newBuilder().setOrderId(orderId!!).build()
)
response.order.status shouldBe "CONFIRMED"
}
}
using<OrderService> {
getOrderByUserId(userId)!!.status shouldBe OrderStatus.CONFIRMED
}
}
}
```
## Anti-patterns
| Don't | Do |
|---|---|
| `Thread.sleep(5000)` | `shouldBePublished<Event>(atLeastIn = 10.seconds) { ... }` |
| Hardcoded IDs `"order-123"` | `UUID.randomUUID().toString()` |
| Shared mutable state | Independent tests with unique data |
| Only assert `status shouldBe 200` | Assert response body, DB state, events |
| Call real external services | Use WireMock / gRPC Mock |
| Configure Stove per test class | Single `AbstractProjectConfig` |
================================================
FILE: .editorconfig
================================================
root = true
[*]
insert_final_newline = true
ktlint_standard_package-name = disabled
ktlint_standard_filename = disabled
ktlint_standard_no-wildcard-imports = disabled
ktlint_standard_multiline-expression-wrapping = disabled
ktlint_standard_string-template-indent = disabled
ktlint_standard_function-signature = disabled
[{*.kt,*.kts}]
indent_style = space
max_line_length = 140
indent_size = 2
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_continuation_indent_size = 2
ij_kotlin_allow_trailing_comma = false
ij_kotlin_allow_trailing_comma_on_call_site = false
ij_kotlin_name_count_to_use_star_import = 2
ij_kotlin_name_count_to_use_star_import_for_members = 2
[{**/test/**.kt,**/test-e2e/**.kt,**/test-int/**.kt}]
max_line_length = 240
ktlint_standard_no-consecutive-comments = disabled
================================================
FILE: .gitattributes
================================================
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
/gradlew text eol=lf
# These are Windows script files and should use crlf
*.bat text eol=crlf
================================================
FILE: .github/workflows/build-jvm-recipes.yml
================================================
name: Build JVM Recipes
on:
push:
branches: [main]
paths:
- 'recipes/jvm/**'
pull_request:
branches: [main]
paths:
- 'recipes/jvm/**'
# Cancel in-progress runs for the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-test:
runs-on: ubuntu-latest
permissions:
checks: write
pull-requests: write
services:
docker:
image: docker:dind
options: --privileged
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
with:
gradle-version: current
cache-read-only: false
cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: Cache Gradle dependencies
uses: actions/cache@v5
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-jvm-recipes-${{ runner.os }}-${{ hashFiles('recipes/jvm/**/gradle/wrapper/gradle-wrapper.properties', 'recipes/jvm/**/gradle/libs.versions.toml') }}
restore-keys: |
gradle-jvm-recipes-${{ runner.os }}-
# Cache Docker images for testcontainers
- name: Cache Docker images
uses: actions/cache@v5
with:
path: /var/lib/docker
key: docker-jvm-recipes-${{ runner.os }}-${{ hashFiles('recipes/jvm/**/gradle/libs.versions.toml') }}
restore-keys: |
docker-jvm-recipes-${{ runner.os }}-
# Run all tasks in a single Gradle invocation
- name: Build, Test, and E2E Test
run: |
gradle -p recipes/jvm build test e2eTest \
--build-cache \
--no-daemon \
-Dorg.gradle.parallel=true
# Upload test results
- name: Upload test results
if: always()
uses: actions/upload-artifact@v7
with:
name: jvm-recipes-test-results
path: |
recipes/jvm/**/build/test-results/
recipes/jvm/**/build/reports/tests/
retention-days: 7
# Publish test report for PRs
- name: Publish Test Report
uses: mikepenz/action-junit-report@v6
if: always() && github.event_name == 'pull_request'
with:
report_paths: "recipes/jvm/**/build/test-results/**/*.xml"
check_name: "JVM Recipes Test Results"
detailed_summary: true
include_passed: false
================================================
FILE: .github/workflows/build-process-recipes.yml
================================================
name: Build Process Recipes
on:
push:
branches: [main]
paths:
- 'recipes/process/**'
pull_request:
branches: [main]
paths:
- 'recipes/process/**'
# Cancel in-progress runs for the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
go-showcase:
runs-on: ubuntu-latest
permissions:
checks: write
pull-requests: write
services:
docker:
image: docker:dind
options: --privileged
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 21
- uses: actions/setup-go@v6
with:
go-version-file: recipes/process/golang/go-showcase/go.mod
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
with:
gradle-version: current
cache-read-only: false
cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: Cache Gradle dependencies
uses: actions/cache@v5
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-process-recipes-${{ runner.os }}-${{ hashFiles('recipes/process/**/gradle/wrapper/gradle-wrapper.properties') }}
restore-keys: |
gradle-process-recipes-${{ runner.os }}-
# Cache Docker images for testcontainers
- name: Cache Docker images
uses: actions/cache@v5
with:
path: /var/lib/docker
key: docker-process-recipes-${{ runner.os }}
restore-keys: |
docker-process-recipes-${{ runner.os }}-
- name: Run E2E Tests (all Kafka libraries)
env:
GOTOOLCHAIN: auto
run: |
GO_EXECUTABLE="$(command -v go)"
export GO_EXECUTABLE
"$GO_EXECUTABLE" version
gradle -p recipes/process/golang/go-showcase e2eTest \
--build-cache \
--no-daemon
- name: Run Container E2E Tests (sarama)
env:
GOTOOLCHAIN: auto
run: |
GO_EXECUTABLE="$(command -v go)"
export GO_EXECUTABLE
"$GO_EXECUTABLE" version
gradle -p recipes/process/golang/go-showcase e2eTest-container \
--build-cache \
--no-daemon && \
gradle -p recipes/process/golang/go-showcase removeContainerImage \
--no-daemon
# Upload test results
- name: Upload test results
if: always()
uses: actions/upload-artifact@v7
with:
name: process-recipes-test-results
path: |
recipes/process/**/build/test-results/
recipes/process/**/build/reports/tests/
retention-days: 7
# Publish test report for PRs
- name: Publish Test Report
uses: mikepenz/action-junit-report@v6
if: always() && github.event_name == 'pull_request'
with:
report_paths: "recipes/process/**/build/test-results/**/*.xml"
check_name: "Process Recipes Test Results"
detailed_summary: true
include_passed: false
================================================
FILE: .github/workflows/build.yml
================================================
name: Build
on:
push:
branches: [main]
paths:
- 'examples/**'
- 'lib/**'
- 'starters/**'
- 'gradle/**'
- 'build.gradle.kts'
- 'settings.gradle.kts'
- 'buildSrc/**'
pull_request:
branches: [main]
paths:
- 'examples/**'
- 'lib/**'
- 'starters/**'
- 'gradle/**'
- 'build.gradle.kts'
- 'settings.gradle.kts'
- 'buildSrc/**'
# Cancel in-progress runs for the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-test:
runs-on: ubuntu-latest
permissions:
checks: write
id-token: write
issues: write
pull-requests: write
services:
docker:
image: docker:dind
options: --privileged
steps:
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
with:
gradle-version: current
cache-read-only: false
cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
- name: Cache Gradle dependencies
uses: actions/cache@v5
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties', '**/gradle/libs.versions.toml') }}
restore-keys: |
gradle-${{ runner.os }}-
# Cache Docker images for testcontainers
- name: Cache Docker images
uses: actions/cache@v5
with:
path: /var/lib/docker
key: docker-${{ runner.os }}-${{ hashFiles('**/gradle/libs.versions.toml') }}
restore-keys: |
docker-${{ runner.os }}-
# Run all tasks in a single Gradle invocation for optimal build time
- name: Build, Test, and Coverage
run: |
gradle build test koverXmlReport \
--build-cache \
--configuration-cache \
--parallel \
--no-daemon
# Upload test results
- name: Upload test results
if: always()
uses: actions/upload-artifact@v7
with:
name: test-results
path: |
**/build/test-results/test/
**/build/reports/tests/
retention-days: 7
# Upload coverage to Codecov
- name: Upload coverage to Codecov
if: github.repository == 'Trendyol/stove'
uses: codecov/codecov-action@v6
with:
fail_ci_if_error: false
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
# Publish test report for PRs
- name: Publish Test Report
uses: mikepenz/action-junit-report@v6
if: always() && github.event_name == 'pull_request'
with:
report_paths: "**/build/test-results/test/*.xml"
check_name: "Test Results"
detailed_summary: true
include_passed: false
================================================
FILE: .github/workflows/gradle-publish-release.yml
================================================
name: Release
on:
workflow_dispatch:
jobs:
release:
runs-on: ubuntu-latest
if: github.repository == 'Trendyol/stove'
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
server-id: github
settings-path: ${{ github.workspace }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
with:
gradle-version: current
- name: Extract version from gradle.properties
run: |
VERSION=$(grep '^version=' gradle.properties | cut -d'=' -f2)
echo "JRELEASER_PROJECT_VERSION=$VERSION" >> $GITHUB_ENV
echo "Releasing version: $VERSION"
- name: Publish to Maven Central
run: gradle --no-configuration-cache publish
env:
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.gpg_private_key }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.gpg_passphrase }}
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ossrh_username }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ossrh_pass }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create GitHub Release
uses: jreleaser/release-action@v2
with:
arguments: full-release
env:
JRELEASER_GITHUB_TOKEN: ${{ secrets.BOT_REPO_TOKEN }}
JRELEASER_PROJECT_VERSION: ${{ env.JRELEASER_PROJECT_VERSION }}
- name: Tag Go stove-kafka module
run: |
git tag "go/stove-kafka/v${{ env.JRELEASER_PROJECT_VERSION }}"
git push origin "go/stove-kafka/v${{ env.JRELEASER_PROJECT_VERSION }}"
env:
GITHUB_TOKEN: ${{ secrets.BOT_REPO_TOKEN }}
- name: Upload JReleaser output
if: always()
uses: actions/upload-artifact@v7
with:
name: jreleaser-release
path: |
out/jreleaser/trace.log
out/jreleaser/output.properties
================================================
FILE: .github/workflows/gradle-publish-snapshot.yml
================================================
name: Publish to Snapshot Maven
on:
workflow_dispatch:
jobs:
publish:
runs-on: ubuntu-latest
if: github.repository == 'Trendyol/stove'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v6
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
server-id: github
settings-path: ${{ github.workspace }}
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
with:
gradle-version: current
- name: Publish to Maven Repository
run: gradle --no-configuration-cache publish --parallel
env:
SNAPSHOT: true
BUILD_NUMBER: ${{ github.run_number }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.gpg_private_key }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.gpg_passphrase }}
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.ossrh_username }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ossrh_pass }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/publish-to-ghpages.yml
================================================
name: Publish MkDocs to GitHub Pages
on:
push:
branches: [ main ]
paths: [ 'docs/**' ]
jobs:
deploy:
runs-on: ubuntu-latest
if: github.repository == 'Trendyol/stove'
steps:
- name: Checkout Repository
uses: actions/checkout@v6
- name: Install Dependencies
run: |
pip install mkdocs mkdocs-material mkdocs-awesome-pages-plugin --use-deprecated=legacy-resolver
- name: Build Site
run: |
mkdocs build
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.BOT_REPO_TOKEN }}
publish_dir: ./site
================================================
FILE: .github/workflows/scorecard.yml
================================================
name: Scorecard supply-chain security
on:
branch_protection_rule:
schedule:
- cron: '29 23 * * 3'
push:
branches: [ "main", "master"]
pull_request:
branches: ["main", "master"]
permissions: read-all
jobs:
visibility-check:
outputs:
visibility: ${{ steps.drv.outputs.visibility }}
runs-on: ubuntu-latest
steps:
- name: Determine repository visibility
id: drv
run: |
visibility=$(gh api /repos/$GITHUB_REPOSITORY --jq '.visibility')
echo "visibility=$visibility" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ github.token }}
analysis:
if: ${{ needs.visibility-check.outputs.visibility == 'public' }}
needs: visibility-check
runs-on: ubuntu-latest
permissions:
security-events: write
id-token: write
steps:
- name: "Checkout code"
uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@05bb7c663f6ec9bd8484da0a5b5a77d423e3f88c
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- name: "Upload artifact"
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
with:
sarif_file: results.sarif
================================================
FILE: .github/workflows/stove-cli-ci.yml
================================================
name: Stove CLI CI
on:
push:
branches: [main]
paths:
- 'tools/stove-cli/**'
- 'lib/stove-dashboard-api/src/main/proto/**'
pull_request:
branches: [main]
paths:
- 'tools/stove-cli/**'
- 'lib/stove-dashboard-api/src/main/proto/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: tools/stove-cli
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 24
cache: 'npm'
cache-dependency-path: tools/stove-cli/spa/package-lock.json
- name: Install SPA dependencies
run: cd spa && npm ci
- name: SPA lint and format check
run: cd spa && npx biome check src
- name: Build SPA
run: cd spa && npm run build
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- name: Install protoc
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
tools/stove-cli/target
key: cargo-${{ runner.os }}-${{ hashFiles('tools/stove-cli/Cargo.lock') }}
restore-keys: cargo-${{ runner.os }}-
- name: Check formatting
run: cargo fmt -- --check
- name: Clippy
run: cargo clippy -- -D warnings
env:
SKIP_SPA_BUILD: "1"
- name: Run tests
run: cargo test
env:
SKIP_SPA_BUILD: "1"
================================================
FILE: .github/workflows/stove-cli-release.yml
================================================
name: Stove CLI Build
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version to release (leave empty to read from gradle.properties)'
required: false
permissions:
contents: write
jobs:
resolve-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.resolve.outputs.version }}
tag: ${{ steps.resolve.outputs.tag }}
steps:
- uses: actions/checkout@v6
- name: Resolve version
id: resolve
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
INPUT_VERSION="${{ github.event.inputs.version }}"
if [ -n "$INPUT_VERSION" ]; then
VERSION="$INPUT_VERSION"
else
VERSION=$(grep '^version=' gradle.properties | cut -d'=' -f2)
fi
TAG="v${VERSION}"
else
TAG="${GITHUB_REF_NAME}"
VERSION="${TAG#v}"
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "Resolved version: ${VERSION} (tag: ${TAG})"
build:
needs: resolve-version
strategy:
matrix:
include:
- target: aarch64-apple-darwin
runner: macos-14
label: darwin-arm64
- target: x86_64-apple-darwin
runner: macos-14
label: darwin-amd64
- target: x86_64-unknown-linux-gnu
runner: ubuntu-latest
label: linux-amd64
runs-on: ${{ matrix.runner }}
defaults:
run:
working-directory: tools/stove-cli
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 24
cache: 'npm'
cache-dependency-path: tools/stove-cli/spa/package-lock.json
- name: Build SPA
run: cd spa && npm ci && npm run build
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install protoc (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Install protoc (macOS)
if: runner.os == 'macOS'
run: brew install protobuf
- uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
tools/stove-cli/target
key: cargo-release-${{ matrix.target }}-${{ hashFiles('tools/stove-cli/Cargo.lock') }}
restore-keys: cargo-release-${{ matrix.target }}-
- name: Build release binary
run: cargo build --release --target ${{ matrix.target }}
env:
SKIP_SPA_BUILD: "1"
- name: Package tarball
run: |
VERSION="${{ needs.resolve-version.outputs.version }}"
ARCHIVE="stove-${VERSION}-${{ matrix.label }}.tar.gz"
mkdir -p staging
cp "target/${{ matrix.target }}/release/stove" staging/
cp "${GITHUB_WORKSPACE}/LICENSE" staging/
tar czf "${ARCHIVE}" -C staging .
shasum -a 256 "${ARCHIVE}" > "${ARCHIVE}.sha256"
- uses: actions/upload-artifact@v7
with:
name: binaries-${{ matrix.label }}
path: |
tools/stove-cli/stove-*.tar.gz
tools/stove-cli/stove-*.sha256
release:
needs: [resolve-version, build]
runs-on: ubuntu-latest
outputs:
version: ${{ needs.resolve-version.outputs.version }}
steps:
- uses: actions/download-artifact@v8
with:
pattern: binaries-*
merge-multiple: true
- name: Upload CLI binaries to release
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ needs.resolve-version.outputs.tag }}
name: "Stove v${{ needs.resolve-version.outputs.version }}"
files: |
stove-*.tar.gz
stove-*.sha256
fail_on_unmatched_files: false
append_body: true
body: |
---
### Stove CLI Install
**Homebrew (macOS):**
```
brew install Trendyol/trendyol-tap/stove
```
**Shell script (macOS & Linux):**
```
curl -fsSL https://raw.githubusercontent.com/Trendyol/stove/main/tools/stove-cli/install.sh | sh
```
**Manual:** Download the archive for your platform below, extract, and place `stove` in your PATH.
update-homebrew:
needs: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Generate and push Homebrew formula
env:
VERSION: ${{ needs.release.outputs.version }}
TAP_TOKEN: ${{ secrets.BOT_REPO_TOKEN }}
run: |
BASE_URL="https://github.com/Trendyol/stove/releases/download/v${VERSION}"
SHA_DARWIN_ARM64=$(curl -fsSL "${BASE_URL}/stove-${VERSION}-darwin-arm64.tar.gz.sha256" | awk '{print $1}')
SHA_DARWIN_AMD64=$(curl -fsSL "${BASE_URL}/stove-${VERSION}-darwin-amd64.tar.gz.sha256" | awk '{print $1}')
SHA_LINUX_AMD64=$(curl -fsSL "${BASE_URL}/stove-${VERSION}-linux-amd64.tar.gz.sha256" | awk '{print $1}')
cp tools/stove-cli/Formula/stove.rb stove.rb
sed -i "s/__VERSION__/${VERSION}/" stove.rb
sed -i "s/__SHA256_DARWIN_ARM64__/${SHA_DARWIN_ARM64}/" stove.rb
sed -i "s/__SHA256_DARWIN_AMD64__/${SHA_DARWIN_AMD64}/" stove.rb
sed -i "s/__SHA256_LINUX_AMD64__/${SHA_LINUX_AMD64}/" stove.rb
WORKDIR="$(mktemp -d)"
git clone "https://x-access-token:${TAP_TOKEN}@github.com/Trendyol/homebrew-trendyol-tap.git" "${WORKDIR}"
cp stove.rb "${WORKDIR}/stove.rb"
cd "${WORKDIR}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add stove.rb
git commit -m "stove ${VERSION}"
git push
================================================
FILE: .gitignore
================================================
.DS_Store
/site
/.idea
.idea/shelf
/confluence/target
/dependencies/repo
/android.tests.dependencies
/dependencies/android.tests.dependencies
/dist
/local
/gh-pages
/ideaSDK
/clionSDK
/android-studio/sdk
out/
/tmp
/intellij
workspace.xml
*.versionsBackup
/idea/testData/debugger/tinyApp/classes*
/jps-plugin/testData/kannotator
/js/js.translator/testData/out/
/js/js.translator/testData/out-min/
/js/js.translator/testData/out-pir/
.gradle/
build/
!**/src/**/build
!**/test/**/build
*.iml
!**/testData/**/*.iml
.idea/remote-targets.xml
.idea/libraries/Gradle*.xml
.idea/libraries/Maven*.xml
.idea/artifacts/PILL_*.xml
.idea/artifacts/KotlinPlugin.xml
.idea/modules
.idea/runConfigurations/JPS_*.xml
.idea/runConfigurations/PILL_*.xml
.idea/runConfigurations/_FP_*.xml
.idea/runConfigurations/_MT_*.xml
.idea/libraries
.idea/modules.xml
.idea/gradle.xml
.idea/compiler.xml
.idea/inspectionProfiles/profiles_settings.xml
.idea/.name
.idea/artifacts/dist_auto_*
.idea/artifacts/dist.xml
.idea/artifacts/ideaPlugin.xml
.idea/artifacts/kotlinc.xml
.idea/artifacts/kotlin_compiler_jar.xml
.idea/artifacts/kotlin_plugin_jar.xml
.idea/artifacts/kotlin_jps_plugin_jar.xml
.idea/artifacts/kotlin_daemon_client_jar.xml
.idea/artifacts/kotlin_imports_dumper_compiler_plugin_jar.xml
.idea/artifacts/kotlin_main_kts_jar.xml
.idea/artifacts/kotlin_compiler_client_embeddable_jar.xml
.idea/artifacts/kotlin_reflect_jar.xml
.idea/artifacts/kotlin_stdlib_js_ir_*
.idea/artifacts/kotlin_test_js_ir_*
.idea/artifacts/kotlin_stdlib_wasm_*
.idea/artifacts/kotlinx_atomicfu_runtime_*
.idea/artifacts/kotlinx_cli_jvm_*
.idea/jarRepositories.xml
.idea/csv-plugin.xml
.idea/libraries-with-intellij-classes.xml
.idea/misc.xml
.idea/**
node_modules/
.rpt2_cache/
libraries/tools/kotlin-test-js-runner/lib/
local.properties
buildSrcTmp/
distTmp/
outTmp/
/test.output
/kotlin-native/dist
kotlin-ide/
**/bin/**/*
# Ignore Gradle project-specific cache directory
.gradle
# Ignore Gradle build output directory
build
.kotlin
*.tsbuildinfo
.junie
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
<h1 align="center">Stove</h1>
<p align="center">
End-to-end testing framework for the JVM.<br/>
Test your application against real infrastructure with a unified Kotlin DSL.
</p>
<p align="center">
<img src="https://img.shields.io/maven-central/v/com.trendyol/stove?versionPrefix=0&label=release&color=blue" alt="Release"/>
<a href="https://github.com/Trendyol/homebrew-trendyol-tap"><img src="https://img.shields.io/github/v/release/Trendyol/stove?label=StoveCLI(homebrew)&logo=homebrew&color=FBB040" alt="Homebrew"/></a>
<a href="https://central.sonatype.com/service/rest/repository/browse/maven-snapshots/com/trendyol/"><img src="https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fcentral.sonatype.com%2Frepository%2Fmaven-snapshots%2Fcom%2Ftrendyol%2Fstove%2Fmaven-metadata.xml&query=%2F%2Fmetadata%2Fversioning%2Flatest&label=snapshot&color=orange" alt="Snapshot"/></a>
<a href="https://codecov.io/gh/Trendyol/stove"><img src="https://codecov.io/gh/Trendyol/stove/graph/badge.svg?token=HcKBT3chO7" alt="codecov"/></a>
<a href="https://scorecard.dev/viewer/?uri=github.com/Trendyol/stove"><img src="https://img.shields.io/ossf-scorecard/github.com/Trendyol/stove?label=openssf%20scorecard&style=flat" alt="OpenSSF Scorecard"/></a>
</p>
```kotlin
stove {
// Call API and verify response
http {
postAndExpectBodilessResponse("/orders", body = CreateOrderRequest(userId, productId).some()) {
it.status shouldBe 201
}
}
// Verify database state
postgresql {
shouldQuery<Order>("SELECT * FROM orders WHERE user_id = '$userId'", mapper = { row ->
Order(row.string("status"))
}) {
it.first().status shouldBe "CONFIRMED"
}
}
// Verify event was published
kafka {
shouldBePublished<OrderCreatedEvent> {
actual.userId == userId
}
}
// Access application beans directly
using<InventoryService> {
getStock(productId) shouldBe 9
}
}
```
## Why Stove?
The JVM ecosystem has excellent frameworks for building applications, but e2e testing remains fragmented. Testcontainers
handles infrastructure, but you still write boilerplate for configuration, app startup, and assertions. Differently for
each framework.
Stove explores how the testing experience on the JVM can be improved by unifying assertions and the supporting
infrastructure. It creates a concise and expressive testing DSL by leveraging Kotlin's unique language features.
Stove works with Java, Kotlin, and Scala applications across Spring Boot, Ktor, Micronaut, and Quarkus. Because tests are
framework-agnostic, teams can migrate between stacks without rewriting test code. It empowers developers to write clear
assertions even for code that is traditionally hard to test (async flows, message consumers, database side effects).
**What Stove does:**
- Starts containers via Testcontainers or connect **provided** infra (PostgreSQL, MySQL, Kafka, etc.)
- Launches your **actual** application with test configuration
- Exposes a unified DSL for assertions across all components
- Provides access to your DI container from tests
- Debug your entire use case with one click (breakpoints work everywhere)
- Get code coverage from e2e test execution
- Supports Spring Boot, Ktor, Micronaut, Quarkus
- Extensible architecture for adding new components and
frameworks ([Writing Custom Systems](https://trendyol.github.io/stove/writing-custom-systems/))
## Dashboard (New in 0.23.0)
Stove Dashboard introduces a local real-time dashboard for end-to-end test runs. It captures HTTP calls, Kafka activity,
database assertions, and traces in one place so you can inspect successful and failed runs with full context.
https://github.com/user-attachments/assets/14597dc6-e9d4-43ab-8cfa-578ab3c3e6df
**Quick start**
```bash
# 1) Install and start the Dashboard CLI
brew install Trendyol/trendyol-tap/stove
stove
# 2) Run your tests and open the dashboard
./gradlew test
# http://localhost:4040
```
```kotlin
// build.gradle.kts
plugins {
id("com.trendyol.stove.tracing") version "$stoveVersion"
}
dependencies {
testImplementation(platform("com.trendyol:stove-bom:$version"))
testImplementation("com.trendyol:stove-extensions-kotest") // or stove-extensions-junit
testImplementation("com.trendyol:stove-dashboard")
testImplementation("com.trendyol:stove-tracing")
}
stoveTracing {
serviceName.set("product-api")
}
```
```kotlin
// Kotest
class StoveConfig : AbstractProjectConfig() {
override val extensions = listOf(StoveKotestExtension())
override suspend fun beforeProject() {
Stove().with {
dashboard { DashboardSystemOptions(appName = "product-api") }
tracing { enableSpanReceiver() } // recommended
}.run()
}
override suspend fun afterProject() = Stove.stop()
}
// JUnit
@ExtendWith(StoveJUnitExtension::class)
abstract class BaseE2ETest { /* Stove().with { ... }.run() in @BeforeAll */ }
```
Keep `stove-cli`, the Stove BOM, the tracing Gradle plugin, and your Stove test dependencies on the same Stove version. The dashboard warns on version mismatches, but aligning versions avoids missing or inconsistent dashboard data.
See [Dashboard docs](https://trendyol.github.io/stove/Components/18-dashboard/) and
[0.23.0 release notes](https://trendyol.github.io/stove/release-notes/0.23.0/) for full details.
## Getting Started
**1. Add dependencies**
```kotlin
dependencies {
// Import BOM for version management
testImplementation(platform("com.trendyol:stove-bom:$version"))
// Core and framework starter
testImplementation("com.trendyol:stove")
testImplementation("com.trendyol:stove-spring") // or stove-ktor, stove-micronaut, stove-quarkus
// Component modules
testImplementation("com.trendyol:stove-postgres")
testImplementation("com.trendyol:stove-mysql")
testImplementation("com.trendyol:stove-kafka")
}
```
> **Snapshots:** As of 5th June 2025, Stove's snapshot packages are hosted on [Central Sonatype](https://central.sonatype.com/service/rest/repository/browse/maven-snapshots/com/trendyol/).
> ```kotlin
> repositories {
> maven("https://central.sonatype.com/repository/maven-snapshots")
> }
> ```
**2. Configure Stove** (runs once before all tests)
```kotlin
class StoveConfig : AbstractProjectConfig() {
override suspend fun beforeProject() = Stove()
.with {
httpClient {
HttpClientSystemOptions(baseUrl = "http://localhost:8080")
}
postgresql {
PostgresqlOptions(
cleanup = { it.execute("TRUNCATE orders, users") },
configureExposedConfiguration = { listOf("spring.datasource.url=${it.jdbcUrl}") }
).migrations {
register<CreateUsersTable>()
}
}
kafka {
KafkaSystemOptions(
cleanup = { it.deleteTopics(listOf("orders")) },
configureExposedConfiguration = { listOf("kafka.bootstrapServers=${it.bootstrapServers}") }
).migrations {
register<CreateOrdersTopic>()
}
}
bridge()
springBoot(runner = { params ->
myApp.run(params) { addTestDependencies() }
})
}.run()
override suspend fun afterProject() = Stove.stop()
}
```
**3. Write tests**
```kotlin
test("should process order") {
stove {
http {
get<Order>("/orders/123") {
it.status shouldBe "CONFIRMED"
}
}
postgresql {
shouldQuery<Order>("SELECT * FROM orders", mapper = { row ->
Order(row.string("status"))
}) {
it.size shouldBe 1
}
}
kafka {
shouldBePublished<OrderCreatedEvent> {
actual.orderId == "123"
}
}
}
}
```
## Writing Tests
All assertions happen inside `stove { }`. Each component has its own DSL block.
### HTTP
```kotlin
http {
get<User>("/users/$id") {
it.name shouldBe "John"
}
postAndExpectBodilessResponse("/users", body = request.some()) {
it.status shouldBe 201
}
postAndExpectBody<User>("/users", body = request.some()) {
it.id shouldNotBe null
}
}
```
### Database
```kotlin
postgresql { // also: mysql, mongodb, couchbase, mssql, elasticsearch, redis
shouldExecute("INSERT INTO users (name) VALUES ('Jane')")
shouldQuery<User>("SELECT * FROM users", mapper = { row ->
User(row.string("name"))
}) {
it.size shouldBe 1
}
}
```
### Kafka
```kotlin
kafka {
publish("orders.created", OrderCreatedEvent(orderId = "123"))
shouldBeConsumed<OrderCreatedEvent> {
actual.orderId == "123"
}
shouldBePublished<OrderConfirmedEvent> {
actual.orderId == "123"
}
}
```
### External API Mocking
```kotlin
wiremock {
mockGet("/external-api/users/1", responseBody = User(id = 1, name = "John").some())
mockPost("/external-api/notify", statusCode = 202)
}
```
### Application Beans
Access your DI container directly via `bridge()`:
```kotlin
using<OrderService> { processOrder(orderId) }
using<UserRepo, EmailService> { userRepo, emailService ->
userRepo.findById(id) shouldNotBe null
}
```
### Reporting
When tests fail, Stove automatically enriches exceptions with a detailed execution report showing exactly what happened:
<details>
<summary><strong>Example Report</strong></summary>
```
╔══════════════════════════════════════════════════════════════════════════════════════════════════╗
║ STOVE TEST EXECUTION REPORT ║
║ ║
║ Test: should create new product when send product create request from api for the allowed ║
║ supplier ║
║ ID: ExampleTest::should create new product when send product create request from api for the ║
║ allowed supplier ║
║ Status: FAILED ║
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ TIMELINE ║
║ ──────── ║
║ ║
║ 12:41:12.371 ✓ PASSED [WireMock] Register stub: GET /suppliers/99/allowed ║
║ Output: kotlin.Unit ║
║ Metadata: {statusCode=200, responseHeaders={}} ║
║ ║
║ 12:41:13.405 ✓ PASSED [HTTP] POST /api/product/create ║
║ Input: ProductCreateRequest(id=1, name=product name, supplierId=99) ║
║ Output: kotlin.Unit ║
║ Metadata: {status=200, headers={}} ║
║ ║
║ 12:41:13.424 ✓ PASSED [Kafka] shouldBePublished<ProductCreatedEvent> ║
║ Output: ProductCreatedEvent(id=1, name=product name, supplierId=99, createdDate=Thu Jan 08 ║
║ 12:41:12 CET 2026, type=ProductCreatedEvent) ║
║ Metadata: {timeout=5s} ║
║ ║
║ 12:41:13.455 ✗ FAILED [Couchbase] Get document ║
║ Input: {id=product:1} ║
║ Error: expected:<100L> but was:<99L> ║
║ ║
╠══════════════════════════════════════════════════════════════════════════════════════════════════╣
║ ║
║ SYSTEM SNAPSHOTS ║
║ ──────────────── ║
║ ║
║ ┌─ HTTP ──────────────────────────────────────────────────────────────────────────────────────── ║
║ ║
║ No detailed state available ║
║ ║
║ ┌─ COUCHBASE ─────────────────────────────────────────────────────────────────────────────────── ║
║ ║
║ No detailed state available ║
║ ║
║ ┌─ KAFKA ─────────────────────────────────────────────────────────────────────────────────────── ║
║ ║
║ Consumed: 0 ║
║ Published: 1 ║
║ Committed: 0 ║
║ ║
║ State Details: ║
║ consumed: 0 item(s) ║
║ published: 1 item(s) ║
║ [0] ║
║ id: 376db940-a367-4419-a628-4754c9466421 ║
║ topic: stove-standalone-example.productCreated.1 ║
║ key: 1 ║
║ headers: {X-EventType=ProductCreatedEvent, X-MessageId=29902970-056d-4ae9-9a84-...} ║
║ message: {"id":1,"name":"product name","supplierId":99,...} ║
║ committed: 0 item(s) ║
║ ║
║ ┌─ WIREMOCK ──────────────────────────────────────────────────────────────────────────────────── ║
║ ║
║ Registered stubs: 0 ║
║ Served requests: 0 (matched: 0) ║
║ Unmatched requests: 0 ║
║ ║
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝
```
</details>
**Features:**
- Timeline of all operations with timestamps and results
- Input/output for each action
- Expected vs actual values on failures
- System snapshots (Kafka messages, WireMock stubs, etc.)
**Test Framework Extensions:**
Use the provided extensions to automatically enrich failures:
```kotlin
// Kotest - register in project config
class StoveConfig : AbstractProjectConfig() {
override val extensions = listOf(StoveKotestExtension())
}
// JUnit 5 - annotate test class
@ExtendWith(StoveJUnitExtension::class)
class MyTest { ... }
```
**Configuration:**
```kotlin
Stove(
StoveOptions(
reportingEnabled = true, // Enable/disable reporting (default: true)
dumpReportOnTestFailure = true, // Enrich failures with report (default: true)
failureRenderer = PrettyConsoleRenderer // Custom renderer (default: PrettyConsoleRenderer)
)
).with { ... }
```
### Tracing
When a test fails, see the **entire execution call chain** inside your application — every controller, service, database query, and Kafka message — powered by OpenTelemetry:
```
EXECUTION TRACE (Call Chain)
═══════════════════════════════════════════════════════════════════
✓ POST (377ms)
✓ POST /api/product/create (361ms)
✓ ProductController.create (141ms)
✓ ProductCreator.create (0ms)
✓ KafkaProducer.send (137ms)
✓ orders.created publish (81ms)
✗ orders.created process (82ms) ← FAILURE POINT
```
**Setup** (two steps):
```kotlin
// 1. In your Stove config
tracing { enableSpanReceiver() }
// 2. In build.gradle.kts
plugins { id("com.trendyol.stove.tracing") version "$stoveVersion" }
stoveTracing { serviceName.set("my-service") }
```
**Validate traces in tests:**
```kotlin
tracing {
shouldContainSpan("OrderService.processOrder")
shouldNotHaveFailedSpans()
executionTimeShouldBeLessThan(500.milliseconds)
}
```
No code changes to your application required. The OpenTelemetry agent instruments 100+ libraries automatically.
### AI Agent Integration
Stove's execution reports and tracing data are structured and deterministic, making them ideal for **AI agent workflows**. When an AI agent runs e2e tests during implementation, it can parse the failure reports — including the full execution trace, system snapshots, and timeline — to understand exactly what went wrong inside the application. This enables agents to iterate on fixes with precise feedback rather than guessing from opaque test failures.
When `stove` is running, it also exposes a local read-only MCP endpoint at `http://localhost:4040/mcp`. Agents can call `stove_failures` first, then drill into a specific `run_id + test_id` for timeline, trace, and snapshot evidence. MCP is optional: if it is unavailable or incomplete, agents should fall back to normal test output, Stove failure reports, and logs.
**Agent Skills:** Stove ships with a ready-to-use [Claude Code skill](https://github.com/Trendyol/stove/tree/main/.claude/skills/stove) that teaches AI agents how to set up and write Stove e2e tests. Copy the `.claude/skills/stove/` directory into your project's `.claude/skills/` folder, and your AI coding agent will know how to configure systems, write tests, enable tracing, and build custom systems — following all Stove conventions automatically.
## Configuration
### Framework Setup
<table>
<tr><th>Spring Boot</th><th>Ktor</th></tr>
<tr>
<td>
```kotlin
springBoot(
runner = { params ->
myApp.run(params) {
addTestDependencies()
}
}
)
```
</td>
<td>
```kotlin
ktor(
runner = { params ->
run(params, shouldWait = false)
}
)
```
</td>
</tr>
<tr><th>Micronaut</th><th>Quarkus</th></tr>
<tr>
<td>
```kotlin
micronaut(
runner = { params ->
myApp.run(params)
}
)
```
</td>
<td>
```kotlin
quarkus(
runner = { params ->
MyApp.main(params)
}
)
```
</td>
</tr>
</table>
### Container Reuse
Speed up local development by keeping containers running between test runs:
```kotlin
Stove { keepDependenciesRunning() }.with { ... }
```
### Cleanup
Run cleanup logic after tests complete:
```kotlin
postgresql {
PostgresqlOptions(cleanup = { it.execute("TRUNCATE users") }, ...)
}
kafka {
KafkaSystemOptions(cleanup = { it.deleteTopics(listOf("test-topic")) }, ...)
}
```
Available for Kafka, PostgreSQL, MySQL, MongoDB, Couchbase, Cassandra, MSSQL, Elasticsearch, Redis.
### Migrations
Run database migrations before tests start:
```kotlin
postgresql {
PostgresqlOptions(...)
.migrations {
register<CreateUsersTable>()
register<CreateOrdersTable>()
}
}
```
Available for Kafka, PostgreSQL, MySQL, MongoDB, Couchbase, Cassandra, MSSQL, Elasticsearch, Redis.
### Provided Instances
Connect to existing infrastructure instead of starting containers (useful for CI/CD):
```kotlin
postgresql { PostgresqlOptions.provided(jdbcUrl = "jdbc:postgresql://ci-db:5432/test", ...) }
kafka { KafkaSystemOptions.provided(bootstrapServers = "ci-kafka:9092", ...) }
```
> **Tip:** When using provided instances, use migrations to create isolated test schemas and cleanups to remove test
> data afterwards. This ensures test isolation on shared infrastructure.
<strong>Complete Example</strong>
```kotlin
test("should create order with payment processing") {
stove {
val userId = UUID.randomUUID().toString()
val productId = UUID.randomUUID().toString()
// 1. Seed database
postgresql {
shouldExecute("INSERT INTO users (id, name) VALUES ('$userId', 'John')")
shouldExecute("INSERT INTO products (id, price, stock) VALUES ('$productId', 99.99, 10)")
}
// 2. Mock external payment API
wiremock {
mockPost(
"/payments/charge", statusCode = 200,
responseBody = PaymentResult(success = true).some()
)
}
// 3. Call API
http {
postAndExpectBody<OrderResponse>(
"/orders",
body = CreateOrderRequest(userId, productId).some()
) {
it.status shouldBe 201
}
}
// 4. Verify database
postgresql {
shouldQuery<Order>("SELECT * FROM orders WHERE user_id = '$userId'", mapper = { row ->
Order(row.string("status"))
}) {
it.first().status shouldBe "CONFIRMED"
}
}
// 5. Verify event published
kafka {
shouldBePublished<OrderCreatedEvent> {
actual.userId == userId
}
}
// 6. Verify via application service
using<InventoryService> { getStock(productId) shouldBe 9 }
}
}
```
## Reference
### Supported Components
| Category | Components |
|------------|-------------------------------------------------------------|
| Databases | PostgreSQL, MySQL, MongoDB, Couchbase, Cassandra, MSSQL, Elasticsearch, Redis |
| Messaging | Kafka |
| HTTP | Built-in client, WebSockets, WireMock |
| gRPC | Client (grpc-kotlin), Mock Server (native) |
| Frameworks | Spring Boot, Ktor, Micronaut, Quarkus |
### Feature Matrix
| Component | Migrations | Cleanup | Provided Instance | Pause/Unpause |
|---------------|:----------:|:-------:|:-----------------:|:-------------:|
| PostgreSQL | ✅ | ✅ | ✅ | ✅ |
| MySQL | ✅ | ✅ | ✅ | ✅ |
| MSSQL | ✅ | ✅ | ✅ | ✅ |
| MongoDB | ✅ | ✅ | ✅ | ✅ |
| Couchbase | ✅ | ✅ | ✅ | ✅ |
| Cassandra | ✅ | ✅ | ✅ | ✅ |
| Elasticsearch | ✅ | ✅ | ✅ | ✅ |
| Redis | ✅ | ✅ | ✅ | ✅ |
| Kafka | ✅ | ✅ | ✅ | ✅ |
| WireMock | n/a | n/a | n/a | n/a |
| HTTP Client | n/a | n/a | n/a | n/a |
| gRPC Mock | n/a | n/a | n/a | n/a |
<details>
<summary><strong>FAQ</strong></summary>
**Can I use Stove with Java applications?**
Yes. Your application can be Java, Scala, or any JVM language. Tests are written in Kotlin for the DSL.
**Does Stove replace Testcontainers?**
No. Stove uses Testcontainers underneath and adds the unified DSL on top.
**How slow is the first run?**
First run pulls Docker images (~1-2 min). Use `keepDependenciesRunning()` for instant subsequent runs.
**Can I run tests in parallel?**
Yes, with unique test data per test.
See [provided instances docs](https://trendyol.github.io/stove/Components/11-provided-instances/).
</details>
## Resources
- **[Documentation](https://trendyol.github.io/stove/)**: Full guides and API reference
- **[Examples](https://github.com/Trendyol/stove/tree/main/examples)**: Working sample projects
- **[AI Agent Skill](https://github.com/Trendyol/stove/tree/main/.claude/skills/stove)**: Drop into `.claude/skills/` to teach AI agents Stove conventions
- **[Blog Post](https://medium.com/trendyol-tech/a-new-approach-to-the-api-end-to-end-testing-in-kotlin-f743fd1901f5)**:
Motivation and design decisions
- **[Video Walkthrough](https://youtu.be/DJ0CI5cBanc?t=669)**: Live demo (Turkish)
## Community
**Used by:**
1. [Trendyol](https://www.trendyol.com): Leading e-commerce platform, Turkey
*Using Stove? Open a PR to add your company.*
**Contributions:** [Issues](https://github.com/Trendyol/stove/issues) and PRs welcome
**License:** Apache 2.0
> **Note:** Production-ready and used at scale. API still evolving; breaking changes possible in minor releases with
> migration guides.
================================================
FILE: api/stove.api
================================================
================================================
FILE: build.gradle.kts
================================================
import org.gradle.plugins.ide.idea.model.IdeaModel
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm").version(libs.versions.kotlin)
alias(libs.plugins.spotless)
alias(libs.plugins.testLogger)
alias(libs.plugins.kover)
alias(libs.plugins.detekt)
alias(libs.plugins.binaryCompatibilityValidator)
alias(libs.plugins.maven.publish)
idea
java
}
group = "com.trendyol"
version = CI.version(project)
apiValidation {
ignoredProjects += listOf(
"ktor-example",
"micronaut-example",
"spring-example",
"spring-4x-example",
"spring-standalone-example",
"spring-streams-example",
"tests",
"spring-test-fixtures",
"spring-2x-kafka-tests",
"spring-3x-kafka-tests",
"spring-4x-kafka-tests",
"spring-4x-tests",
"spring-3x-tests",
"spring-2x-tests",
"quarkus-example",
"ktor-di-tests",
"ktor-koin-tests",
"ktor-test-fixtures",
"stove-tracing-gradle-plugin",
"stove-dashboard-api",
"stove-dashboard",
)
}
kover {
reports {
filters {
excludes {
classes(
"com.trendyol.stove.functional.*",
"com.trendyol.stove.system.abstractions.*",
"com.trendyol.stove.system.annotations.*",
"com.trendyol.stove.serialization.*",
"stove.spring.example.*",
"stove.spring.standalone.example.*",
"stove.spring.streams.example.*",
"stove.ktor.example.*",
"stove.quarkus.example.*",
"stove.micronaut.example.*",
)
}
}
}
}
v
gitextract_mbe2tl_w/
├── .claude/
│ └── skills/
│ └── stove/
│ ├── SKILL.md
│ ├── container.md
│ ├── custom-systems.md
│ ├── go-setup.md
│ ├── gradle-config.md
│ ├── mcp.md
│ ├── other-languages.md
│ ├── system-setup.md
│ ├── tracing.md
│ └── writing-tests.md
├── .editorconfig
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── build-jvm-recipes.yml
│ ├── build-process-recipes.yml
│ ├── build.yml
│ ├── gradle-publish-release.yml
│ ├── gradle-publish-snapshot.yml
│ ├── publish-to-ghpages.yml
│ ├── scorecard.yml
│ ├── stove-cli-ci.yml
│ └── stove-cli-release.yml
├── .gitignore
├── LICENSE
├── README.md
├── api/
│ └── stove.api
├── build.gradle.kts
├── buildSrc/
│ ├── build.gradle.kts
│ ├── settings.gradle.kts
│ └── src/
│ └── main/
│ └── kotlin/
│ ├── BuildConstants.kt
│ ├── CI.kt
│ ├── GenerateDashboardVersionSourceTask.kt
│ ├── Helpers.kt
│ ├── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── gradle/
│ │ └── StoveTracingConfiguration.kt
│ └── stove-publishing.gradle.kts
├── codecov.yml
├── detekt.yml
├── docs/
│ ├── Components/
│ │ ├── 01-couchbase.md
│ │ ├── 02-kafka.md
│ │ ├── 03-elasticsearch.md
│ │ ├── 04-wiremock.md
│ │ ├── 05-http.md
│ │ ├── 06-postgresql.md
│ │ ├── 07-mongodb.md
│ │ ├── 08-mssql.md
│ │ ├── 09-redis.md
│ │ ├── 10-bridge.md
│ │ ├── 11-provided-instances.md
│ │ ├── 12-grpc.md
│ │ ├── 13-reporting.md
│ │ ├── 14-grpc-mock.md
│ │ ├── 15-tracing.md
│ │ ├── 16-mysql.md
│ │ ├── 17-cassandra.md
│ │ ├── 18-dashboard.md
│ │ ├── 19-provided-application.md
│ │ ├── 20-multiple-systems.md
│ │ ├── 21-mcp.md
│ │ ├── 22-container.md
│ │ └── index.md
│ ├── assets/
│ │ ├── rough-notation.iife.js
│ │ └── stove_dashboard.webm
│ ├── best-practices.md
│ ├── blog/
│ │ ├── dashboard-0.23.0.md
│ │ ├── polyglot-0.24.0.md
│ │ └── tracing-0.21.0.md
│ ├── css/
│ │ └── custom.css
│ ├── frameworks/
│ │ ├── index.md
│ │ ├── ktor.md
│ │ ├── micronaut.md
│ │ ├── quarkus.md
│ │ └── spring-boot.md
│ ├── getting-started.md
│ ├── index.md
│ ├── js/
│ │ └── rough-notation-mkdocs.js
│ ├── other-languages/
│ │ ├── go-container.md
│ │ ├── go-process.md
│ │ ├── go.md
│ │ └── index.md
│ ├── release-notes/
│ │ ├── 0.15.0.md
│ │ ├── 0.19.0.md
│ │ ├── 0.20.0.md
│ │ ├── 0.21.0.md
│ │ ├── 0.21.2.md
│ │ ├── 0.22.2.md
│ │ ├── 0.23.0.md
│ │ └── 0.24.0.md
│ ├── troubleshooting.md
│ └── writing-custom-systems.md
├── examples/
│ ├── build.gradle.kts
│ ├── ktor-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── ktor/
│ │ │ │ └── example/
│ │ │ │ ├── Application.kt
│ │ │ │ ├── app/
│ │ │ │ │ ├── app.kt
│ │ │ │ │ ├── configuration.kt
│ │ │ │ │ ├── database.kt
│ │ │ │ │ ├── kafka.kt
│ │ │ │ │ └── routing.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── ExampleAppConsumer.kt
│ │ │ │ │ ├── LockProvider.kt
│ │ │ │ │ ├── ProductService.kt
│ │ │ │ │ └── UpdateProductRequest.kt
│ │ │ │ ├── domain/
│ │ │ │ │ ├── Product.kt
│ │ │ │ │ └── ProductRepository.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── FeatureToggleClient.kt
│ │ │ │ └── PricingClient.kt
│ │ │ ├── proto/
│ │ │ │ ├── feature_toggle.proto
│ │ │ │ └── pricing.proto
│ │ │ └── resources/
│ │ │ ├── application.yaml
│ │ │ └── logback.xml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── ktor/
│ │ │ └── example/
│ │ │ └── e2e/
│ │ │ ├── ExampleTest.kt
│ │ │ ├── StoveConfig.kt
│ │ │ └── TestStub.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── micronaut-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── micronaut/
│ │ │ │ └── example/
│ │ │ │ ├── Application.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── domain/
│ │ │ │ │ │ └── Product.kt
│ │ │ │ │ ├── repository/
│ │ │ │ │ │ └── ProductRepository.kt
│ │ │ │ │ └── services/
│ │ │ │ │ ├── ProductService.kt
│ │ │ │ │ └── SupplierService.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── ObjectMapperConfig.kt
│ │ │ │ ├── api/
│ │ │ │ │ ├── ProductController.kt
│ │ │ │ │ └── model/
│ │ │ │ │ └── request/
│ │ │ │ │ └── CreateProductRequest.kt
│ │ │ │ ├── http/
│ │ │ │ │ └── SupplierHttpService.kt
│ │ │ │ ├── persistence/
│ │ │ │ │ └── ProductJdbcRepository.kt
│ │ │ │ └── postgres/
│ │ │ │ └── PostgresConfiguration.kt
│ │ │ └── resources/
│ │ │ ├── application.yml
│ │ │ └── logback.xml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── micronaut/
│ │ │ └── example/
│ │ │ └── e2e/
│ │ │ ├── CreateProductsTableMigration.kt
│ │ │ ├── ProductControllerTest.kt
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── quarkus-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── quarkus/
│ │ │ │ └── example/
│ │ │ │ ├── QuarkusMainApp.kt
│ │ │ │ ├── StoveStartupSignal.kt
│ │ │ │ ├── api/
│ │ │ │ │ └── ProductResource.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── Models.kt
│ │ │ │ │ └── ProductCreator.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── http/
│ │ │ │ │ └── SupplierHttpService.kt
│ │ │ │ ├── kafka/
│ │ │ │ │ ├── CustomProducerInterceptor.kt
│ │ │ │ │ ├── KafkaClientConfiguration.kt
│ │ │ │ │ ├── KafkaSerde.kt
│ │ │ │ │ ├── ProductCommandConsumer.kt
│ │ │ │ │ └── ProductEventPublisher.kt
│ │ │ │ └── postgres/
│ │ │ │ └── ProductRepository.kt
│ │ │ └── resources/
│ │ │ ├── application.properties
│ │ │ └── db/
│ │ │ └── migration/
│ │ │ └── V1__create_products.sql
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── quarkus/
│ │ │ └── example/
│ │ │ └── e2e/
│ │ │ ├── ExampleTest.kt
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-4x-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── spring/
│ │ │ │ └── example4x/
│ │ │ │ ├── ExampleApp.kt
│ │ │ │ ├── application/
│ │ │ │ │ └── handlers/
│ │ │ │ │ └── ProductCreator.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── api/
│ │ │ │ │ └── ProductController.kt
│ │ │ │ └── messaging/
│ │ │ │ └── kafka/
│ │ │ │ ├── KafkaConfiguration.kt
│ │ │ │ ├── KafkaProducer.kt
│ │ │ │ └── ProductCreateConsumer.kt
│ │ │ └── resources/
│ │ │ └── application.yml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── spring/
│ │ │ └── example4x/
│ │ │ └── e2e/
│ │ │ ├── ExampleTest.kt
│ │ │ ├── StoveConfig.kt
│ │ │ └── jackson3.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── spring/
│ │ │ │ └── example/
│ │ │ │ ├── ExampleApp.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── handlers/
│ │ │ │ │ │ └── ProductCreator.kt
│ │ │ │ │ └── services/
│ │ │ │ │ └── SupplierService.kt
│ │ │ │ ├── domain/
│ │ │ │ │ └── ProductTable.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── Constants.kt
│ │ │ │ ├── ObjectMapperConfig.kt
│ │ │ │ ├── api/
│ │ │ │ │ └── ProductController.kt
│ │ │ │ ├── http/
│ │ │ │ │ ├── SupplierHttpService.kt
│ │ │ │ │ ├── WebClientConfiguration.kt
│ │ │ │ │ └── WebClientConfigurationProperties.kt
│ │ │ │ ├── messaging/
│ │ │ │ │ └── kafka/
│ │ │ │ │ ├── KafkaProducer.kt
│ │ │ │ │ ├── configuration/
│ │ │ │ │ │ ├── ConsumerSettings.kt
│ │ │ │ │ │ ├── KafkaConsumerConfiguration.kt
│ │ │ │ │ │ ├── KafkaProducerConfiguration.kt
│ │ │ │ │ │ ├── KafkaProperties.kt
│ │ │ │ │ │ ├── MapBasedSettings.kt
│ │ │ │ │ │ └── ProducerSettings.kt
│ │ │ │ │ ├── consumers/
│ │ │ │ │ │ ├── FailingProductCreateConsumer.kt
│ │ │ │ │ │ ├── JobTopicConfig.kt
│ │ │ │ │ │ └── ProductCreateConsumers.kt
│ │ │ │ │ └── interceptors/
│ │ │ │ │ ├── CustomConsumerInterceptor.kt
│ │ │ │ │ └── CustomProducerInterceptor.kt
│ │ │ │ └── postgres/
│ │ │ │ └── ExposedConfiguration.kt
│ │ │ └── resources/
│ │ │ └── application.yml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── spring/
│ │ │ └── example/
│ │ │ └── e2e/
│ │ │ ├── CreateProductsTableMigration.kt
│ │ │ ├── ExampleTest.kt
│ │ │ ├── StoveConfig.kt
│ │ │ ├── TestSystemInitializer.kt
│ │ │ ├── TracingValidationTest.kt
│ │ │ └── hierarchy/
│ │ │ ├── BehaviorSpecHierarchyTest.kt
│ │ │ ├── DescribeSpecHierarchyTest.kt
│ │ │ ├── FunSpecContextHierarchyTest.kt
│ │ │ ├── NestedJunitHierarchyTest.kt
│ │ │ └── StringSpecHierarchyTest.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-standalone-example/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── stove/
│ │ │ │ └── spring/
│ │ │ │ └── standalone/
│ │ │ │ └── example/
│ │ │ │ ├── ExampleApp.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── handlers/
│ │ │ │ │ │ └── ProductCreator.kt
│ │ │ │ │ └── services/
│ │ │ │ │ └── SupplierService.kt
│ │ │ │ ├── domain/
│ │ │ │ │ └── ProductTable.kt
│ │ │ │ └── infrastructure/
│ │ │ │ ├── Constants.kt
│ │ │ │ ├── ObjectMapperConfig.kt
│ │ │ │ ├── api/
│ │ │ │ │ └── ProductController.kt
│ │ │ │ ├── http/
│ │ │ │ │ ├── SupplierHttpService.kt
│ │ │ │ │ ├── WebClientConfiguration.kt
│ │ │ │ │ └── WebClientConfigurationProperties.kt
│ │ │ │ ├── messaging/
│ │ │ │ │ └── kafka/
│ │ │ │ │ ├── KafkaProducer.kt
│ │ │ │ │ ├── configuration/
│ │ │ │ │ │ ├── ConsumerSettings.kt
│ │ │ │ │ │ ├── KafkaConsumerConfiguration.kt
│ │ │ │ │ │ ├── KafkaProducerConfiguration.kt
│ │ │ │ │ │ ├── KafkaProperties.kt
│ │ │ │ │ │ ├── MapBasedSettings.kt
│ │ │ │ │ │ └── ProducerSettings.kt
│ │ │ │ │ ├── consumers/
│ │ │ │ │ │ ├── FailingProductCreateConsumer.kt
│ │ │ │ │ │ ├── JobTopicConfig.kt
│ │ │ │ │ │ └── ProductCreateConsumers.kt
│ │ │ │ │ └── interceptors/
│ │ │ │ │ ├── CustomConsumerInterceptor.kt
│ │ │ │ │ └── CustomProducerInterceptor.kt
│ │ │ │ └── postgres/
│ │ │ │ └── ExposedConfiguration.kt
│ │ │ └── resources/
│ │ │ └── application.yml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── stove/
│ │ │ └── spring/
│ │ │ └── standalone/
│ │ │ └── example/
│ │ │ └── e2e/
│ │ │ ├── CreateProductsTableMigration.kt
│ │ │ ├── ExampleTest.kt
│ │ │ ├── ReportingIntegrationTest.kt
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ └── spring-streams-example/
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── kotlin/
│ │ │ └── stove/
│ │ │ └── spring/
│ │ │ └── streams/
│ │ │ └── example/
│ │ │ ├── ExampleApp.kt
│ │ │ └── kafka/
│ │ │ ├── CustomDeserializationExceptionHandler.kt
│ │ │ ├── CustomProductionExceptionHandler.kt
│ │ │ ├── CustomSerDe.kt
│ │ │ ├── StreamsConfig.kt
│ │ │ └── application/
│ │ │ └── processor/
│ │ │ └── ExampleJoin.kt
│ │ ├── proto/
│ │ │ ├── Input1-value.proto
│ │ │ ├── Input2-value.proto
│ │ │ └── Output-value.proto
│ │ └── resources/
│ │ └── application.properties
│ └── test/
│ ├── kotlin/
│ │ └── com/
│ │ └── stove/
│ │ └── spring/
│ │ └── streams/
│ │ └── example/
│ │ └── e2e/
│ │ ├── ExampleTest.kt
│ │ ├── StoveConfig.kt
│ │ └── TestHelper.kt
│ └── resources/
│ ├── kotest.properties
│ └── logback-test.xml
├── go/
│ └── stove-kafka/
│ ├── bridge.go
│ ├── bridge_test.go
│ ├── franz/
│ │ ├── hooks.go
│ │ └── hooks_test.go
│ ├── go.mod
│ ├── go.sum
│ ├── sarama/
│ │ ├── interceptors.go
│ │ └── interceptors_test.go
│ ├── segmentio/
│ │ ├── bridge.go
│ │ └── bridge_test.go
│ └── stoveobserver/
│ ├── messages.pb.go
│ └── messages_grpc.pb.go
├── gradle/
│ ├── libs.versions.toml
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── jreleaser.yml
├── lib/
│ ├── stove/
│ │ ├── api/
│ │ │ └── stove.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ ├── containers/
│ │ │ │ ├── ContainerOptions.kt
│ │ │ │ ├── ProvidedRegistry.kt
│ │ │ │ └── StoveContainer.kt
│ │ │ ├── database/
│ │ │ │ └── migrations/
│ │ │ │ ├── DatabaseMigration.kt
│ │ │ │ ├── MigrationCollection.kt
│ │ │ │ └── SupportsMigrations.kt
│ │ │ ├── functional/
│ │ │ │ ├── Extensions.kt
│ │ │ │ ├── Reflect.kt
│ │ │ │ └── Try.kt
│ │ │ ├── http/
│ │ │ │ └── StoveHttpResponse.kt
│ │ │ ├── messaging/
│ │ │ │ └── Observation.kt
│ │ │ ├── reporting/
│ │ │ │ ├── JsonReportRenderer.kt
│ │ │ │ ├── PrettyConsoleRenderer.kt
│ │ │ │ ├── ReportEntry.kt
│ │ │ │ ├── ReportEventListener.kt
│ │ │ │ ├── ReportRenderer.kt
│ │ │ │ ├── Reports.kt
│ │ │ │ ├── SpanEventListener.kt
│ │ │ │ ├── SpanListenerRegistry.kt
│ │ │ │ ├── StoveReporter.kt
│ │ │ │ ├── StoveTestContext.kt
│ │ │ │ ├── StoveTestContextHolder.kt
│ │ │ │ ├── StoveTestExceptions.kt
│ │ │ │ ├── SystemSnapshot.kt
│ │ │ │ ├── TestReport.kt
│ │ │ │ └── TraceProvider.kt
│ │ │ ├── serialization/
│ │ │ │ ├── gson.kt
│ │ │ │ ├── jackson.kt
│ │ │ │ ├── kotlinx.kt
│ │ │ │ └── serialization.kt
│ │ │ ├── system/
│ │ │ │ ├── BridgeSystem.kt
│ │ │ │ ├── PortFinder.kt
│ │ │ │ ├── PropertiesFile.kt
│ │ │ │ ├── ProvidedApplicationUnderTest.kt
│ │ │ │ ├── ReadinessChecker.kt
│ │ │ │ ├── ReadinessStrategy.kt
│ │ │ │ ├── Runner.kt
│ │ │ │ ├── Stove.kt
│ │ │ │ ├── StoveOptions.kt
│ │ │ │ ├── StoveOptionsDsl.kt
│ │ │ │ ├── ValidationDsl.kt
│ │ │ │ ├── WithDsl.kt
│ │ │ │ ├── abstractions/
│ │ │ │ │ ├── ApplicationUnderTest.kt
│ │ │ │ │ ├── Exceptions.kt
│ │ │ │ │ ├── ExposesConfiguration.kt
│ │ │ │ │ ├── PluggedSystem.kt
│ │ │ │ │ ├── ReadyStove.kt
│ │ │ │ │ ├── RunnableSystemWithContext.kt
│ │ │ │ │ ├── StateStorage.kt
│ │ │ │ │ ├── SystemKey.kt
│ │ │ │ │ ├── SystemOptions.kt
│ │ │ │ │ ├── SystemRuntime.kt
│ │ │ │ │ ├── ThenSystemContinuation.kt
│ │ │ │ │ └── ValidatedSystem.kt
│ │ │ │ ├── annotations/
│ │ │ │ │ └── StoveDsl.kt
│ │ │ │ └── application/
│ │ │ │ ├── ApplicationConfigurations.kt
│ │ │ │ ├── ArgsProvider.kt
│ │ │ │ └── EnvProvider.kt
│ │ │ └── tracing/
│ │ │ ├── SpanInfo.kt
│ │ │ ├── SpanTree.kt
│ │ │ ├── TraceContext.kt
│ │ │ ├── TraceTreeRenderer.kt
│ │ │ └── TraceVisualization.kt
│ │ ├── test/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ ├── containers/
│ │ │ │ │ ├── ContainerOptionsTest.kt
│ │ │ │ │ ├── ProvidedRegistryTest.kt
│ │ │ │ │ └── StoveContainerTest.kt
│ │ │ │ ├── database/
│ │ │ │ │ └── migrations/
│ │ │ │ │ ├── MigrationCollectionTest.kt
│ │ │ │ │ └── SupportsMigrationsTest.kt
│ │ │ │ ├── http/
│ │ │ │ │ └── StoveHttpResponseTest.kt
│ │ │ │ ├── messaging/
│ │ │ │ │ └── ObservationTest.kt
│ │ │ │ ├── reporting/
│ │ │ │ │ ├── JsonReportRendererTest.kt
│ │ │ │ │ ├── PrettyConsoleRendererTest.kt
│ │ │ │ │ ├── ReportEntryTest.kt
│ │ │ │ │ ├── ReportEventListenerTest.kt
│ │ │ │ │ ├── ReportsTest.kt
│ │ │ │ │ ├── StoveReporterTest.kt
│ │ │ │ │ ├── StoveTestContextTest.kt
│ │ │ │ │ ├── StoveTestExceptionsTest.kt
│ │ │ │ │ ├── SystemSnapshotTest.kt
│ │ │ │ │ ├── TestReportTest.kt
│ │ │ │ │ └── TraceProviderTest.kt
│ │ │ │ ├── serialization/
│ │ │ │ │ └── SerializationTests.kt
│ │ │ │ ├── system/
│ │ │ │ │ ├── BridgeSystemGuardTest.kt
│ │ │ │ │ ├── BridgeSystemTest.kt
│ │ │ │ │ ├── KeyedSystemTest.kt
│ │ │ │ │ ├── PortFinderTest.kt
│ │ │ │ │ ├── ProvidedApplicationUnderTestTest.kt
│ │ │ │ │ ├── ReadinessCheckerTest.kt
│ │ │ │ │ ├── StoveOptionsDslTest.kt
│ │ │ │ │ ├── StoveTest.kt
│ │ │ │ │ ├── ValidationDslTest.kt
│ │ │ │ │ └── abstractions/
│ │ │ │ │ ├── ProvidedSystemOptionsTest.kt
│ │ │ │ │ ├── StateStorageKeyTest.kt
│ │ │ │ │ └── SystemKeyTest.kt
│ │ │ │ └── tracing/
│ │ │ │ ├── SpanInfoTest.kt
│ │ │ │ ├── SpanTreeTest.kt
│ │ │ │ ├── TraceContextTest.kt
│ │ │ │ ├── TraceTreeRendererTest.kt
│ │ │ │ └── TraceVisualizationTest.kt
│ │ │ └── resources/
│ │ │ └── logback.xml
│ │ └── testFixtures/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── CapturedOutput.kt
│ ├── stove-bom/
│ │ └── build.gradle.kts
│ ├── stove-cassandra/
│ │ ├── api/
│ │ │ └── stove-cassandra.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── cassandra/
│ │ │ ├── CassandraDsl.kt
│ │ │ ├── CassandraSystem.kt
│ │ │ ├── CassandraSystemOptions.kt
│ │ │ └── Options.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── cassandra/
│ │ │ ├── CassandraOptionsTests.kt
│ │ │ └── CassandraSystemTests.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback.xml
│ ├── stove-couchbase/
│ │ ├── api/
│ │ │ └── stove-couchbase.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── couchbase/
│ │ │ ├── CouchbaseDsl.kt
│ │ │ ├── CouchbaseSystem.kt
│ │ │ ├── Options.kt
│ │ │ ├── StoveCouchbaseContainer.kt
│ │ │ └── util.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── couchbase/
│ │ │ ├── CouchbaseOptionsTest.kt
│ │ │ ├── CouchbaseTestSystemTests.kt
│ │ │ └── TestSystemConfig.kt
│ │ └── resources/
│ │ └── kotest.properties
│ ├── stove-dashboard/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── dashboard/
│ │ │ ├── DashboardDsl.kt
│ │ │ ├── DashboardEmitter.kt
│ │ │ ├── DashboardOptions.kt
│ │ │ └── DashboardSystem.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── dashboard/
│ │ ├── DashboardEmitterTest.kt
│ │ └── DashboardSystemTest.kt
│ ├── stove-dashboard-api/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── main/
│ │ └── proto/
│ │ └── stove/
│ │ └── dashboard/
│ │ └── v1/
│ │ ├── dashboard_events.proto
│ │ └── dashboard_service.proto
│ ├── stove-elasticsearch/
│ │ ├── api/
│ │ │ └── stove-elasticsearch.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── elasticsearch/
│ │ │ ├── ElasticsearchExposedCertificate.kt
│ │ │ ├── ElasticsearchSystem.kt
│ │ │ ├── Extensions.kt
│ │ │ ├── Options.kt
│ │ │ └── util.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── elasticsearch/
│ │ │ ├── ElasticsearchExposedCertificateTest.kt
│ │ │ ├── ElasticsearchOptionsTest.kt
│ │ │ └── ElasticsearchTestSystemTests.kt
│ │ └── resources/
│ │ └── kotest.properties
│ ├── stove-grpc/
│ │ ├── api/
│ │ │ └── stove-grpc.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── grpc/
│ │ │ ├── GrpcDsl.kt
│ │ │ ├── GrpcSystem.kt
│ │ │ └── GrpcSystemOptions.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── grpc/
│ │ │ ├── GrpcAuthInterceptorTest.kt
│ │ │ ├── GrpcSystemStubTest.kt
│ │ │ ├── GrpcSystemWireTest.kt
│ │ │ ├── StoveConfig.kt
│ │ │ └── TestGrpcServer.kt
│ │ ├── proto/
│ │ │ └── test_service.proto
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── stove-grpc-mock/
│ │ ├── api/
│ │ │ └── stove-grpc-mock.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── testing/
│ │ │ └── grpcmock/
│ │ │ ├── GrpcMockDsl.kt
│ │ │ ├── GrpcMockSystem.kt
│ │ │ ├── GrpcMockSystemOptions.kt
│ │ │ └── StubDefinition.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── testing/
│ │ │ └── grpcmock/
│ │ │ ├── GrpcMockSystemTest.kt
│ │ │ └── StoveConfig.kt
│ │ ├── proto/
│ │ │ └── test_service.proto
│ │ └── resources/
│ │ └── kotest.properties
│ ├── stove-http/
│ │ ├── api/
│ │ │ └── stove-http.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── http/
│ │ │ ├── HttpClientFactory.kt
│ │ │ ├── HttpDsl.kt
│ │ │ ├── HttpSystem.kt
│ │ │ ├── StoveMultiPartContent.kt
│ │ │ ├── streaming.kt
│ │ │ └── websocket.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── http/
│ │ │ ├── HttpSystemTests.kt
│ │ │ └── WebSocketTests.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── stove-kafka/
│ │ ├── api/
│ │ │ └── stove-kafka.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── kafka/
│ │ │ │ ├── Caching.kt
│ │ │ │ ├── Extensions.kt
│ │ │ │ ├── KafkaContainerOptions.kt
│ │ │ │ ├── KafkaContext.kt
│ │ │ │ ├── KafkaExposedConfiguration.kt
│ │ │ │ ├── KafkaSystem.kt
│ │ │ │ ├── KafkaSystemOptions.kt
│ │ │ │ ├── SerDe.kt
│ │ │ │ ├── coroutines.kt
│ │ │ │ ├── intercepting/
│ │ │ │ │ ├── CommonOps.kt
│ │ │ │ │ ├── GrpcUtils.kt
│ │ │ │ │ ├── MessageSinkOps.kt
│ │ │ │ │ ├── MessageSinkPublishOps.kt
│ │ │ │ │ ├── MessageStore.kt
│ │ │ │ │ ├── StoveKafkaBridge.kt
│ │ │ │ │ ├── StoveKafkaObserverGrpcServer.kt
│ │ │ │ │ └── StoveMessageSink.kt
│ │ │ │ └── messages.kt
│ │ │ └── proto/
│ │ │ └── messages.proto
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── kafka/
│ │ │ ├── setup/
│ │ │ │ ├── TestSystemConfig.kt
│ │ │ │ └── example/
│ │ │ │ ├── DomainEvents.kt
│ │ │ │ ├── KafkaTestShared.kt
│ │ │ │ ├── StoveListener.kt
│ │ │ │ └── consumers/
│ │ │ │ ├── ProductConsumer.kt
│ │ │ │ └── ProductFailingConsumer.kt
│ │ │ └── tests/
│ │ │ ├── CoroutineExecutorServiceTests.kt
│ │ │ ├── KafkaOptionsTests.kt
│ │ │ ├── KafkaSystemTests.kt
│ │ │ ├── MessageStoreTests.kt
│ │ │ └── TopicSuffixesTests.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── stove-mongodb/
│ │ ├── api/
│ │ │ └── stove-mongodb.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mongodb/
│ │ │ ├── MongoDsl.kt
│ │ │ ├── MongodbSystem.kt
│ │ │ ├── MongodbSystemOptions.kt
│ │ │ ├── ObjectIdJsonOperations.kt
│ │ │ └── Options.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mongodb/
│ │ │ ├── MongodbOptionsTests.kt
│ │ │ ├── MongodbTestSystemTests.kt
│ │ │ └── ObjectIdJsonOperationsTest.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── stove-mssql/
│ │ ├── api/
│ │ │ └── stove-mssql.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mssql/
│ │ │ ├── MsSqlOptions.kt
│ │ │ └── MsSqlSystem.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mssql/
│ │ │ ├── MsSqlOptionsTest.kt
│ │ │ └── MssqlSystemTest.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── stove-mysql/
│ │ ├── api/
│ │ │ └── stove-mysql.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mysql/
│ │ │ ├── MySqlSystem.kt
│ │ │ └── Options.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── mysql/
│ │ │ ├── MySqlOptionsTest.kt
│ │ │ ├── MySqlSystemTests.kt
│ │ │ └── TestSystemConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback.xml
│ ├── stove-postgres/
│ │ ├── api/
│ │ │ └── stove-postgres.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── postgres/
│ │ │ ├── Options.kt
│ │ │ └── PostgresqlSystem.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── postgres/
│ │ │ ├── PostgresqlOptionsTest.kt
│ │ │ ├── PostgresqlSystemTest.kt
│ │ │ └── TestSystemConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback.xml
│ ├── stove-rdbms/
│ │ ├── api/
│ │ │ └── stove-rdbms.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── rdbms/
│ │ │ ├── NativeSqlOperations.kt
│ │ │ ├── RelationalDatabaseContext.kt
│ │ │ ├── RelationalDatabaseExposedConfiguration.kt
│ │ │ └── RelationalDatabaseSystem.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── rdbms/
│ │ ├── RelationalDatabaseContextTest.kt
│ │ └── RelationalDatabaseSystemTest.kt
│ ├── stove-redis/
│ │ ├── api/
│ │ │ └── stove-redis.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── redis/
│ │ │ ├── RedisOptions.kt
│ │ │ └── RedisSystem.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── redis/
│ │ │ ├── RedisOptionsTest.kt
│ │ │ └── RedisSystemTests.kt
│ │ └── resources/
│ │ └── kotest.properties
│ ├── stove-tracing/
│ │ ├── api/
│ │ │ └── stove-tracing.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── tracing/
│ │ │ ├── Constants.kt
│ │ │ ├── OTLPSpanReceiver.kt
│ │ │ ├── StoveTraceCollector.kt
│ │ │ ├── TraceReportBuilder.kt
│ │ │ ├── TraceValidation.kt
│ │ │ ├── TracingOptions.kt
│ │ │ └── TracingSystem.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── tracing/
│ │ ├── OtlpSpanReceiverTest.kt
│ │ ├── SpanEventListenerTest.kt
│ │ ├── SpanInfoTest.kt
│ │ ├── SpanTreeTest.kt
│ │ ├── StoveTraceCollectorTest.kt
│ │ ├── TraceReportBuilderTest.kt
│ │ ├── TraceTreeRendererTest.kt
│ │ ├── TraceValidationTest.kt
│ │ ├── TracingDslTest.kt
│ │ ├── TracingOptionsTest.kt
│ │ ├── TracingSystemTest.kt
│ │ └── TracingValidationScopeTest.kt
│ └── stove-wiremock/
│ ├── api/
│ │ └── stove-wiremock.api
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── wiremock/
│ │ ├── Extensions.kt
│ │ ├── Options.kt
│ │ ├── WireMockBodyMatching.kt
│ │ ├── WireMockCallJournal.kt
│ │ ├── WireMockReportConstants.kt
│ │ ├── WireMockRequestListener.kt
│ │ ├── WireMockSnapshot.kt
│ │ ├── WireMockSystem.kt
│ │ ├── WireMockVacuumCleaner.kt
│ │ ├── WireMockVerification.kt
│ │ ├── WiremockDsl.kt
│ │ └── stubbing.kt
│ └── test/
│ ├── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── wiremock/
│ │ ├── ExtensionsTest.kt
│ │ ├── StoveConfig.kt
│ │ ├── WireMockDeletionTest.kt
│ │ ├── WireMockExposedConfigurationTest.kt
│ │ ├── WireMockOperationsTest.kt
│ │ ├── WireMockPartialMockingTest.kt
│ │ ├── WireMockSystemTests.kt
│ │ └── WireMockVerificationTest.kt
│ └── resources/
│ └── kotest.properties
├── lint.sh
├── mkdocs.yml
├── plugins/
│ └── stove-tracing-gradle-plugin/
│ ├── build.gradle.kts
│ ├── gradle/
│ │ └── libs.versions.toml
│ ├── gradle.properties
│ ├── settings.gradle.kts
│ └── src/
│ ├── main/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── gradle/
│ │ ├── StoveTracingExtension.kt
│ │ ├── StoveTracingPlugin.kt
│ │ └── internal/
│ │ ├── JvmArgsBuilder.kt
│ │ ├── TestTaskConfigurator.kt
│ │ └── TracingDefaults.kt
│ └── test/
│ └── kotlin/
│ └── com/
│ └── trendyol/
│ └── stove/
│ └── gradle/
│ └── StoveTracingPluginFunctionalTest.kt
├── pre-commit.sh
├── recipes/
│ ├── jvm/
│ │ ├── .editorconfig
│ │ ├── .gitattributes
│ │ ├── .gitignore
│ │ ├── build.gradle.kts
│ │ ├── buildSrc/
│ │ │ ├── build.gradle.kts
│ │ │ ├── settings.gradle.kts
│ │ │ └── src/
│ │ │ └── main/
│ │ │ └── kotlin/
│ │ │ └── TestFolders.kt
│ │ ├── detekt.yml
│ │ ├── gradle/
│ │ │ ├── libs.versions.toml
│ │ │ └── wrapper/
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ ├── java-recipes/
│ │ │ ├── build.gradle.kts
│ │ │ ├── quarkus-basic-recipe/
│ │ │ │ ├── build.gradle.kts
│ │ │ │ └── src/
│ │ │ │ ├── main/
│ │ │ │ │ ├── java/
│ │ │ │ │ │ └── com/
│ │ │ │ │ │ └── trendyol/
│ │ │ │ │ │ └── stove/
│ │ │ │ │ │ └── recipes/
│ │ │ │ │ │ └── quarkus/
│ │ │ │ │ │ ├── EnglishGreetingService.java
│ │ │ │ │ │ ├── GreetingResource.java
│ │ │ │ │ │ ├── GreetingService.java
│ │ │ │ │ │ ├── HelloService.java
│ │ │ │ │ │ ├── HelloServiceImpl.java
│ │ │ │ │ │ ├── InMemoryItemRepository.java
│ │ │ │ │ │ ├── Item.java
│ │ │ │ │ │ ├── ItemRepository.java
│ │ │ │ │ │ ├── QuarkusMainApp.java
│ │ │ │ │ │ ├── SpanishGreetingService.java
│ │ │ │ │ │ ├── StoveStartupSignal.java
│ │ │ │ │ │ └── TurkishGreetingService.java
│ │ │ │ │ └── resources/
│ │ │ │ │ └── application.properties
│ │ │ │ └── test-e2e/
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── com/
│ │ │ │ │ └── trendyol/
│ │ │ │ │ └── stove/
│ │ │ │ │ └── recipes/
│ │ │ │ │ └── quarkus/
│ │ │ │ │ └── e2e/
│ │ │ │ │ ├── setup/
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ └── StoveConfig.kt
│ │ │ │ │ └── tests/
│ │ │ │ │ └── IndexTests.kt
│ │ │ │ └── resources/
│ │ │ │ ├── kotest.properties
│ │ │ │ └── logback-test.xml
│ │ │ └── spring-boot-postgres-recipe/
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ ├── main/
│ │ │ │ ├── java/
│ │ │ │ │ └── com/
│ │ │ │ │ └── trendyol/
│ │ │ │ │ └── stove/
│ │ │ │ │ └── examples/
│ │ │ │ │ └── java/
│ │ │ │ │ └── spring/
│ │ │ │ │ ├── ExampleSpringBootApp.java
│ │ │ │ │ ├── application/
│ │ │ │ │ │ ├── external/
│ │ │ │ │ │ │ └── category/
│ │ │ │ │ │ │ ├── CategoryApiSpringConfiguration.java
│ │ │ │ │ │ │ ├── CategoryHttpApi.java
│ │ │ │ │ │ │ ├── CategoryHttpApiConfiguration.java
│ │ │ │ │ │ │ └── CategoryHttpApiImpl.java
│ │ │ │ │ │ └── product/
│ │ │ │ │ │ ├── command/
│ │ │ │ │ │ │ └── ProductApplicationService.java
│ │ │ │ │ │ └── messaging/
│ │ │ │ │ │ └── ProductEventHandlerListener.java
│ │ │ │ │ ├── domain/
│ │ │ │ │ │ └── ProductReactiveRepository.java
│ │ │ │ │ └── infra/
│ │ │ │ │ ├── boilerplate/
│ │ │ │ │ │ ├── http/
│ │ │ │ │ │ │ └── ControllerAdvice.java
│ │ │ │ │ │ ├── kafka/
│ │ │ │ │ │ │ ├── KafkaBeanConfiguration.java
│ │ │ │ │ │ │ ├── KafkaConfiguration.java
│ │ │ │ │ │ │ ├── KafkaDomainEventPublisher.java
│ │ │ │ │ │ │ ├── Topic.java
│ │ │ │ │ │ │ └── TopicResolver.java
│ │ │ │ │ │ ├── postgres/
│ │ │ │ │ │ │ └── PostgresConfiguration.java
│ │ │ │ │ │ └── serialization/
│ │ │ │ │ │ └── JacksonConfiguration.java
│ │ │ │ │ └── components/
│ │ │ │ │ ├── index/
│ │ │ │ │ │ └── IndexController.java
│ │ │ │ │ └── product/
│ │ │ │ │ ├── api/
│ │ │ │ │ │ ├── ProductController.java
│ │ │ │ │ │ └── ProductCreateRequest.java
│ │ │ │ │ └── persistency/
│ │ │ │ │ └── JdbcProductRepository.java
│ │ │ │ └── resources/
│ │ │ │ └── application.yml
│ │ │ └── test-e2e/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── example/
│ │ │ │ └── java/
│ │ │ │ └── spring/
│ │ │ │ └── e2e/
│ │ │ │ ├── setup/
│ │ │ │ │ ├── CreateProductsTableMigration.kt
│ │ │ │ │ ├── Stove.kt
│ │ │ │ │ └── TestData.kt
│ │ │ │ └── tests/
│ │ │ │ ├── IndexTests.kt
│ │ │ │ └── product/
│ │ │ │ └── CreateTests.kt
│ │ │ └── resources/
│ │ │ ├── kotest.properties
│ │ │ └── logback-test.xml
│ │ ├── kotlin-recipes/
│ │ │ ├── build.gradle.kts
│ │ │ ├── ktor-mongo-recipe/
│ │ │ │ ├── build.gradle.kts
│ │ │ │ └── src/
│ │ │ │ ├── main/
│ │ │ │ │ ├── kotlin/
│ │ │ │ │ │ └── com/
│ │ │ │ │ │ └── trendyol/
│ │ │ │ │ │ └── stove/
│ │ │ │ │ │ └── examples/
│ │ │ │ │ │ └── kotlin/
│ │ │ │ │ │ └── ktor/
│ │ │ │ │ │ ├── ExampleStoveKtorApp.kt
│ │ │ │ │ │ ├── application/
│ │ │ │ │ │ │ ├── RecipeAppConfig.kt
│ │ │ │ │ │ │ ├── external/
│ │ │ │ │ │ │ │ ├── CategoryHttpApi.kt
│ │ │ │ │ │ │ │ └── CategoryHttpApiImpl.kt
│ │ │ │ │ │ │ └── product/
│ │ │ │ │ │ │ └── command/
│ │ │ │ │ │ │ ├── ProductCommandHandler.kt
│ │ │ │ │ │ │ └── handling.kt
│ │ │ │ │ │ ├── domain/
│ │ │ │ │ │ │ └── product/
│ │ │ │ │ │ │ └── ProductRepository.kt
│ │ │ │ │ │ └── infra/
│ │ │ │ │ │ ├── boilerplate/
│ │ │ │ │ │ │ ├── http/
│ │ │ │ │ │ │ │ └── http.kt
│ │ │ │ │ │ │ ├── kafka/
│ │ │ │ │ │ │ │ ├── ConsumerEngine.kt
│ │ │ │ │ │ │ │ ├── ConsumerSupervisor.kt
│ │ │ │ │ │ │ │ ├── KafkaDomainEventPublisher.kt
│ │ │ │ │ │ │ │ ├── SerDe.kt
│ │ │ │ │ │ │ │ ├── Topic.kt
│ │ │ │ │ │ │ │ ├── TopicResolver.kt
│ │ │ │ │ │ │ │ └── kafka.kt
│ │ │ │ │ │ │ ├── kediatr/
│ │ │ │ │ │ │ │ └── kediatr.kt
│ │ │ │ │ │ │ ├── mongo/
│ │ │ │ │ │ │ │ └── mongo.kt
│ │ │ │ │ │ │ ├── serialization/
│ │ │ │ │ │ │ │ └── JacksonConfiguration.kt
│ │ │ │ │ │ │ └── util.kt
│ │ │ │ │ │ └── components/
│ │ │ │ │ │ ├── external/
│ │ │ │ │ │ │ └── category.kt
│ │ │ │ │ │ └── product/
│ │ │ │ │ │ ├── api/
│ │ │ │ │ │ │ └── routing.kt
│ │ │ │ │ │ ├── defs.kt
│ │ │ │ │ │ ├── messaging/
│ │ │ │ │ │ │ └── ProductAggregateRootEventsConsumer.kt
│ │ │ │ │ │ └── persistency/
│ │ │ │ │ │ └── MongoProductRepository.kt
│ │ │ │ │ └── resources/
│ │ │ │ │ └── application.yaml
│ │ │ │ └── test-e2e/
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── com/
│ │ │ │ │ └── trendyol/
│ │ │ │ │ └── stove/
│ │ │ │ │ └── examples/
│ │ │ │ │ └── kotlin/
│ │ │ │ │ └── ktor/
│ │ │ │ │ └── e2e/
│ │ │ │ │ ├── setup/
│ │ │ │ │ │ ├── StoveConfig.kt
│ │ │ │ │ │ └── TestData.kt
│ │ │ │ │ └── tests/
│ │ │ │ │ ├── IndexTests.kt
│ │ │ │ │ ├── configuration/
│ │ │ │ │ │ └── ConfigurationTests.kt
│ │ │ │ │ └── product/
│ │ │ │ │ └── CreateTests.kt
│ │ │ │ └── resources/
│ │ │ │ ├── kotest.properties
│ │ │ │ └── logback-test.xml
│ │ │ ├── ktor-postgres-recipe/
│ │ │ │ ├── build.gradle.kts
│ │ │ │ └── src/
│ │ │ │ ├── main/
│ │ │ │ │ ├── kotlin/
│ │ │ │ │ │ └── com/
│ │ │ │ │ │ └── trendyol/
│ │ │ │ │ │ └── stove/
│ │ │ │ │ │ └── examples/
│ │ │ │ │ │ └── kotlin/
│ │ │ │ │ │ └── ktor/
│ │ │ │ │ │ ├── ExampleStoveKtorApp.kt
│ │ │ │ │ │ ├── application/
│ │ │ │ │ │ │ ├── RecipeAppConfig.kt
│ │ │ │ │ │ │ ├── external/
│ │ │ │ │ │ │ │ ├── CategoryHttpApi.kt
│ │ │ │ │ │ │ │ └── CategoryHttpApiImpl.kt
│ │ │ │ │ │ │ └── product/
│ │ │ │ │ │ │ └── command/
│ │ │ │ │ │ │ ├── ProductCommandHandler.kt
│ │ │ │ │ │ │ └── handling.kt
│ │ │ │ │ │ ├── domain/
│ │ │ │ │ │ │ └── product/
│ │ │ │ │ │ │ └── ProductRepository.kt
│ │ │ │ │ │ └── infra/
│ │ │ │ │ │ ├── boilerplate/
│ │ │ │ │ │ │ ├── http/
│ │ │ │ │ │ │ │ └── http.kt
│ │ │ │ │ │ │ ├── kafka/
│ │ │ │ │ │ │ │ ├── ConsumerEngine.kt
│ │ │ │ │ │ │ │ ├── ConsumerSupervisor.kt
│ │ │ │ │ │ │ │ ├── KafkaDomainEventPublisher.kt
│ │ │ │ │ │ │ │ ├── SerDe.kt
│ │ │ │ │ │ │ │ ├── Topic.kt
│ │ │ │ │ │ │ │ ├── TopicResolver.kt
│ │ │ │ │ │ │ │ └── kafka.kt
│ │ │ │ │ │ │ ├── kediatr/
│ │ │ │ │ │ │ │ └── kediatr.kt
│ │ │ │ │ │ │ ├── serialization/
│ │ │ │ │ │ │ │ └── JacksonConfiguration.kt
│ │ │ │ │ │ │ └── util.kt
│ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ ├── external/
│ │ │ │ │ │ │ │ └── category.kt
│ │ │ │ │ │ │ └── product/
│ │ │ │ │ │ │ ├── api/
│ │ │ │ │ │ │ │ └── routing.kt
│ │ │ │ │ │ │ ├── defs.kt
│ │ │ │ │ │ │ ├── messaging/
│ │ │ │ │ │ │ │ └── ProductAggregateRootEventsConsumer.kt
│ │ │ │ │ │ │ └── persistency/
│ │ │ │ │ │ │ ├── PostgresProductRepository.kt
│ │ │ │ │ │ │ └── Product.kt
│ │ │ │ │ │ └── postgres/
│ │ │ │ │ │ ├── flyway.kt
│ │ │ │ │ │ └── postgres.kt
│ │ │ │ │ └── resources/
│ │ │ │ │ ├── application.yaml
│ │ │ │ │ └── db/
│ │ │ │ │ └── migration/
│ │ │ │ │ └── V1__init.sql
│ │ │ │ └── test-e2e/
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── com/
│ │ │ │ │ └── trendyol/
│ │ │ │ │ └── stove/
│ │ │ │ │ └── examples/
│ │ │ │ │ └── kotlin/
│ │ │ │ │ └── ktor/
│ │ │ │ │ └── e2e/
│ │ │ │ │ ├── setup/
│ │ │ │ │ │ ├── StoveConfig.kt
│ │ │ │ │ │ └── TestData.kt
│ │ │ │ │ └── tests/
│ │ │ │ │ ├── IndexTests.kt
│ │ │ │ │ ├── configuration/
│ │ │ │ │ │ └── ConfigurationTests.kt
│ │ │ │ │ └── product/
│ │ │ │ │ └── CreateTests.kt
│ │ │ │ └── resources/
│ │ │ │ ├── kotest.properties
│ │ │ │ └── logback-test.xml
│ │ │ └── spring-showcase/
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ ├── main/
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── com/
│ │ │ │ │ └── trendyol/
│ │ │ │ │ └── stove/
│ │ │ │ │ └── examples/
│ │ │ │ │ └── kotlin/
│ │ │ │ │ └── spring/
│ │ │ │ │ ├── ExampleStoveSpringBootApp.kt
│ │ │ │ │ ├── domain/
│ │ │ │ │ │ ├── order/
│ │ │ │ │ │ │ ├── Order.kt
│ │ │ │ │ │ │ ├── OrderController.kt
│ │ │ │ │ │ │ ├── OrderRepository.kt
│ │ │ │ │ │ │ └── OrderService.kt
│ │ │ │ │ │ └── statistics/
│ │ │ │ │ │ └── UserOrderStatistics.kt
│ │ │ │ │ ├── events/
│ │ │ │ │ │ ├── OrderCreatedEvent.kt
│ │ │ │ │ │ └── PaymentProcessedEvent.kt
│ │ │ │ │ └── infra/
│ │ │ │ │ ├── GlobalErrorHandler.kt
│ │ │ │ │ ├── clients/
│ │ │ │ │ │ ├── FraudDetectionClient.kt
│ │ │ │ │ │ ├── InventoryClient.kt
│ │ │ │ │ │ └── PaymentClient.kt
│ │ │ │ │ ├── grpc/
│ │ │ │ │ │ ├── GrpcErrorSpanInterceptor.kt
│ │ │ │ │ │ ├── GrpcServerConfig.kt
│ │ │ │ │ │ └── OrderQueryGrpcService.kt
│ │ │ │ │ ├── kafka/
│ │ │ │ │ │ ├── KafkaConfig.kt
│ │ │ │ │ │ ├── OrderCreatedEventListener.kt
│ │ │ │ │ │ └── OrderEventPublisher.kt
│ │ │ │ │ ├── persistence/
│ │ │ │ │ │ ├── DataSourceConfig.kt
│ │ │ │ │ │ ├── PostgresOrderRepository.kt
│ │ │ │ │ │ └── PostgresUserOrderStatisticsRepository.kt
│ │ │ │ │ └── scheduling/
│ │ │ │ │ ├── DbSchedulerConfig.kt
│ │ │ │ │ ├── EmailSchedulerService.kt
│ │ │ │ │ └── SendOrderEmailTask.kt
│ │ │ │ ├── proto/
│ │ │ │ │ ├── fraud_detection.proto
│ │ │ │ │ └── order_query.proto
│ │ │ │ └── resources/
│ │ │ │ └── application.yml
│ │ │ └── test-e2e/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── examples/
│ │ │ │ └── kotlin/
│ │ │ │ └── spring/
│ │ │ │ └── e2e/
│ │ │ │ ├── painful/
│ │ │ │ │ └── BaseIntegrationTest.kt
│ │ │ │ ├── setup/
│ │ │ │ │ ├── DbSchedulerSystem.kt
│ │ │ │ │ ├── OrderExampleInitialMigration.kt
│ │ │ │ │ └── StoveConfig.kt
│ │ │ │ └── tests/
│ │ │ │ ├── StreamingTests.kt
│ │ │ │ └── TheShowcase.kt
│ │ │ └── resources/
│ │ │ ├── kotest.properties
│ │ │ └── logback-test.xml
│ │ ├── scala-recipes/
│ │ │ ├── build.gradle.kts
│ │ │ └── spring-boot-basic-recipe/
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ ├── main/
│ │ │ │ └── scala/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── recipes/
│ │ │ │ └── scala/
│ │ │ │ └── spring/
│ │ │ │ └── SpringBootRecipeApp.scala
│ │ │ └── test-e2e/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── recipes/
│ │ │ │ └── scala/
│ │ │ │ └── spring/
│ │ │ │ └── e2e/
│ │ │ │ ├── setup/
│ │ │ │ │ └── StoveConfig.kt
│ │ │ │ └── tests/
│ │ │ │ └── IndexTests.kt
│ │ │ └── resources/
│ │ │ └── kotest.properties
│ │ ├── settings.gradle.kts
│ │ └── shared/
│ │ ├── application/
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ └── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── recipes/
│ │ │ └── shared/
│ │ │ └── application/
│ │ │ ├── BusinessException.java
│ │ │ ├── ErrorResponse.java
│ │ │ ├── ExternalApiConfiguration.java
│ │ │ └── category/
│ │ │ ├── CategoryApiConfiguration.java
│ │ │ └── CategoryApiResponse.java
│ │ └── domain/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── examples/
│ │ │ └── domain/
│ │ │ ├── ddd/
│ │ │ │ ├── AggregateRoot.java
│ │ │ │ ├── DomainEvent.java
│ │ │ │ ├── Entity.java
│ │ │ │ ├── EventPublisher.java
│ │ │ │ ├── EventRecorder.java
│ │ │ │ └── EventRouter.java
│ │ │ └── product/
│ │ │ ├── Product.java
│ │ │ └── events/
│ │ │ ├── ProductCreatedEvent.java
│ │ │ ├── ProductNameChangedEvent.java
│ │ │ └── ProductPriceChangedEvent.java
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── examples/
│ │ └── domain/
│ │ ├── ProductTests.kt
│ │ └── testing/
│ │ └── aggregateroot/
│ │ └── AggregateRootAssertion.kt
│ └── process/
│ └── golang/
│ └── go-showcase/
│ ├── .dockerignore
│ ├── .editorconfig
│ ├── .gitignore
│ ├── Dockerfile.container
│ ├── build.gradle.kts
│ ├── db.go
│ ├── go.mod
│ ├── go.sum
│ ├── gradle/
│ │ ├── libs.versions.toml
│ │ └── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── handlers.go
│ ├── kafka.go
│ ├── kafka_franz.go
│ ├── kafka_sarama.go
│ ├── kafka_segmentio.go
│ ├── main.go
│ ├── settings.gradle.kts
│ ├── stovetests/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── examples/
│ │ │ └── go/
│ │ │ └── e2e/
│ │ │ ├── setup/
│ │ │ │ ├── ProductMigration.kt
│ │ │ │ └── StoveConfig.kt
│ │ │ └── tests/
│ │ │ └── GoShowcaseTest.kt
│ │ └── resources/
│ │ └── kotest.properties
│ └── tracing.go
├── renovate.json
├── settings.gradle.kts
├── starters/
│ ├── container/
│ │ └── stove-container/
│ │ ├── api/
│ │ │ └── stove-container.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── container/
│ │ │ ├── ContainerApplicationUnderTest.kt
│ │ │ ├── ContainerDsl.kt
│ │ │ └── ContainerTarget.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── container/
│ │ ├── ContainerApplicationUnderTestTest.kt
│ │ ├── ContainerDslTest.kt
│ │ └── ContainerTargetTest.kt
│ ├── ktor/
│ │ ├── stove-ktor/
│ │ │ ├── api/
│ │ │ │ └── stove-ktor.api
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ ├── main/
│ │ │ │ └── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── ktor/
│ │ │ │ ├── DependencyResolvers.kt
│ │ │ │ ├── KtorApplicationUnderTest.kt
│ │ │ │ ├── KtorBridgeSystem.kt
│ │ │ │ └── KtorDiCheck.kt
│ │ │ └── test/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── ktor/
│ │ │ ├── DependencyResolversLinkageTest.kt
│ │ │ └── KtorDiCheckTest.kt
│ │ └── tests/
│ │ ├── ktor-di-tests/
│ │ │ ├── api/
│ │ │ │ └── ktor-di-tests.api
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ └── test/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── ktor/
│ │ │ │ ├── AutoDetectRuntimeStateTest.kt
│ │ │ │ ├── StoveConfig.kt
│ │ │ │ └── app.kt
│ │ │ └── resources/
│ │ │ └── simplelogger.properties
│ │ ├── ktor-koin-tests/
│ │ │ ├── api/
│ │ │ │ └── ktor-koin-tests.api
│ │ │ ├── build.gradle.kts
│ │ │ └── src/
│ │ │ └── test/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── ktor/
│ │ │ │ ├── AutoDetectRuntimeSelectionTest.kt
│ │ │ │ ├── StoveConfig.kt
│ │ │ │ └── app.kt
│ │ │ └── resources/
│ │ │ └── simplelogger.properties
│ │ └── ktor-test-fixtures/
│ │ ├── api/
│ │ │ └── ktor-test-fixtures.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── testFixtures/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── ktor/
│ │ ├── BridgeSystemTests.kt
│ │ └── TestDomain.kt
│ ├── micronaut/
│ │ └── stove-micronaut/
│ │ ├── api/
│ │ │ └── stove-micronaut.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── trendyol/
│ │ │ │ └── stove/
│ │ │ │ └── micronaut/
│ │ │ │ ├── MicronautApplicationUnderTest.kt
│ │ │ │ └── MicronautBridgeSystem.kt
│ │ │ └── resources/
│ │ │ ├── application.properties
│ │ │ └── logback.xml
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── BridgeSystemTestConfig.kt
│ │ └── resources/
│ │ └── kotest.properties
│ ├── process/
│ │ └── stove-process/
│ │ ├── api/
│ │ │ └── stove-process.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── process/
│ │ │ ├── ArgsProvider.kt
│ │ │ ├── EnvProvider.kt
│ │ │ ├── ProcessApplicationOptions.kt
│ │ │ ├── ProcessApplicationUnderTest.kt
│ │ │ └── ProcessDsl.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── process/
│ │ ├── ArgsProviderTest.kt
│ │ ├── EnvProviderTest.kt
│ │ └── ProcessApplicationUnderTestTest.kt
│ ├── quarkus/
│ │ └── stove-quarkus/
│ │ ├── api/
│ │ │ └── stove-quarkus.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── main/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── quarkus/
│ │ └── QuarkusApplicationUnderTest.kt
│ └── spring/
│ ├── stove-spring/
│ │ ├── api/
│ │ │ ├── stove-spring-common.api
│ │ │ └── stove-spring.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── spring/
│ │ │ ├── BridgeSystem.kt
│ │ │ ├── SpringApplicationUnderTest.kt
│ │ │ ├── SpringBootVersionCheck.kt
│ │ │ └── registrar.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ ├── SpringApplicationUnderTestTests.kt
│ │ ├── SpringBridgeSystemTests.kt
│ │ └── spring/
│ │ └── SpringBootVersionCheckTest.kt
│ ├── stove-spring-kafka/
│ │ ├── api/
│ │ │ ├── stove-spring-kafka-common.api
│ │ │ └── stove-spring-kafka.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── kafka/
│ │ │ ├── Caching.kt
│ │ │ ├── Extensions.kt
│ │ │ ├── KafkaDsl.kt
│ │ │ ├── KafkaSystem.kt
│ │ │ ├── KafkaTemplateCompatibility.kt
│ │ │ ├── MessageStore.kt
│ │ │ ├── Options.kt
│ │ │ ├── SpringKafkaVersionCheck.kt
│ │ │ ├── StoveMessage.kt
│ │ │ └── TestSystemKafkaInterceptor.kt
│ │ └── test/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── kafka/
│ │ ├── CachingTests.kt
│ │ ├── ExtensionsTests.kt
│ │ ├── KafkaOptionsTest.kt
│ │ ├── MessageStoreTests.kt
│ │ ├── SpringKafkaVersionCheckTest.kt
│ │ └── StoveMessageTests.kt
│ └── tests/
│ ├── spring-2x-kafka-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── kafka/
│ │ │ ├── protobufserde/
│ │ │ │ ├── ProtobufSerdeKafkaSystemTest.kt
│ │ │ │ └── app.kt
│ │ │ ├── shared.kt
│ │ │ └── stringserde/
│ │ │ ├── StringSerdeKafkaSystemTest.kt
│ │ │ └── app.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-2x-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-3x-kafka-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── kafka/
│ │ │ ├── protobufserde/
│ │ │ │ ├── ProtobufSerdeKafkaSystemTest.kt
│ │ │ │ └── app.kt
│ │ │ ├── shared.kt
│ │ │ └── stringserde/
│ │ │ ├── StringSerdeKafkaSystemTest.kt
│ │ │ └── app.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-3x-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-4x-kafka-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── kafka/
│ │ │ ├── protobufserde/
│ │ │ │ ├── ProtobufSerdeKafkaSystemTest.kt
│ │ │ │ └── app.kt
│ │ │ ├── shared.kt
│ │ │ └── stringserde/
│ │ │ ├── StringSerdeKafkaSystemTest.kt
│ │ │ └── app.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ ├── spring-4x-tests/
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── StoveConfig.kt
│ │ └── resources/
│ │ ├── kotest.properties
│ │ └── logback-test.xml
│ └── spring-test-fixtures/
│ ├── build.gradle.kts
│ └── src/
│ └── testFixtures/
│ ├── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ ├── BridgeSystemTests.kt
│ │ ├── TestDomain.kt
│ │ └── kafka/
│ │ ├── KafkaTestDomain.kt
│ │ ├── ProtobufSerdeKafkaSystemTests.kt
│ │ ├── ProtobufTestUtils.kt
│ │ └── StringSerdeKafkaSystemTests.kt
│ └── proto/
│ └── example.proto
├── test-extensions/
│ ├── stove-extensions-junit/
│ │ ├── api/
│ │ │ └── stove-extensions-junit.api
│ │ ├── build.gradle.kts
│ │ └── src/
│ │ ├── main/
│ │ │ └── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── extensions/
│ │ │ └── junit/
│ │ │ └── StoveJUnitExtension.kt
│ │ └── test/
│ │ ├── kotlin/
│ │ │ └── com/
│ │ │ └── trendyol/
│ │ │ └── stove/
│ │ │ └── extensions/
│ │ │ └── junit/
│ │ │ └── StoveJUnitExtensionTest.kt
│ │ └── resources/
│ │ └── logback-test.xml
│ └── stove-extensions-kotest/
│ ├── api/
│ │ └── stove-extensions-kotest.api
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── extensions/
│ │ └── kotest/
│ │ └── StoveKotestExtension.kt
│ └── test/
│ ├── kotlin/
│ │ └── com/
│ │ └── trendyol/
│ │ └── stove/
│ │ └── extensions/
│ │ └── kotest/
│ │ ├── KotestHierarchyExplorationTest.kt
│ │ └── StoveKotestExtensionTest.kt
│ └── resources/
│ ├── kotest.properties
│ └── logback-test.xml
└── tools/
└── stove-cli/
├── .gitignore
├── .idea/
│ ├── .gitignore
│ ├── copilot.data.migration.ask2agent.xml
│ ├── inspectionProfiles/
│ │ └── Project_Default.xml
│ ├── modules.xml
│ └── vcs.xml
├── Cargo.toml
├── Formula/
│ └── stove.rb
├── build.rs
├── clippy.toml
├── install.sh
├── rustfmt.toml
├── spa/
│ ├── biome.json
│ ├── index.html
│ ├── package.json
│ ├── postcss.config.js
│ ├── src/
│ │ ├── App.tsx
│ │ ├── api/
│ │ │ ├── client.ts
│ │ │ ├── live-cache.ts
│ │ │ ├── sse.ts
│ │ │ └── types.ts
│ │ ├── components/
│ │ │ ├── Badge.tsx
│ │ │ ├── CapturedStateLane.tsx
│ │ │ ├── Detail.tsx
│ │ │ ├── DurationEdge.tsx
│ │ │ ├── EntryDetails.tsx
│ │ │ ├── EntryRow.tsx
│ │ │ ├── FlowDag.tsx
│ │ │ ├── FlowTab.tsx
│ │ │ ├── GapNode.tsx
│ │ │ ├── JsonTree.tsx
│ │ │ ├── NodePopup.tsx
│ │ │ ├── ResultIcon.tsx
│ │ │ ├── SnapshotCards.tsx
│ │ │ ├── SnapshotMetricTiles.tsx
│ │ │ ├── SnapshotStateDialog.tsx
│ │ │ ├── SpanTree.tsx
│ │ │ ├── SysBadge.tsx
│ │ │ ├── SystemNode.tsx
│ │ │ └── VersionMismatchBanner.tsx
│ │ ├── hooks/
│ │ │ ├── useAppData.ts
│ │ │ └── useTheme.tsx
│ │ ├── index.css
│ │ ├── layout/
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ ├── TestDetail.tsx
│ │ │ ├── detail/
│ │ │ │ ├── TabBar.tsx
│ │ │ │ └── TestHeader.tsx
│ │ │ └── sidebar/
│ │ │ ├── AppPicker.tsx
│ │ │ ├── RunSummary.tsx
│ │ │ ├── TestFilters.tsx
│ │ │ ├── TestListItem.tsx
│ │ │ └── TestTree.tsx
│ │ ├── main.tsx
│ │ ├── utils/
│ │ │ ├── filters.ts
│ │ │ ├── flow.ts
│ │ │ ├── format.ts
│ │ │ ├── json.ts
│ │ │ ├── result.ts
│ │ │ ├── snapshot-state.ts
│ │ │ ├── status.ts
│ │ │ ├── systems.ts
│ │ │ └── version-mismatch.ts
│ │ └── vite-env.d.ts
│ ├── test/
│ │ ├── api-client.test.mjs
│ │ ├── flow.test.mjs
│ │ ├── json.test.mjs
│ │ ├── live-cache.test.mjs
│ │ ├── snapshot-state.test.mjs
│ │ └── version-mismatch.test.mjs
│ ├── tsconfig.json
│ └── vite.config.ts
├── src/
│ ├── config.rs
│ ├── error.rs
│ ├── grpc/
│ │ ├── mod.rs
│ │ └── service.rs
│ ├── http/
│ │ ├── mod.rs
│ │ ├── routes/
│ │ │ ├── meta.rs
│ │ │ ├── mod.rs
│ │ │ ├── runs.rs
│ │ │ ├── sse.rs
│ │ │ ├── static_files.rs
│ │ │ ├── tests.rs
│ │ │ └── traces.rs
│ │ └── server.rs
│ ├── ingest.rs
│ ├── lib.rs
│ ├── main.rs
│ ├── mcp/
│ │ ├── analysis/
│ │ │ └── evidence.rs
│ │ ├── analysis.rs
│ │ ├── args.rs
│ │ ├── contract.rs
│ │ ├── mod.rs
│ │ ├── protocol.rs
│ │ ├── security.rs
│ │ └── tools.rs
│ ├── skills.rs
│ ├── sse/
│ │ ├── manager.rs
│ │ └── mod.rs
│ └── storage/
│ ├── database.rs
│ ├── migrations/
│ │ ├── V1__initial_schema.sql
│ │ ├── V2__run_stove_version.sql
│ │ └── V3__test_path.sql
│ ├── mod.rs
│ ├── models.rs
│ └── repository.rs
└── tests/
├── api_e2e.rs
├── common/
│ └── mod.rs
└── mcp_e2e.rs
SYMBOL INDEX (1135 symbols across 144 files)
FILE: docs/assets/rough-notation.iife.js
class s (line 1) | class s{constructor(t){this.seed=t}next(){return this.seed?(2**31-1&(thi...
method constructor (line 1) | constructor(t){this.seed=t}
method next (line 1) | next(){return this.seed?(2**31-1&(this.seed=Math.imul(48271,this.seed)...
function i (line 1) | function i(t,e,s,i,n){return{type:"path",ops:u(t,e,s,i,n)}}
function n (line 1) | function n(t,e,s){const n=(t||[]).length;if(n>2){const i=[];for(let e=0;...
function o (line 1) | function o(t,e,s,i,o){return function(t,e){return n(t,!0,e)}([[t,e],[t+s...
function r (line 1) | function r(t,e,s,i,n){return function(t,e,s,i){const[n,o]=g(i.increment,...
function h (line 1) | function h(t){return t.randomizer||(t.randomizer=new s(t.seed||0)),t.ran...
function a (line 1) | function a(t,e,s,i=1){return s.roughness*i*(h(s)*(e-t)+t)}
function c (line 1) | function c(t,e,s=1){return a(-t,t,e,s)}
function u (line 1) | function u(t,e,s,i,n,o=!1){const r=o?n.disableMultiStrokeFill:n.disableM...
function f (line 1) | function f(t,e,s,i,n,o,r){const a=Math.pow(t-s,2)+Math.pow(e-i,2),u=Math...
function l (line 1) | function l(t,e,s){const i=t.length,n=[];if(i>3){const o=[],r=1-s.curveTi...
function g (line 1) | function g(t,e,s,i,n,o,r,h){const a=[],u=[],f=c(.5,h)-Math.PI/2;u.push([...
function d (line 1) | function d(t,e){return{maxRandomnessOffset:2,roughness:"highlight"===t?3...
function p (line 1) | function p(t,s,h,a,c,u){const f=[];let l=h.strokeWidth||2;const g=functi...
class _ (line 1) | class _{constructor(t,e){this._state="unattached",this._resizing=!1,this...
method constructor (line 1) | constructor(t,e){this._state="unattached",this._resizing=!1,this._seed...
method animate (line 1) | get animate(){return this._config.animate}
method animate (line 1) | set animate(t){this._config.animate=t}
method animationDuration (line 1) | get animationDuration(){return this._config.animationDuration}
method animationDuration (line 1) | set animationDuration(t){this._config.animationDuration=t}
method iterations (line 1) | get iterations(){return this._config.iterations}
method iterations (line 1) | set iterations(t){this._config.iterations=t}
method color (line 1) | get color(){return this._config.color}
method color (line 1) | set color(t){this._config.color!==t&&(this._config.color=t,this.refres...
method strokeWidth (line 1) | get strokeWidth(){return this._config.strokeWidth}
method strokeWidth (line 1) | set strokeWidth(t){this._config.strokeWidth!==t&&(this._config.strokeW...
method padding (line 1) | get padding(){return this._config.padding}
method padding (line 1) | set padding(t){this._config.padding!==t&&(this._config.padding=t,this....
method attach (line 1) | attach(){if("unattached"===this._state&&this._e.parentElement){!functi...
method detachListeners (line 1) | detachListeners(){window.removeEventListener("resize",this._resizeList...
method attachListeners (line 1) | attachListeners(){this.detachListeners(),window.addEventListener("resi...
method haveRectsChanged (line 1) | haveRectsChanged(){if(this._lastSizes.length){const t=this.rects();if(...
method isSameRect (line 1) | isSameRect(t,e){const s=(t,e)=>Math.round(t)===Math.round(e);return s(...
method isShowing (line 1) | isShowing(){return"not-showing"!==this._state}
method refresh (line 1) | refresh(){this.isShowing()&&!this.pendingRefresh&&(this.pendingRefresh...
method show (line 1) | show(){switch(this._state){case"unattached":break;case"showing":this.h...
method hide (line 1) | hide(){if(this._svg)for(;this._svg.lastChild;)this._svg.removeChild(th...
method remove (line 1) | remove(){this._svg&&this._svg.parentElement&&this._svg.parentElement.r...
method render (line 1) | render(t,e){let s=this._config;e&&(s=JSON.parse(JSON.stringify(this._c...
method rects (line 1) | rects(){const t=[];if(this._svg)if(this._config.multiline){const e=thi...
method svgRect (line 1) | svgRect(t,e){const s=t.getBoundingClientRect(),i=e;return{x:(i.x||i.le...
method show (line 1) | show(){for(const t of s)t.show()}
method hide (line 1) | hide(){for(const t of s)t.hide()}
FILE: docs/js/rough-notation-mkdocs.js
function parseOpts (line 41) | function parseOpts(el) {
function observe (line 55) | function observe(target, threshold, callback) {
function init (line 67) | function init() {
FILE: examples/quarkus-example/src/main/resources/db/migration/V1__create_products.sql
type products (line 1) | CREATE TABLE IF NOT EXISTS products (
FILE: go/stove-kafka/bridge.go
type PublishedMessage (line 46) | type PublishedMessage struct
type ConsumedMessage (line 54) | type ConsumedMessage struct
constant envBridgePort (line 63) | envBridgePort = "STOVE_KAFKA_BRIDGE_PORT"
constant envBridgeHost (line 64) | envBridgeHost = "STOVE_KAFKA_BRIDGE_HOST"
type Bridge (line 68) | type Bridge struct
method Close (line 105) | func (b *Bridge) Close() error {
method ReportPublished (line 114) | func (b *Bridge) ReportPublished(ctx context.Context, msg *PublishedMe...
method ReportConsumed (line 134) | func (b *Bridge) ReportConsumed(ctx context.Context, msg *ConsumedMess...
method ReportCommitted (line 156) | func (b *Bridge) ReportCommitted(ctx context.Context, topic string, pa...
function NewBridge (line 75) | func NewBridge(port string) (*Bridge, error) {
function NewBridgeFromEnv (line 100) | func NewBridgeFromEnv() (*Bridge, error) {
FILE: go/stove-kafka/bridge_test.go
function TestNilBridge_ReportPublished (line 8) | func TestNilBridge_ReportPublished(t *testing.T) {
function TestNilBridge_ReportConsumed (line 20) | func TestNilBridge_ReportConsumed(t *testing.T) {
function TestNilBridge_ReportCommitted (line 31) | func TestNilBridge_ReportCommitted(t *testing.T) {
function TestNilBridge_Close (line 39) | func TestNilBridge_Close(t *testing.T) {
function TestNewBridge_EmptyPort (line 47) | func TestNewBridge_EmptyPort(t *testing.T) {
function TestNewBridgeFromEnv_Unset (line 57) | func TestNewBridgeFromEnv_Unset(t *testing.T) {
FILE: go/stove-kafka/franz/hooks.go
type Hook (line 23) | type Hook struct
method OnProduceRecordBuffered (line 29) | func (h *Hook) OnProduceRecordBuffered(r *kgo.Record) {
method OnFetchRecordBuffered (line 44) | func (h *Hook) OnFetchRecordBuffered(r *kgo.Record) {
function recordHeaders (line 59) | func recordHeaders(headers []kgo.RecordHeader) map[string]string {
FILE: go/stove-kafka/franz/hooks_test.go
function TestHook_NilBridge_OnProduceRecordBuffered (line 9) | func TestHook_NilBridge_OnProduceRecordBuffered(t *testing.T) {
function TestHook_NilBridge_OnFetchRecordBuffered (line 18) | func TestHook_NilBridge_OnFetchRecordBuffered(t *testing.T) {
function TestRecordHeaders (line 29) | func TestRecordHeaders(t *testing.T) {
function TestRecordHeaders_Empty (line 43) | func TestRecordHeaders_Empty(t *testing.T) {
FILE: go/stove-kafka/sarama/interceptors.go
type ProducerInterceptor (line 26) | type ProducerInterceptor struct
method OnSend (line 33) | func (i *ProducerInterceptor) OnSend(msg *sarama.ProducerMessage) {
type ConsumerInterceptor (line 57) | type ConsumerInterceptor struct
method OnConsume (line 65) | func (i *ConsumerInterceptor) OnConsume(msg *sarama.ConsumerMessage) {
function encodeSaramaKey (line 81) | func encodeSaramaKey(key sarama.Encoder) (string, error) {
function producerHeaders (line 92) | func producerHeaders(headers []sarama.RecordHeader) map[string]string {
function consumerHeaders (line 100) | func consumerHeaders(headers []*sarama.RecordHeader) map[string]string {
FILE: go/stove-kafka/sarama/interceptors_test.go
function TestProducerInterceptor_NilBridge (line 9) | func TestProducerInterceptor_NilBridge(t *testing.T) {
function TestConsumerInterceptor_NilBridge (line 19) | func TestConsumerInterceptor_NilBridge(t *testing.T) {
function TestEncodeSaramaKey_Nil (line 30) | func TestEncodeSaramaKey_Nil(t *testing.T) {
function TestEncodeSaramaKey_String (line 40) | func TestEncodeSaramaKey_String(t *testing.T) {
function TestProducerHeaders (line 50) | func TestProducerHeaders(t *testing.T) {
function TestProducerHeaders_Empty (line 64) | func TestProducerHeaders_Empty(t *testing.T) {
function TestConsumerHeaders (line 71) | func TestConsumerHeaders(t *testing.T) {
function TestConsumerHeaders_Empty (line 85) | func TestConsumerHeaders_Empty(t *testing.T) {
FILE: go/stove-kafka/segmentio/bridge.go
function ReportWritten (line 27) | func ReportWritten(ctx context.Context, bridge *stovekafka.Bridge, msgs ...
function ReportRead (line 39) | func ReportRead(ctx context.Context, bridge *stovekafka.Bridge, msg kafk...
function toPublished (line 47) | func toPublished(msg kafka.Message) *stovekafka.PublishedMessage {
function toConsumed (line 56) | func toConsumed(msg kafka.Message) *stovekafka.ConsumedMessage {
function messageHeaders (line 67) | func messageHeaders(headers []kafka.Header) map[string]string {
FILE: go/stove-kafka/segmentio/bridge_test.go
function TestReportWritten_NilBridge (line 10) | func TestReportWritten_NilBridge(t *testing.T) {
function TestReportRead_NilBridge (line 18) | func TestReportRead_NilBridge(t *testing.T) {
function TestMessageHeaders (line 28) | func TestMessageHeaders(t *testing.T) {
function TestMessageHeaders_Empty (line 42) | func TestMessageHeaders_Empty(t *testing.T) {
function TestToPublished (line 49) | func TestToPublished(t *testing.T) {
function TestToConsumed (line 65) | func TestToConsumed(t *testing.T) {
FILE: go/stove-kafka/stoveobserver/messages.pb.go
constant _ (line 21) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 23) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
type HealthCheckResponse_ServingStatus (line 26) | type HealthCheckResponse_ServingStatus
method Enum (line 51) | func (x HealthCheckResponse_ServingStatus) Enum() *HealthCheckResponse...
method String (line 57) | func (x HealthCheckResponse_ServingStatus) String() string {
method Descriptor (line 61) | func (HealthCheckResponse_ServingStatus) Descriptor() protoreflect.Enu...
method Type (line 65) | func (HealthCheckResponse_ServingStatus) Type() protoreflect.EnumType {
method Number (line 69) | func (x HealthCheckResponse_ServingStatus) Number() protoreflect.EnumN...
method EnumDescriptor (line 74) | func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []i...
constant HealthCheckResponse_UNKNOWN (line 29) | HealthCheckResponse_UNKNOWN HealthCheckResponse_ServingStatus = 0
constant HealthCheckResponse_SERVING (line 30) | HealthCheckResponse_SERVING HealthCheckResponse_ServingStatus = 1
constant HealthCheckResponse_NOT_SERVING (line 31) | HealthCheckResponse_NOT_SERVING HealthCheckResponse_ServingStatus = 2
constant HealthCheckResponse_SERVICE_UNKNOWN (line 32) | HealthCheckResponse_SERVICE_UNKNOWN HealthCheckResponse_ServingStatus = 3
type ConsumedMessage (line 78) | type ConsumedMessage struct
method Reset (line 91) | func (x *ConsumedMessage) Reset() {
method String (line 98) | func (x *ConsumedMessage) String() string {
method ProtoMessage (line 102) | func (*ConsumedMessage) ProtoMessage() {}
method ProtoReflect (line 104) | func (x *ConsumedMessage) ProtoReflect() protoreflect.Message {
method Descriptor (line 117) | func (*ConsumedMessage) Descriptor() ([]byte, []int) {
method GetId (line 121) | func (x *ConsumedMessage) GetId() string {
method GetMessage (line 128) | func (x *ConsumedMessage) GetMessage() []byte {
method GetTopic (line 135) | func (x *ConsumedMessage) GetTopic() string {
method GetPartition (line 142) | func (x *ConsumedMessage) GetPartition() int32 {
method GetOffset (line 149) | func (x *ConsumedMessage) GetOffset() int64 {
method GetKey (line 156) | func (x *ConsumedMessage) GetKey() string {
method GetHeaders (line 163) | func (x *ConsumedMessage) GetHeaders() map[string]string {
type PublishedMessage (line 170) | type PublishedMessage struct
method Reset (line 181) | func (x *PublishedMessage) Reset() {
method String (line 188) | func (x *PublishedMessage) String() string {
method ProtoMessage (line 192) | func (*PublishedMessage) ProtoMessage() {}
method ProtoReflect (line 194) | func (x *PublishedMessage) ProtoReflect() protoreflect.Message {
method Descriptor (line 207) | func (*PublishedMessage) Descriptor() ([]byte, []int) {
method GetId (line 211) | func (x *PublishedMessage) GetId() string {
method GetMessage (line 218) | func (x *PublishedMessage) GetMessage() []byte {
method GetTopic (line 225) | func (x *PublishedMessage) GetTopic() string {
method GetKey (line 232) | func (x *PublishedMessage) GetKey() string {
method GetHeaders (line 239) | func (x *PublishedMessage) GetHeaders() map[string]string {
type CommittedMessage (line 246) | type CommittedMessage struct
method Reset (line 257) | func (x *CommittedMessage) Reset() {
method String (line 264) | func (x *CommittedMessage) String() string {
method ProtoMessage (line 268) | func (*CommittedMessage) ProtoMessage() {}
method ProtoReflect (line 270) | func (x *CommittedMessage) ProtoReflect() protoreflect.Message {
method Descriptor (line 283) | func (*CommittedMessage) Descriptor() ([]byte, []int) {
method GetId (line 287) | func (x *CommittedMessage) GetId() string {
method GetTopic (line 294) | func (x *CommittedMessage) GetTopic() string {
method GetPartition (line 301) | func (x *CommittedMessage) GetPartition() int32 {
method GetOffset (line 308) | func (x *CommittedMessage) GetOffset() int64 {
method GetMetadata (line 315) | func (x *CommittedMessage) GetMetadata() string {
type AcknowledgedMessage (line 322) | type AcknowledgedMessage struct
method Reset (line 333) | func (x *AcknowledgedMessage) Reset() {
method String (line 340) | func (x *AcknowledgedMessage) String() string {
method ProtoMessage (line 344) | func (*AcknowledgedMessage) ProtoMessage() {}
method ProtoReflect (line 346) | func (x *AcknowledgedMessage) ProtoReflect() protoreflect.Message {
method Descriptor (line 359) | func (*AcknowledgedMessage) Descriptor() ([]byte, []int) {
method GetId (line 363) | func (x *AcknowledgedMessage) GetId() string {
method GetTopic (line 370) | func (x *AcknowledgedMessage) GetTopic() string {
method GetPartition (line 377) | func (x *AcknowledgedMessage) GetPartition() int32 {
method GetOffset (line 384) | func (x *AcknowledgedMessage) GetOffset() int64 {
method GetException (line 391) | func (x *AcknowledgedMessage) GetException() string {
type Reply (line 398) | type Reply struct
method Reset (line 405) | func (x *Reply) Reset() {
method String (line 412) | func (x *Reply) String() string {
method ProtoMessage (line 416) | func (*Reply) ProtoMessage() {}
method ProtoReflect (line 418) | func (x *Reply) ProtoReflect() protoreflect.Message {
method Descriptor (line 431) | func (*Reply) Descriptor() ([]byte, []int) {
method GetStatus (line 435) | func (x *Reply) GetStatus() int32 {
type HealthCheckRequest (line 442) | type HealthCheckRequest struct
method Reset (line 449) | func (x *HealthCheckRequest) Reset() {
method String (line 456) | func (x *HealthCheckRequest) String() string {
method ProtoMessage (line 460) | func (*HealthCheckRequest) ProtoMessage() {}
method ProtoReflect (line 462) | func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 475) | func (*HealthCheckRequest) Descriptor() ([]byte, []int) {
method GetService (line 479) | func (x *HealthCheckRequest) GetService() string {
type HealthCheckResponse (line 486) | type HealthCheckResponse struct
method Reset (line 493) | func (x *HealthCheckResponse) Reset() {
method String (line 500) | func (x *HealthCheckResponse) String() string {
method ProtoMessage (line 504) | func (*HealthCheckResponse) ProtoMessage() {}
method ProtoReflect (line 506) | func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 519) | func (*HealthCheckResponse) Descriptor() ([]byte, []int) {
method GetStatus (line 523) | func (x *HealthCheckResponse) GetStatus() HealthCheckResponse_ServingS...
constant file_messages_proto_rawDesc (line 532) | file_messages_proto_rawDesc = "" +
function file_messages_proto_rawDescGZIP (line 590) | func file_messages_proto_rawDescGZIP() []byte {
function init (line 632) | func init() { file_messages_proto_init() }
function file_messages_proto_init (line 633) | func file_messages_proto_init() {
FILE: go/stove-kafka/stoveobserver/messages_grpc.pb.go
constant _ (line 21) | _ = grpc.SupportPackageIsVersion9
constant StoveKafkaObserverService_HealthCheck_FullMethodName (line 24) | StoveKafkaObserverService_HealthCheck_FullMethodName = "/com.t...
constant StoveKafkaObserverService_OnConsumedMessage_FullMethodName (line 25) | StoveKafkaObserverService_OnConsumedMessage_FullMethodName = "/com.t...
constant StoveKafkaObserverService_OnPublishedMessage_FullMethodName (line 26) | StoveKafkaObserverService_OnPublishedMessage_FullMethodName = "/com.t...
constant StoveKafkaObserverService_OnCommittedMessage_FullMethodName (line 27) | StoveKafkaObserverService_OnCommittedMessage_FullMethodName = "/com.t...
constant StoveKafkaObserverService_OnAcknowledgedMessage_FullMethodName (line 28) | StoveKafkaObserverService_OnAcknowledgedMessage_FullMethodName = "/com.t...
type StoveKafkaObserverServiceClient (line 34) | type StoveKafkaObserverServiceClient interface
type stoveKafkaObserverServiceClient (line 46) | type stoveKafkaObserverServiceClient struct
method HealthCheck (line 54) | func (c *stoveKafkaObserverServiceClient) HealthCheck(ctx context.Cont...
method OnConsumedMessage (line 64) | func (c *stoveKafkaObserverServiceClient) OnConsumedMessage(ctx contex...
method OnPublishedMessage (line 74) | func (c *stoveKafkaObserverServiceClient) OnPublishedMessage(ctx conte...
method OnCommittedMessage (line 84) | func (c *stoveKafkaObserverServiceClient) OnCommittedMessage(ctx conte...
method OnAcknowledgedMessage (line 94) | func (c *stoveKafkaObserverServiceClient) OnAcknowledgedMessage(ctx co...
function NewStoveKafkaObserverServiceClient (line 50) | func NewStoveKafkaObserverServiceClient(cc grpc.ClientConnInterface) Sto...
type StoveKafkaObserverServiceServer (line 107) | type StoveKafkaObserverServiceServer interface
type UnimplementedStoveKafkaObserverServiceServer (line 125) | type UnimplementedStoveKafkaObserverServiceServer struct
method HealthCheck (line 127) | func (UnimplementedStoveKafkaObserverServiceServer) HealthCheck(contex...
method OnConsumedMessage (line 130) | func (UnimplementedStoveKafkaObserverServiceServer) OnConsumedMessage(...
method OnPublishedMessage (line 133) | func (UnimplementedStoveKafkaObserverServiceServer) OnPublishedMessage...
method OnCommittedMessage (line 136) | func (UnimplementedStoveKafkaObserverServiceServer) OnCommittedMessage...
method OnAcknowledgedMessage (line 139) | func (UnimplementedStoveKafkaObserverServiceServer) OnAcknowledgedMess...
method mustEmbedUnimplementedStoveKafkaObserverServiceServer (line 142) | func (UnimplementedStoveKafkaObserverServiceServer) mustEmbedUnimpleme...
method testEmbeddedByValue (line 144) | func (UnimplementedStoveKafkaObserverServiceServer) testEmbeddedByValu...
type UnsafeStoveKafkaObserverServiceServer (line 149) | type UnsafeStoveKafkaObserverServiceServer interface
function RegisterStoveKafkaObserverServiceServer (line 153) | func RegisterStoveKafkaObserverServiceServer(s grpc.ServiceRegistrar, sr...
function _StoveKafkaObserverService_HealthCheck_Handler (line 164) | func _StoveKafkaObserverService_HealthCheck_Handler(srv interface{}, ctx...
function _StoveKafkaObserverService_OnConsumedMessage_Handler (line 182) | func _StoveKafkaObserverService_OnConsumedMessage_Handler(srv interface{...
function _StoveKafkaObserverService_OnPublishedMessage_Handler (line 200) | func _StoveKafkaObserverService_OnPublishedMessage_Handler(srv interface...
function _StoveKafkaObserverService_OnCommittedMessage_Handler (line 218) | func _StoveKafkaObserverService_OnCommittedMessage_Handler(srv interface...
function _StoveKafkaObserverService_OnAcknowledgedMessage_Handler (line 236) | func _StoveKafkaObserverService_OnAcknowledgedMessage_Handler(srv interf...
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/EnglishGreetingService.java
class EnglishGreetingService (line 5) | @ApplicationScoped
method greet (line 7) | @Override
method getLanguage (line 12) | @Override
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/GreetingResource.java
class GreetingResource (line 10) | @Path("/hello")
method hello (line 24) | @GET
method greetings (line 30) | @GET
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/GreetingService.java
type GreetingService (line 4) | public interface GreetingService {
method greet (line 5) | String greet(String name);
method getLanguage (line 7) | String getLanguage();
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/HelloService.java
type HelloService (line 7) | public interface HelloService {
method hello (line 8) | String hello();
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/HelloServiceImpl.java
class HelloServiceImpl (line 5) | @Singleton
method hello (line 7) | @Override
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/InMemoryItemRepository.java
class InMemoryItemRepository (line 9) | @ApplicationScoped
method add (line 14) | @Override
method addItem (line 19) | @Override
method getById (line 24) | @Override
method getItemById (line 29) | @Override
method getAllIds (line 35) | @Override
method clear (line 40) | @Override
method count (line 45) | @Override
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/Item.java
class Item (line 4) | public class Item {
method Item (line 8) | public Item(String id, String name) {
method getId (line 13) | public String getId() {
method getName (line 17) | public String getName() {
method toString (line 21) | @Override
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/ItemRepository.java
type ItemRepository (line 6) | public interface ItemRepository {
method add (line 7) | void add(String id, String name);
method addItem (line 9) | void addItem(Item item);
method getById (line 11) | String getById(String id);
method getItemById (line 13) | Item getItemById(String id);
method getAllIds (line 15) | List<String> getAllIds();
method clear (line 17) | void clear();
method count (line 19) | int count();
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/QuarkusMainApp.java
class QuarkusMainApp (line 6) | @QuarkusMain
method main (line 9) | public static void main(String[] args) {
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/SpanishGreetingService.java
class SpanishGreetingService (line 5) | @ApplicationScoped
method greet (line 7) | @Override
method getLanguage (line 12) | @Override
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/StoveStartupSignal.java
class StoveStartupSignal (line 8) | @ApplicationScoped
method onStart (line 13) | void onStart(@Observes StartupEvent event) {
method onStop (line 17) | void onStop(@Observes ShutdownEvent event) {
FILE: recipes/jvm/java-recipes/quarkus-basic-recipe/src/main/java/com/trendyol/stove/recipes/quarkus/TurkishGreetingService.java
class TurkishGreetingService (line 5) | @ApplicationScoped
method greet (line 7) | @Override
method getLanguage (line 12) | @Override
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/ExampleSpringBootApp.java
class ExampleSpringBootApp (line 9) | @SpringBootApplication
method main (line 12) | public static void main(String[] args) {
method run (line 16) | public static ConfigurableApplicationContext run(
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/application/external/category/CategoryApiSpringConfiguration.java
class CategoryApiSpringConfiguration (line 6) | @ConfigurationProperties(prefix = "external-apis.category")
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/application/external/category/CategoryHttpApi.java
type CategoryHttpApi (line 7) | public interface CategoryHttpApi {
method getCategoryById (line 8) | Mono<CategoryApiResponse> getCategoryById(int id) throws BusinessExcep...
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/application/external/category/CategoryHttpApiConfiguration.java
class CategoryHttpApiConfiguration (line 17) | @Configuration
method categoryHttpApi (line 21) | @Bean
method webClient (line 27) | private WebClient webClient(
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/application/external/category/CategoryHttpApiImpl.java
class CategoryHttpApiImpl (line 7) | public class CategoryHttpApiImpl implements CategoryHttpApi {
method CategoryHttpApiImpl (line 10) | public CategoryHttpApiImpl(WebClient categoryWebClient) {
method getCategoryById (line 14) | @Override
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/application/product/command/ProductApplicationService.java
class ProductApplicationService (line 11) | @Component
method ProductApplicationService (line 16) | public ProductApplicationService(
method create (line 22) | public Mono<Void> create(String name, double price, int categoryId) th...
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/application/product/messaging/ProductEventHandlerListener.java
class ProductEventHandlerListener (line 7) | @Component
method listen (line 10) | @KafkaListener(topics = {"${kafka.topics.product.name}"})
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/domain/ProductReactiveRepository.java
type ProductReactiveRepository (line 6) | public interface ProductReactiveRepository {
method findById (line 7) | Mono<Product> findById(String id);
method save (line 9) | Mono<Void> save(Product product);
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/boilerplate/http/ControllerAdvice.java
class ControllerAdvice (line 12) | @RestControllerAdvice
method handleException (line 17) | @ExceptionHandler(BusinessException.class)
method handleException (line 24) | @ExceptionHandler(Exception.class)
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/boilerplate/kafka/KafkaBeanConfiguration.java
class KafkaBeanConfiguration (line 23) | @Configuration
method kafkaConfiguration (line 27) | @Bean
method topicResolver (line 33) | @Bean
method consumerProperties (line 38) | @Bean
method producerProperties (line 70) | @Bean
method kafkaTemplate (line 82) | @Bean
method kafkaListenerContainerFactory (line 88) | @Bean
method toMap (line 101) | private Map<String, Object> toMap(Properties properties) {
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/boilerplate/kafka/KafkaConfiguration.java
class KafkaConfiguration (line 6) | public @Data class KafkaConfiguration {
method flattenInterceptorClasses (line 17) | public String flattenInterceptorClasses() {
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/boilerplate/kafka/KafkaDomainEventPublisher.java
class KafkaDomainEventPublisher (line 11) | @Component
method KafkaDomainEventPublisher (line 17) | public KafkaDomainEventPublisher(
method publishFor (line 23) | @Override
method mapEventsToProducerRecords (line 28) | private <TId> Stream<ProducerRecord<String, Object>> mapEventsToProduc...
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/boilerplate/kafka/Topic.java
class Topic (line 5) | public @Data class Topic {
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/boilerplate/kafka/TopicResolver.java
class TopicResolver (line 3) | public class TopicResolver {
method TopicResolver (line 6) | public TopicResolver(KafkaConfiguration kafkaConfiguration) {
method resolve (line 10) | public Topic resolve(String aggregateName) {
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/boilerplate/postgres/PostgresConfiguration.java
class PostgresConfiguration (line 8) | @Configuration
method databaseClient (line 11) | @Bean
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/boilerplate/serialization/JacksonConfiguration.java
class JacksonConfiguration (line 11) | @Configuration
method defaultObjectMapper (line 14) | public static ObjectMapper defaultObjectMapper() {
method objectMapper (line 26) | @Bean
method jackson2ObjectMapperBuilderCustomizer (line 32) | @Bean
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/components/index/IndexController.java
class IndexController (line 6) | @RestController
method index (line 10) | @RequestMapping
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/components/product/api/ProductController.java
class ProductController (line 12) | @RestController
method ProductController (line 17) | public ProductController(ProductApplicationService productService) {
method createProduct (line 21) | @PostMapping
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/components/product/api/ProductCreateRequest.java
class ProductCreateRequest (line 5) | @Data
method ProductCreateRequest (line 11) | public ProductCreateRequest(String name, double price, int categoryId) {
FILE: recipes/jvm/java-recipes/spring-boot-postgres-recipe/src/main/java/com/trendyol/stove/examples/java/spring/infra/components/product/persistency/JdbcProductRepository.java
class JdbcProductRepository (line 11) | @Component
method JdbcProductRepository (line 16) | public JdbcProductRepository(DatabaseClient databaseClient, EventPubli...
method findById (line 21) | @Override
method save (line 39) | public Mono<Void> save(Product product) {
FILE: recipes/jvm/kotlin-recipes/ktor-postgres-recipe/src/main/resources/db/migration/V1__init.sql
type products (line 1) | create table products
FILE: recipes/jvm/shared/application/src/main/java/com/trendyol/stove/recipes/shared/application/BusinessException.java
class BusinessException (line 3) | public class BusinessException extends Exception {
method BusinessException (line 4) | public BusinessException(String message) {
method BusinessException (line 8) | public BusinessException(String message, Throwable cause) {
FILE: recipes/jvm/shared/application/src/main/java/com/trendyol/stove/recipes/shared/application/ExternalApiConfiguration.java
class ExternalApiConfiguration (line 5) | @Data
method ExternalApiConfiguration (line 10) | public ExternalApiConfiguration() {
method ExternalApiConfiguration (line 14) | public ExternalApiConfiguration(String url, int timeout) {
FILE: recipes/jvm/shared/application/src/main/java/com/trendyol/stove/recipes/shared/application/category/CategoryApiConfiguration.java
class CategoryApiConfiguration (line 5) | public class CategoryApiConfiguration extends ExternalApiConfiguration {}
FILE: recipes/jvm/shared/domain/src/main/java/com/trendyol/stove/examples/domain/ddd/AggregateRoot.java
class AggregateRoot (line 9) | @SuppressWarnings("unchecked")
method AggregateRoot (line 20) | protected AggregateRoot(TId id) {
method register (line 27) | protected <TEvent extends DomainEvent> void register(
method applyEvent (line 32) | protected <TEvent extends DomainEvent> void applyEvent(TEvent event) {
method play (line 39) | protected <TEvent> void play(TEvent event) {
method getIdAsString (line 43) | @JsonIgnore
method clearDomainEvents (line 48) | @JsonIgnore
method domainEvents (line 53) | @JsonIgnore
method hasChanges (line 58) | @JsonIgnore
method isNew (line 63) | @JsonIgnore
method getAggregateName (line 68) | @JsonIgnore
method equals (line 73) | @Override
method hashCode (line 81) | @Override
FILE: recipes/jvm/shared/domain/src/main/java/com/trendyol/stove/examples/domain/ddd/DomainEvent.java
class DomainEvent (line 6) | public abstract class DomainEvent {
method DomainEvent (line 12) | public DomainEvent() {
FILE: recipes/jvm/shared/domain/src/main/java/com/trendyol/stove/examples/domain/ddd/Entity.java
class Entity (line 5) | public class Entity<TId, TAggregate extends AggregateRoot<?>> {
method Entity (line 9) | public Entity(TId id) {
method register (line 14) | protected <TEvent extends DomainEvent> void register(
method route (line 19) | public <TEvent> void route(TEvent event) {
FILE: recipes/jvm/shared/domain/src/main/java/com/trendyol/stove/examples/domain/ddd/EventPublisher.java
type EventPublisher (line 3) | public interface EventPublisher {
method publishFor (line 4) | <TId> void publishFor(AggregateRoot<TId> aggregateRoot);
FILE: recipes/jvm/shared/domain/src/main/java/com/trendyol/stove/examples/domain/ddd/EventRecorder.java
class EventRecorder (line 6) | public class EventRecorder {
method EventRecorder (line 9) | public EventRecorder() {
method record (line 13) | public void record(DomainEvent event) {
method getRecords (line 17) | public List<DomainEvent> getRecords() {
method removeAll (line 21) | public void removeAll() {
FILE: recipes/jvm/shared/domain/src/main/java/com/trendyol/stove/examples/domain/ddd/EventRouter.java
class EventRouter (line 8) | public class EventRouter {
method register (line 11) | public <TEvent extends DomainEvent> void register(
method route (line 16) | public <TEvent> void route(TEvent event) {
FILE: recipes/jvm/shared/domain/src/main/java/com/trendyol/stove/examples/domain/product/Product.java
class Product (line 11) | @Getter
method Product (line 14) | @SuppressWarnings("unused")
method Product (line 19) | private Product(String id, String name, double price, int categoryId) {
method changePrice (line 33) | public void changePrice(double newPrice) {
method changeName (line 37) | public void changeName(String newName) {
method handle (line 41) | private void handle(ProductCreatedEvent event) {
method handle (line 46) | private void handle(ProductPriceChangedEvent event) {
method handle (line 50) | private void handle(ProductNameChangedEvent event) {
method create (line 54) | public static Product create(String name, double price, int categoryId) {
method fromPersistency (line 64) | public static Product fromPersistency(
FILE: recipes/jvm/shared/domain/src/main/java/com/trendyol/stove/examples/domain/product/events/ProductCreatedEvent.java
class ProductCreatedEvent (line 6) | @NoArgsConstructor(force = true)
method ProductCreatedEvent (line 12) | public ProductCreatedEvent(String name, double price, int categoryId) {
FILE: recipes/jvm/shared/domain/src/main/java/com/trendyol/stove/examples/domain/product/events/ProductNameChangedEvent.java
class ProductNameChangedEvent (line 6) | @NoArgsConstructor(force = true)
method ProductNameChangedEvent (line 10) | public ProductNameChangedEvent(String newName) {
FILE: recipes/jvm/shared/domain/src/main/java/com/trendyol/stove/examples/domain/product/events/ProductPriceChangedEvent.java
class ProductPriceChangedEvent (line 6) | @NoArgsConstructor(force = true)
method ProductPriceChangedEvent (line 10) | public ProductPriceChangedEvent(double newPrice) {
FILE: recipes/process/golang/go-showcase/db.go
function initDB (line 13) | func initDB(connStr string) (*sql.DB, error) {
function insertProduct (line 27) | func insertProduct(ctx context.Context, db *sql.DB, p Product) error {
function getProduct (line 35) | func getProduct(ctx context.Context, db *sql.DB, id string) (*Product, e...
function updateProduct (line 47) | func updateProduct(ctx context.Context, db *sql.DB, id string, name stri...
function listProducts (line 55) | func listProducts(ctx context.Context, db *sql.DB) ([]Product, error) {
FILE: recipes/process/golang/go-showcase/handlers.go
type Product (line 13) | type Product struct
type createProductRequest (line 19) | type createProductRequest struct
function registerRoutes (line 24) | func registerRoutes(mux *http.ServeMux, db *sql.DB, producer KafkaProduc...
function handleHealth (line 31) | func handleHealth(w http.ResponseWriter, _ *http.Request) {
function handleCreateProduct (line 35) | func handleCreateProduct(db *sql.DB, producer KafkaProducer) http.Handle...
function handleGetProduct (line 69) | func handleGetProduct(db *sql.DB) http.HandlerFunc {
function handleListProducts (line 87) | func handleListProducts(db *sql.DB) http.HandlerFunc {
function writeJSON (line 102) | func writeJSON(w http.ResponseWriter, status int, v any) {
FILE: recipes/process/golang/go-showcase/kafka.go
constant topicProductCreated (line 13) | topicProductCreated = "product.created"
constant topicProductUpdate (line 14) | topicProductUpdate = "product.update"
type ProductCreatedEvent (line 18) | type ProductCreatedEvent struct
type ProductUpdateEvent (line 25) | type ProductUpdateEvent struct
type KafkaProducer (line 32) | type KafkaProducer interface
function initKafka (line 39) | func initKafka(library, brokers string, db *sql.DB, bridge *stovekafka.B...
function handleProductUpdate (line 59) | func handleProductUpdate(db *sql.DB, value []byte) {
FILE: recipes/process/golang/go-showcase/kafka_franz.go
type franzProducer (line 15) | type franzProducer struct
method SendMessage (line 19) | func (p *franzProducer) SendMessage(topic, key string, value []byte) e...
method Close (line 28) | func (p *franzProducer) Close() error {
function initFranzKafka (line 33) | func initFranzKafka(brokers, groupID string, db *sql.DB, bridge *stoveka...
FILE: recipes/process/golang/go-showcase/kafka_sarama.go
type saramaProducer (line 15) | type saramaProducer struct
method SendMessage (line 19) | func (p *saramaProducer) SendMessage(topic, key string, value []byte) ...
method Close (line 28) | func (p *saramaProducer) Close() error {
function initSaramaKafka (line 32) | func initSaramaKafka(brokers, groupID string, db *sql.DB, bridge *stovek...
type saramaUpdateHandler (line 82) | type saramaUpdateHandler struct
method Setup (line 86) | func (h *saramaUpdateHandler) Setup(_ sarama.ConsumerGroupSession) err...
method Cleanup (line 87) | func (h *saramaUpdateHandler) Cleanup(_ sarama.ConsumerGroupSession) e...
method ConsumeClaim (line 89) | func (h *saramaUpdateHandler) ConsumeClaim(session sarama.ConsumerGrou...
FILE: recipes/process/golang/go-showcase/kafka_segmentio.go
type segmentioProducer (line 16) | type segmentioProducer struct
method SendMessage (line 21) | func (p *segmentioProducer) SendMessage(topic, key string, value []byt...
method Close (line 35) | func (p *segmentioProducer) Close() error {
function initSegmentioKafka (line 39) | func initSegmentioKafka(brokers, groupID string, db *sql.DB, bridge *sto...
FILE: recipes/process/golang/go-showcase/main.go
function getEnv (line 17) | func getEnv(key, fallback string) string {
function main (line 24) | func main() {
FILE: recipes/process/golang/go-showcase/tracing.go
function initTracing (line 18) | func initTracing(ctx context.Context, serviceName string) (func(context....
FILE: tools/stove-cli/Formula/stove.rb
class Stove (line 6) | class Stove < Formula
method install (line 30) | def install
FILE: tools/stove-cli/build.rs
function main (line 4) | fn main() -> Result<(), Box<dyn std::error::Error>> {
function build_spa (line 35) | fn build_spa() {
function run_npm (line 62) | fn run_npm(dir: &Path, args: &[&str]) {
FILE: tools/stove-cli/spa/src/App.tsx
function App (line 7) | function App() {
FILE: tools/stove-cli/spa/src/api/client.ts
constant BASE (line 3) | const BASE = "/api/v1";
function get (line 6) | async function get<T>(url: string): Promise<T> {
function del (line 12) | async function del(url: string): Promise<void> {
FILE: tools/stove-cli/spa/src/api/live-cache.ts
constant RUNNING (line 6) | const RUNNING: Status = "RUNNING";
function applyLiveDashboardEvent (line 8) | function applyLiveDashboardEvent(queryClient: QueryClient, event: LiveDa...
function invalidateDashboardQueries (line 185) | function invalidateDashboardQueries(queryClient: QueryClient, runId?: st...
function upsertAppSummary (line 198) | function upsertAppSummary(apps: AppSummary[] | undefined, incoming: AppS...
function nextRunCount (line 204) | function nextRunCount(apps: AppSummary[] | undefined, appName: string, r...
function upsertRun (line 212) | function upsertRun(runs: Run[] | undefined, incoming: Run): Run[] {
function upsertTest (line 216) | function upsertTest(tests: Test[] | undefined, incoming: Test): Test[] {
function updateCachedRuns (line 220) | function updateCachedRuns(queryClient: QueryClient, runId: string, updat...
function updateCachedTests (line 232) | function updateCachedTests(
function appendEntries (line 245) | function appendEntries(entries: Entry[] | undefined, incoming: Entry): E...
function appendSpan (line 254) | function appendSpan(spans: Span[] | undefined, incoming: Span): Span[] {
function mergeSpans (line 263) | function mergeSpans(existing: Span[] | undefined, incoming: Span[]): Spa...
function appendSnapshots (line 267) | function appendSnapshots(snapshots: Snapshot[] | undefined, incoming: Sn...
function compareRuns (line 281) | function compareRuns(left: Run, right: Run): number {
function compareTests (line 285) | function compareTests(left: Test, right: Test): number {
function isSameSpan (line 289) | function isSameSpan(left: Span, right: Span): boolean {
function findTestIdForTrace (line 293) | function findTestIdForTrace(
FILE: tools/stove-cli/spa/src/api/sse.ts
type UseSSEOptions (line 4) | interface UseSSEOptions {
function useSSE (line 11) | function useSSE({ onEvent, onGap, onReconnect, onDisconnect }: UseSSEOpt...
FILE: tools/stove-cli/spa/src/api/types.ts
constant EVENT_TYPE (line 5) | const EVENT_TYPE = {
type EventType (line 15) | type EventType = (typeof EVENT_TYPE)[keyof typeof EVENT_TYPE];
type AppSummary (line 17) | interface AppSummary {
type MetaResponse (line 25) | interface MetaResponse {
type LiveRecordId (line 29) | type LiveRecordId = number | string;
type Run (line 31) | interface Run {
type Test (line 45) | interface Test {
type Entry (line 58) | interface Entry {
type Span (line 75) | interface Span {
type Snapshot (line 92) | interface Snapshot {
type LiveRunStartedPayload (line 101) | interface LiveRunStartedPayload {
type LiveRunEndedPayload (line 108) | interface LiveRunEndedPayload {
type LiveTestStartedPayload (line 117) | interface LiveTestStartedPayload {
type LiveTestEndedPayload (line 126) | interface LiveTestEndedPayload {
type LiveEntryRecordedPayload (line 134) | interface LiveEntryRecordedPayload {
type LiveSpanRecordedPayload (line 150) | interface LiveSpanRecordedPayload {
type LiveSnapshotPayload (line 167) | interface LiveSnapshotPayload {
type LiveEventBase (line 175) | interface LiveEventBase {
type LiveDashboardEvent (line 180) | type LiveDashboardEvent =
FILE: tools/stove-cli/spa/src/components/Badge.tsx
type BadgeProps (line 5) | interface BadgeProps {
function Badge (line 9) | function Badge({ status }: BadgeProps) {
type BadgeStyle (line 34) | interface BadgeStyle {
constant DARK (line 40) | const DARK: Record<Status | "DEFAULT", BadgeStyle> = {
constant LIGHT (line 48) | const LIGHT: Record<Status | "DEFAULT", BadgeStyle> = {
FILE: tools/stove-cli/spa/src/components/CapturedStateLane.tsx
type CapturedStateLaneProps (line 5) | interface CapturedStateLaneProps {
function CapturedStateLane (line 10) | function CapturedStateLane({ snapshots, onSelect }: CapturedStateLanePro...
function SnapshotLaneCard (line 34) | function SnapshotLaneCard({
FILE: tools/stove-cli/spa/src/components/Detail.tsx
type DetailProps (line 3) | interface DetailProps {
function Detail (line 9) | function Detail({ label, value, color }: DetailProps) {
FILE: tools/stove-cli/spa/src/components/DurationEdge.tsx
function DurationEdge (line 6) | function DurationEdge(props: EdgeProps) {
FILE: tools/stove-cli/spa/src/components/EntryDetails.tsx
function EntryDetails (line 4) | function EntryDetails({ entry }: { entry: Entry }) {
FILE: tools/stove-cli/spa/src/components/EntryRow.tsx
type EntryRowProps (line 8) | interface EntryRowProps {
function EntryRow (line 12) | function EntryRow({ entry }: EntryRowProps) {
FILE: tools/stove-cli/spa/src/components/FlowDag.tsx
type FlowDagProps (line 24) | interface FlowDagProps {
function FlowDag (line 31) | function FlowDag({ nodes, edges, onNodeClick, compact }: FlowDagProps) {
FILE: tools/stove-cli/spa/src/components/FlowTab.tsx
type FlowTabProps (line 11) | interface FlowTabProps {
type FlowMode (line 18) | type FlowMode = "timeline" | "trace";
function modeButtonClass (line 20) | function modeButtonClass(active: boolean): string {
function FlowTab (line 28) | function FlowTab({ entries, spans, snapshots, onOpenTraceTab }: FlowTabP...
FILE: tools/stove-cli/spa/src/components/GapNode.tsx
function GapNode (line 6) | function GapNode({ data }: NodeProps) {
FILE: tools/stove-cli/spa/src/components/JsonTree.tsx
type JsonTreeProps (line 3) | interface JsonTreeProps {
function JsonTree (line 9) | function JsonTree({ value, defaultExpandedDepth = 1, searchQuery = "" }:...
type JsonTreeNodeProps (line 23) | interface JsonTreeNodeProps {
function JsonTreeNode (line 31) | function JsonTreeNode({
function JsonPrimitive (line 119) | function JsonPrimitive({ value, searchQuery }: { value: unknown; searchQ...
function isExpandable (line 159) | function isExpandable(value: unknown): value is Record<string, unknown> ...
function getChildren (line 163) | function getChildren(value: Record<string, unknown> | unknown[]): Array<...
function renderCollapsedPreview (line 171) | function renderCollapsedPreview(children: Array<[string, unknown]>, isAr...
function HighlightedText (line 184) | function HighlightedText({ text, query }: { text: string; query: string ...
function splitByQuery (line 205) | function splitByQuery(
function escapeRegExp (line 227) | function escapeRegExp(value: string): string {
FILE: tools/stove-cli/spa/src/components/NodePopup.tsx
type NodePopupProps (line 6) | interface NodePopupProps {
function NodePopup (line 13) | function NodePopup({ entries, traceId, onClose, onOpenTrace }: NodePopup...
FILE: tools/stove-cli/spa/src/components/ResultIcon.tsx
function ResultIcon (line 3) | function ResultIcon({ result }: { result: string }) {
FILE: tools/stove-cli/spa/src/components/SnapshotCards.tsx
type SnapshotCardsProps (line 9) | interface SnapshotCardsProps {
function SnapshotCards (line 14) | function SnapshotCards({ snapshots, hiddenCount = 0 }: SnapshotCardsProp...
function DetailedSnapshotCard (line 55) | function DetailedSnapshotCard({ snapshot, onOpen }: { snapshot: Snapshot...
function HiddenSnapshotNotice (line 115) | function HiddenSnapshotNotice({
FILE: tools/stove-cli/spa/src/components/SnapshotMetricTiles.tsx
type SnapshotMetricTilesProps (line 3) | interface SnapshotMetricTilesProps {
function SnapshotMetricTiles (line 8) | function SnapshotMetricTiles({ metrics, compact = false }: SnapshotMetri...
function SnapshotMetricTile (line 22) | function SnapshotMetricTile({ metric, compact }: { metric: SnapshotMetri...
function toneStyle (line 54) | function toneStyle(tone: SnapshotMetric["tone"]): {
FILE: tools/stove-cli/spa/src/components/SnapshotStateDialog.tsx
type SnapshotStateDialogProps (line 14) | interface SnapshotStateDialogProps {
function SnapshotStateDialog (line 19) | function SnapshotStateDialog({ snapshot, onClose }: SnapshotStateDialogP...
FILE: tools/stove-cli/spa/src/components/SpanTree.tsx
type SpanTreeProps (line 7) | interface SpanTreeProps {
type SpanNode (line 11) | interface SpanNode {
function SpanTree (line 16) | function SpanTree({ spans }: SpanTreeProps) {
function SpanNodeView (line 40) | function SpanNodeView({ node, depth }: { node: SpanNode; depth: number }) {
function buildTree (line 120) | function buildTree(spans: Span[]): SpanNode[] {
FILE: tools/stove-cli/spa/src/components/SysBadge.tsx
type SysBadgeProps (line 3) | interface SysBadgeProps {
function SysBadge (line 7) | function SysBadge({ system }: SysBadgeProps) {
FILE: tools/stove-cli/spa/src/components/SystemNode.tsx
function SystemNode (line 10) | function SystemNode({ data }: NodeProps) {
FILE: tools/stove-cli/spa/src/components/VersionMismatchBanner.tsx
type VersionMismatchBannerProps (line 6) | interface VersionMismatchBannerProps {
function VersionMismatchBanner (line 10) | function VersionMismatchBanner({ summary }: VersionMismatchBannerProps) {
FILE: tools/stove-cli/spa/src/hooks/useAppData.ts
function useAppData (line 10) | function useAppData() {
FILE: tools/stove-cli/spa/src/hooks/useTheme.tsx
type Theme (line 3) | type Theme = "light" | "dark";
type ThemeContext (line 5) | interface ThemeContext {
function ThemeProvider (line 12) | function ThemeProvider({ children }: { children: ReactNode }) {
function useTheme (line 29) | function useTheme() {
FILE: tools/stove-cli/spa/src/layout/Header.tsx
function Header (line 3) | function Header() {
FILE: tools/stove-cli/spa/src/layout/Sidebar.tsx
constant SIDEBAR_MIN_WIDTH (line 12) | const SIDEBAR_MIN_WIDTH = 240;
constant SIDEBAR_MAX_WIDTH (line 13) | const SIDEBAR_MAX_WIDTH = 600;
constant SIDEBAR_DEFAULT_WIDTH (line 14) | const SIDEBAR_DEFAULT_WIDTH = 320;
constant SIDEBAR_STORAGE_KEY (line 15) | const SIDEBAR_STORAGE_KEY = "stove-sidebar-width";
function loadSidebarWidth (line 17) | function loadSidebarWidth(): number {
type SidebarProps (line 26) | interface SidebarProps {
function Sidebar (line 37) | function Sidebar({
FILE: tools/stove-cli/spa/src/layout/TestDetail.tsx
type TestDetailProps (line 15) | interface TestDetailProps {
function TestDetail (line 21) | function TestDetail({ runId, test, liveConnected }: TestDetailProps) {
function QueryErrorMessage (line 129) | function QueryErrorMessage({ error, fallback }: { error: unknown; fallba...
FILE: tools/stove-cli/spa/src/layout/detail/TabBar.tsx
type Tab (line 1) | type Tab = "timeline" | "trace" | "snapshots" | "flow";
type TabDef (line 3) | interface TabDef {
type TabBarProps (line 9) | interface TabBarProps {
function TabBar (line 15) | function TabBar({ tabs, active, onSelect }: TabBarProps) {
FILE: tools/stove-cli/spa/src/layout/detail/TestHeader.tsx
type TestHeaderProps (line 5) | interface TestHeaderProps {
function TestHeader (line 9) | function TestHeader({ test }: TestHeaderProps) {
FILE: tools/stove-cli/spa/src/layout/sidebar/AppPicker.tsx
type AppPickerProps (line 3) | interface AppPickerProps {
function AppPicker (line 10) | function AppPicker({ apps, mismatchedApps, selectedApp, onSelectApp }: A...
FILE: tools/stove-cli/spa/src/layout/sidebar/RunSummary.tsx
type RunSummaryProps (line 6) | interface RunSummaryProps {
function RunSummary (line 11) | function RunSummary({ run, tests }: RunSummaryProps) {
function Stat (line 36) | function Stat({ label, value, color }: { label: string; value: number; c...
FILE: tools/stove-cli/spa/src/layout/sidebar/TestFilters.tsx
type FilterValue (line 1) | type FilterValue = "all" | "pass" | "fail";
type TestFiltersProps (line 3) | interface TestFiltersProps {
function TestFilters (line 10) | function TestFilters({ filter, onFilterChange, search, onSearchChange }:...
FILE: tools/stove-cli/spa/src/layout/sidebar/TestListItem.tsx
type TestListItemProps (line 5) | interface TestListItemProps {
function TestListItem (line 12) | function TestListItem({ test, selected, onSelect, hideSpec }: TestListIt...
FILE: tools/stove-cli/spa/src/layout/sidebar/TestTree.tsx
type TestTreeProps (line 6) | interface TestTreeProps {
type TreeNode (line 12) | interface TreeNode {
function TestTree (line 18) | function TestTree({ tests, selectedTestId, onSelectTest }: TestTreeProps) {
function buildTree (line 32) | function buildTree(tests: Test[]): Map<string, TreeNode> {
function renderNodes (line 63) | function renderNodes(
function getNodeAggregateStatus (line 137) | function getNodeAggregateStatus(node: TreeNode): Status {
function collectNodeStatuses (line 143) | function collectNodeStatuses(node: TreeNode, out: Status[]): void {
function StatusDot (line 152) | function StatusDot({ status }: { status: Status }) {
FILE: tools/stove-cli/spa/src/utils/filters.ts
function matchesFilter (line 5) | function matchesFilter(test: Test, filter: FilterValue): boolean {
function matchesSearch (line 11) | function matchesSearch(test: Test, query: string): boolean {
function filterTests (line 21) | function filterTests(tests: Test[], filter: FilterValue, search: string)...
FILE: tools/stove-cli/spa/src/utils/flow.ts
constant EXECUTION_GAP_THRESHOLD_MS (line 8) | const EXECUTION_GAP_THRESHOLD_MS = 1000;
constant ADJACENT_MERGE_WINDOW_MS (line 9) | const ADJACENT_MERGE_WINDOW_MS = 250;
constant STEP_NODE_SIZE (line 10) | const STEP_NODE_SIZE = { width: 240, height: 128 };
constant TRACE_NODE_SIZE (line 11) | const TRACE_NODE_SIZE = { width: 240, height: 120 };
constant ARRANGE_NODE_SIZE (line 12) | const ARRANGE_NODE_SIZE = { width: 240, height: 128 };
constant GAP_NODE_SIZE (line 13) | const GAP_NODE_SIZE = { width: 208, height: 96 };
type SystemNodeData (line 15) | interface SystemNodeData extends Record<string, unknown> {
type GapNodeData (line 30) | interface GapNodeData extends Record<string, unknown> {
type FlowNodeData (line 39) | type FlowNodeData = SystemNodeData | GapNodeData;
type DurationEdgeData (line 41) | interface DurationEdgeData extends Record<string, unknown> {
type TimelineStepGroup (line 46) | interface TimelineStepGroup {
type TimelineStepSpec (line 55) | interface TimelineStepSpec {
type TimelineGapSpec (line 60) | interface TimelineGapSpec {
type TimelineSpec (line 69) | type TimelineSpec = TimelineStepSpec | TimelineGapSpec;
function entriesToDag (line 71) | function entriesToDag(entries: Entry[]): { nodes: Node<FlowNodeData>[]; ...
function spansToTraceDag (line 185) | function spansToTraceDag(spans: Span[]): { nodes: Node<FlowNodeData>[]; ...
function applyDagreLayout (line 229) | function applyDagreLayout(
function getNodeLayoutSize (line 267) | function getNodeLayoutSize(node: Node<FlowNodeData>): { width: number; h...
constant DB_SYSTEM_MAP (line 278) | const DB_SYSTEM_MAP: Record<string, string> = {
function detectSystemFromSpan (line 289) | function detectSystemFromSpan(span: Span): string {
function createSystemNode (line 305) | function createSystemNode(id: string, data: SystemNodeData): Node<FlowNo...
function getSystemNodeLayoutSize (line 314) | function getSystemNodeLayoutSize(data: SystemNodeData): { width: number;...
function cloneLayoutSize (line 325) | function cloneLayoutSize(size: { width: number; height: number }): {
function groupEntriesIntoSteps (line 332) | function groupEntriesIntoSteps(entries: Entry[]): TimelineStepGroup[] {
function canMergeAdjacentEntries (line 374) | function canMergeAdjacentEntries(
function expandTimelineSpecs (line 390) | function expandTimelineSpecs(groups: TimelineStepGroup[]): TimelineSpec[] {
function splitArrangeGroups (line 420) | function splitArrangeGroups(groups: TimelineStepGroup[]): {
function collapseArrangeRuns (line 435) | function collapseArrangeRuns(groups: TimelineStepGroup[]): TimelineStepG...
function combineArrangeRun (line 466) | function combineArrangeRun(groups: TimelineStepGroup[]): TimelineStepGro...
function isArrangeEntryGroup (line 489) | function isArrangeEntryGroup(entries: Entry[]): boolean {
function isArrangeSystem (line 498) | function isArrangeSystem(system: string): boolean {
function isArrangeAction (line 502) | function isArrangeAction(action: string): boolean {
function nodeIdForSpec (line 506) | function nodeIdForSpec(spec: TimelineSpec, index: number): string {
function getSpecStartedAtMs (line 510) | function getSpecStartedAtMs(spec: TimelineSpec): number {
function getSpecEndedAtMs (line 514) | function getSpecEndedAtMs(spec: TimelineSpec): number {
function summarizeArrangeAction (line 518) | function summarizeArrangeAction(
function toMs (line 534) | function toMs(timestamp: string): number {
constant ARRANGE_SYSTEMS (line 538) | const ARRANGE_SYSTEMS = new Set(["WireMock", "gRPC Mock"]);
constant ARRANGE_ACTION_PATTERNS (line 539) | const ARRANGE_ACTION_PATTERNS = [/^Register stub:/, /^Register .* stub:/];
FILE: tools/stove-cli/spa/src/utils/format.ts
function formatDuration (line 1) | function formatDuration(ms: number | null | undefined): string {
function formatTimestamp (line 8) | function formatTimestamp(iso: string): string {
function formatNanosDuration (line 23) | function formatNanosDuration(startNanos: number, endNanos: number): stri...
FILE: tools/stove-cli/spa/src/utils/json.ts
function tryFormatJson (line 1) | function tryFormatJson(s: string): string {
function tryFormatJsonDeep (line 9) | function tryFormatJsonDeep(s: string): string {
function parseJsonDeep (line 17) | function parseJsonDeep(s: string): unknown | null {
type JsonSearchResult (line 25) | interface JsonSearchResult {
function filterJsonByQuery (line 30) | function filterJsonByQuery(value: unknown, query: string): JsonSearchRes...
function describeJsonValue (line 42) | function describeJsonValue(value: unknown): string {
function getJsonPreviewKeys (line 59) | function getJsonPreviewKeys(value: unknown, limit = 4): string[] {
function normalizeEmbeddedJson (line 71) | function normalizeEmbeddedJson(value: unknown): unknown {
function filterJsonValue (line 100) | function filterJsonValue(value: unknown, normalizedQuery: string): JsonS...
function primitiveIncludes (line 146) | function primitiveIncludes(value: unknown, normalizedQuery: string): boo...
function parseAttrs (line 154) | function parseAttrs(json: string | null): Record<string, string> {
FILE: tools/stove-cli/spa/src/utils/result.ts
function isFailed (line 1) | function isFailed(result: string): boolean {
function isSuccessful (line 6) | function isSuccessful(result: string): boolean {
function getResultTone (line 11) | function getResultTone(result: string): "failed" | "success" | "neutral" {
FILE: tools/stove-cli/spa/src/utils/snapshot-state.ts
type SnapshotMetric (line 4) | interface SnapshotMetric {
type SnapshotPartition (line 11) | interface SnapshotPartition<TSnapshot> {
function hasDetailedSnapshotState (line 16) | function hasDetailedSnapshotState(
function getKafkaSnapshotMetrics (line 28) | function getKafkaSnapshotMetrics(
function partitionSnapshotsByDetail (line 66) | function partitionSnapshotsByDetail<TSnapshot extends Pick<Snapshot, "st...
function hasInspectableValue (line 77) | function hasInspectableValue(value: unknown): boolean {
function countMetricValue (line 94) | function countMetricValue(value: unknown): number | null {
function metricTone (line 110) | function metricTone(key: string, value: number): SnapshotMetric["tone"] {
FILE: tools/stove-cli/spa/src/utils/status.ts
type Status (line 1) | type Status = "RUNNING" | "PASSED" | "FAILED" | "ERROR";
function aggregateStatus (line 7) | function aggregateStatus(statuses: Iterable<Status>): Status {
function collectStatuses (line 17) | function collectStatuses<T>(items: T[], getStatus: (item: T) => Status):...
FILE: tools/stove-cli/spa/src/utils/systems.ts
type SystemInfo (line 1) | interface SystemInfo {
constant SYSTEM_MAP (line 6) | const SYSTEM_MAP: Record<string, SystemInfo> = {
constant DEFAULT_SYSTEM (line 24) | const DEFAULT_SYSTEM: SystemInfo = { color: "#94a3b8", icon: "\u2022" };
function getSystemInfo (line 26) | function getSystemInfo(name: string): SystemInfo {
FILE: tools/stove-cli/spa/src/utils/version-mismatch.ts
constant RELEASE_VERSION_PATTERN (line 3) | const RELEASE_VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)$/;
constant SWITCH_HINT (line 4) | const SWITCH_HINT = "Switch to a mismatched app to see exact remediation.";
constant CLI_UPGRADE_COMMAND (line 5) | const CLI_UPGRADE_COMMAND = "brew upgrade Trendyol/trendyol-tap/stove";
type VersionMismatchKind (line 7) | type VersionMismatchKind = "runtime_older" | "cli_older" | "unknown";
type VersionMismatch (line 9) | interface VersionMismatch {
type VersionMismatchSummary (line 16) | interface VersionMismatchSummary {
type VersionMismatchRemediationStep (line 23) | interface VersionMismatchRemediationStep {
type VersionMismatchBannerModel (line 28) | interface VersionMismatchBannerModel {
function compareVersions (line 38) | function compareVersions(
function summarizeVersionMismatches (line 69) | function summarizeVersionMismatches(
function buildVersionMismatchBannerModel (line 96) | function buildVersionMismatchBannerModel(
function createVersionMismatch (line 113) | function createVersionMismatch(app: AppSummary, cliVersion: string): Ver...
function bannerTitle (line 127) | function bannerTitle(mismatchCount: number): string {
function normalizeVersion (line 133) | function normalizeVersion(version: string | null | undefined): string | ...
function parseReleaseVersion (line 138) | function parseReleaseVersion(version: string): number[] | null {
function remediationStepsForMismatch (line 147) | function remediationStepsForMismatch(mismatch: VersionMismatch): Version...
function dependencyAlignmentMessage (line 167) | function dependencyAlignmentMessage(cliVersion: string): string {
function installScriptCommand (line 171) | function installScriptCommand(runtimeVersion: string): string {
function textStep (line 175) | function textStep(value: string): VersionMismatchRemediationStep {
function commandStep (line 179) | function commandStep(value: string): VersionMismatchRemediationStep {
FILE: tools/stove-cli/src/config.rs
type Config (line 13) | pub struct Config {
type StoveCommand (line 50) | pub enum StoveCommand {
type SkillsCommand (line 60) | pub enum SkillsCommand {
function handle_fresh_start (line 73) | pub fn handle_fresh_start(db_path: &str) -> std::io::Result<Option<Strin...
function default_db_path (line 91) | fn default_db_path() -> String {
function dirs_fallback (line 99) | fn dirs_fallback() -> std::path::PathBuf {
function fresh_start_backs_up_and_deletes_existing_db (line 115) | fn fresh_start_backs_up_and_deletes_existing_db() {
function fresh_start_returns_none_when_file_does_not_exist (line 130) | fn fresh_start_returns_none_when_file_does_not_exist() {
function fresh_start_skips_in_memory_database (line 140) | fn fresh_start_skips_in_memory_database() {
function cli_parses_default_values (line 147) | fn cli_parses_default_values() {
function cli_parses_custom_ports (line 157) | fn cli_parses_custom_ports() {
function cli_parses_clear_flag (line 166) | fn cli_parses_clear_flag() {
function cli_parses_fresh_start_flag (line 173) | fn cli_parses_fresh_start_flag() {
function cli_parses_custom_db_path (line 180) | fn cli_parses_custom_db_path() {
function cli_defaults_skills_flags_off (line 187) | fn cli_defaults_skills_flags_off() {
function cli_parses_update_skills_flag (line 195) | fn cli_parses_update_skills_flag() {
function cli_parses_no_skills_check_flag (line 201) | fn cli_parses_no_skills_check_flag() {
function cli_parses_skills_install_subcommand (line 207) | fn cli_parses_skills_install_subcommand() {
function cli_parses_skills_install_force (line 217) | fn cli_parses_skills_install_force() {
FILE: tools/stove-cli/src/error.rs
type AppError (line 8) | pub enum AppError {
method into_response (line 30) | fn into_response(self) -> axum::response::Response {
type Result (line 26) | pub type Result<T> = std::result::Result<T, AppError>;
FILE: tools/stove-cli/src/grpc/service.rs
constant RUN_STARTED (line 23) | pub const RUN_STARTED: &str = "run_started";
constant RUN_ENDED (line 24) | pub const RUN_ENDED: &str = "run_ended";
constant TEST_STARTED (line 25) | pub const TEST_STARTED: &str = "test_started";
constant TEST_ENDED (line 26) | pub const TEST_ENDED: &str = "test_ended";
constant ENTRY_RECORDED (line 27) | pub const ENTRY_RECORDED: &str = "entry_recorded";
constant SPAN_RECORDED (line 28) | pub const SPAN_RECORDED: &str = "span_recorded";
constant SNAPSHOT (line 29) | pub const SNAPSHOT: &str = "snapshot";
type DashboardEventServiceImpl (line 33) | pub struct DashboardEventServiceImpl {
method new (line 44) | pub fn new(repository: Arc<Repository>, sse_manager: Arc<SseManager>) ...
method new_with_ingest_config (line 54) | pub fn new_with_ingest_config(
method new_with_ingestor (line 65) | pub fn new_with_ingestor(
method flush_pending (line 79) | pub async fn flush_pending(&self) -> AppResult<()> {
method process_event (line 84) | fn process_event(&self, event: &proto::DashboardEvent) -> std::result:...
method prepare_event (line 104) | fn prepare_event(
method prepare_run_started (line 144) | fn prepare_run_started(
method prepare_run_ended (line 173) | fn prepare_run_ended(
method prepare_test_started (line 207) | fn prepare_test_started(
method prepare_test_ended (line 242) | fn prepare_test_ended(
method prepare_entry_recorded (line 273) | fn prepare_entry_recorded(
method prepare_span_recorded (line 329) | fn prepare_span_recorded(
method prepare_snapshot (line 397) | fn prepare_snapshot(
method live_event (line 426) | fn live_event(
method stream_events (line 442) | async fn stream_events(
method send_event (line 453) | async fn send_event(
type PreparedDashboardEvent (line 462) | struct PreparedDashboardEvent {
type LiveState (line 469) | struct LiveState {
method clear_run (line 476) | fn clear_run(&mut self, run_id: &str) {
function ensure_run_known (line 487) | fn ensure_run_known(state: &LiveState, run_id: &str) -> AppResult<()> {
function ensure_test_known (line 497) | fn ensure_test_known(state: &LiveState, run_id: &str, test_id: &str) -> ...
function extract_test_id (line 511) | fn extract_test_id(attributes: &HashMap<String, String>) -> Option<Strin...
function run_status (line 523) | fn run_status(failed: i32) -> RunStatus {
function format_timestamp (line 531) | fn format_timestamp(ts: Option<&prost_types::Timestamp>) -> String {
function non_empty (line 541) | fn non_empty(value: &str) -> Option<String> {
function to_status (line 550) | fn to_status(error: AppError) -> Status {
function test_service (line 562) | fn test_service() -> DashboardEventServiceImpl {
function ts (line 569) | fn ts(seconds: i64) -> Option<prost_types::Timestamp> {
function no_broadcast_on_invalid_event_order (line 574) | async fn no_broadcast_on_invalid_event_order() {
function broadcast_fires_before_batch_flush (line 602) | async fn broadcast_fires_before_batch_flush() {
function process_run_started_event (line 634) | async fn process_run_started_event() {
function process_full_lifecycle (line 661) | async fn process_full_lifecycle() {
FILE: tools/stove-cli/src/http/routes/meta.rs
type MetaResponse (line 9) | pub struct MetaResponse {
type McpMeta (line 15) | pub struct McpMeta {
function get_meta (line 22) | pub async fn get_meta(headers: HeaderMap) -> Json<MetaResponse> {
function mcp_endpoint (line 34) | fn mcp_endpoint(headers: &HeaderMap) -> String {
FILE: tools/stove-cli/src/http/routes/runs.rs
type RunsQuery (line 9) | pub struct RunsQuery {
function get_apps (line 13) | pub async fn get_apps(
function get_runs (line 20) | pub async fn get_runs(
function get_run (line 28) | pub async fn get_run(
function clear_all (line 36) | pub async fn clear_all(
FILE: tools/stove-cli/src/http/routes/sse.rs
function sse_handler (line 15) | pub async fn sse_handler(
FILE: tools/stove-cli/src/http/routes/static_files.rs
type SpaAssets (line 11) | struct SpaAssets;
function static_handler (line 14) | pub async fn static_handler(uri: Uri) -> Response {
function is_asset_like_path (line 38) | fn is_asset_like_path(path: &str) -> bool {
function detects_asset_like_paths_by_extension (line 47) | fn detects_asset_like_paths_by_extension() {
FILE: tools/stove-cli/src/http/routes/tests.rs
function get_tests (line 7) | pub async fn get_tests(
function get_entries (line 15) | pub async fn get_entries(
function get_snapshots (line 23) | pub async fn get_snapshots(
function get_test_spans (line 31) | pub async fn get_test_spans(
FILE: tools/stove-cli/src/http/routes/traces.rs
function get_trace (line 7) | pub async fn get_trace(
FILE: tools/stove-cli/src/http/server.rs
type AppState (line 13) | pub struct AppState {
function create_router (line 20) | pub fn create_router(repository: Arc<Repository>, sse_manager: Arc<SseMa...
function create_router_with_ingestor (line 25) | pub fn create_router_with_ingestor(
FILE: tools/stove-cli/src/ingest.rs
constant DEFAULT_MAX_BATCH_SIZE (line 12) | pub const DEFAULT_MAX_BATCH_SIZE: usize = 20;
constant DEFAULT_MAX_BATCH_DELAY (line 13) | pub const DEFAULT_MAX_BATCH_DELAY: Duration = Duration::from_secs(5);
type PersistedDashboardEvent (line 16) | pub enum PersistedDashboardEvent {
type LiveDashboardEvent (line 60) | pub struct LiveDashboardEvent {
method with_seq (line 69) | pub fn with_seq(mut self, seq: u64) -> Self {
type LiveDashboardPayload (line 87) | pub enum LiveDashboardPayload {
type LiveRunStartedPayload (line 98) | pub struct LiveRunStartedPayload {
type LiveRunEndedPayload (line 106) | pub struct LiveRunEndedPayload {
type LiveTestStartedPayload (line 116) | pub struct LiveTestStartedPayload {
type LiveTestEndedPayload (line 126) | pub struct LiveTestEndedPayload {
type LiveEntryRecordedPayload (line 135) | pub struct LiveEntryRecordedPayload {
type LiveSpanRecordedPayload (line 152) | pub struct LiveSpanRecordedPayload {
type LiveSnapshotPayload (line 170) | pub struct LiveSnapshotPayload {
type EventIngestor (line 179) | pub struct EventIngestor {
method new (line 185) | pub fn new(repository: Arc<Repository>) -> Self {
method with_config (line 190) | pub fn with_config(
method enqueue (line 205) | pub fn enqueue(&self, event: PersistedDashboardEvent, flush_immediatel...
method flush_pending (line 215) | pub async fn flush_pending(&self) -> Result<()> {
type IngestCommand (line 227) | enum IngestCommand {
function run_ingest_loop (line 237) | async fn run_ingest_loop(
function handle_command (line 287) | fn handle_command(
function persist_pending (line 311) | fn persist_pending(
function live_record_id (line 348) | fn live_record_id(seq: u64) -> i64 {
FILE: tools/stove-cli/src/lib.rs
constant STOVE_CLI_VERSION (line 9) | pub const STOVE_CLI_VERSION: &str = env!("STOVE_VERSION");
FILE: tools/stove-cli/src/main.rs
function main (line 17) | async fn main() -> anyhow::Result<()> {
FILE: tools/stove-cli/src/mcp/analysis.rs
constant FLUSH_TIMEOUT (line 21) | const FLUSH_TIMEOUT: Duration = Duration::from_millis(500);
type ToolOutput (line 24) | pub struct ToolOutput {
type Analyzer (line 30) | pub struct Analyzer {
method new (line 37) | pub fn new(repository: Arc<Repository>, ingestor: Option<EventIngestor...
method call_tool (line 44) | pub async fn call_tool(&self, name: &str, arguments: Value) -> Result<...
method flush_pending (line 59) | async fn flush_pending(&self) {
method apps (line 67) | fn apps(&self, arguments: Value) -> Result<ToolOutput, String> {
method runs (line 107) | fn runs(&self, arguments: Value) -> Result<ToolOutput, String> {
method failures (line 153) | fn failures(&self, arguments: Value) -> Result<ToolOutput, String> {
method failure_detail (line 210) | fn failure_detail(&self, arguments: Value) -> Result<ToolOutput, Strin...
method timeline (line 279) | fn timeline(&self, arguments: Value) -> Result<ToolOutput, String> {
method trace (line 315) | fn trace(&self, arguments: Value) -> Result<ToolOutput, String> {
method snapshot (line 366) | fn snapshot(&self, arguments: Value) -> Result<ToolOutput, String> {
method raw_evidence (line 398) | fn raw_evidence(&self, arguments: Value) -> Result<ToolOutput, String> {
method resolve_test (line 478) | fn resolve_test(&self, run_id: &str, test_id: &str) -> Result<(Run, Te...
function selected_runs (line 495) | fn selected_runs(
function failure_item (line 512) | fn failure_item(run: &Run, test: &Test) -> Value {
function test_json (line 529) | fn test_json(test: &Test) -> Value {
function timeline_summary (line 542) | fn timeline_summary(entries: &[Entry], run_id: &str, test_id: &str, max_...
function failure_window (line 552) | fn failure_window(entries: &[Entry], max_events: usize) -> Vec<&Entry> {
function trace_summary (line 582) | fn trace_summary(
function trace_ids_from_entries (line 627) | fn trace_ids_from_entries(entries: &[Entry]) -> Vec<String> {
function ranked_trace_ids (line 637) | fn ranked_trace_ids(spans: &[Span], entries: &[Entry]) -> Vec<String> {
function critical_path_for_trace (line 662) | fn critical_path_for_trace(spans: &[Span], trace_id: &str, max_spans: us...
function compact_event (line 701) | fn compact_event(entry: &Entry) -> Value {
function groups_have_running_runs (line 712) | fn groups_have_running_runs(groups: &[Value]) -> bool {
function correlated_test_for_trace (line 718) | fn correlated_test_for_trace(repository: &Repository, run: &Run, trace_i...
function is_failed_test (line 734) | fn is_failed_test(test: &Test) -> bool {
function is_failed_status (line 738) | fn is_failed_status(status: &TestStatus) -> bool {
function is_failed_span (line 742) | fn is_failed_span(span: &Span) -> bool {
function tool_call (line 750) | pub(super) fn tool_call(tool: ToolName, arguments: Value) -> Value {
function exact_test_tool_call (line 757) | fn exact_test_tool_call(tool: ToolName, run_id: &str, test_id: &str) -> ...
function tool_args (line 767) | pub(super) fn tool_args(entries: impl IntoIterator<Item = (ArgName, Valu...
function selector_rules (line 776) | fn selector_rules() -> Value {
function fallback_message (line 785) | fn fallback_message() -> &'static str {
function output (line 789) | fn output(structured: Value, heading: &str) -> ToolOutput {
function compact_text (line 794) | fn compact_text(value: &Value) -> String {
function display_error (line 799) | fn display_error(error: impl std::fmt::Display) -> String {
FILE: tools/stove-cli/src/mcp/analysis/evidence.rs
function entry_preview (line 6) | pub(super) fn entry_preview(entry: &Entry, max_chars: usize) -> Value {
function span_preview (line 29) | pub(super) fn span_preview(span: &Span, max_chars: usize) -> Value {
function snapshot_summary (line 46) | pub(super) fn snapshot_summary(snapshot: &Snapshot, max_chars: usize) ->...
function snapshot_detail (line 61) | pub(super) fn snapshot_detail(
function state_overview (line 94) | fn state_overview(parsed: &Value) -> Value {
function parse_state (line 109) | fn parse_state(raw: &str, max_chars: usize) -> Value {
function preview_field (line 120) | fn preview_field(raw: Option<&str>, max_chars: usize) -> Value {
function redact_value (line 135) | fn redact_value(value: &Value, max_chars: usize) -> Value {
function is_sensitive_key (line 161) | fn is_sensitive_key(key: &str) -> bool {
function clip_opt (line 177) | pub(super) fn clip_opt(value: Option<&str>, max_chars: usize) -> Value {
function clip_string (line 183) | fn clip_string(value: &str, max_chars: usize) -> String {
function nanos_to_millis (line 197) | fn nanos_to_millis(nanos: i64) -> f64 {
function value_type (line 201) | fn value_type(value: &Value) -> &'static str {
function redacts_sensitive_keys_recursively (line 217) | fn redacts_sensitive_keys_recursively() {
function clips_long_strings_deterministically (line 233) | fn clips_long_strings_deterministically() {
function malformed_json_preview_keeps_parse_error (line 240) | fn malformed_json_preview_keeps_parse_error() {
FILE: tools/stove-cli/src/mcp/args.rs
constant DEFAULT_LIMIT (line 6) | const DEFAULT_LIMIT: usize = 20;
constant MAX_LIMIT (line 7) | const MAX_LIMIT: usize = 100;
type CommonArgs (line 10) | pub(crate) struct CommonArgs {
method limit (line 17) | pub(crate) fn limit(&self) -> usize {
type ListArgs (line 23) | pub(crate) struct ListArgs {
method limit (line 29) | pub(crate) fn limit(&self) -> usize {
type RunsArgs (line 35) | pub(crate) struct RunsArgs {
type FailuresArgs (line 43) | pub(crate) struct FailuresArgs {
type ExactTestArgs (line 51) | pub(crate) struct ExactTestArgs {
type TimelineArgs (line 59) | pub(crate) struct TimelineArgs {
type TraceArgs (line 66) | pub(crate) struct TraceArgs {
type SnapshotArgs (line 76) | pub(crate) struct SnapshotArgs {
type RawEvidenceArgs (line 84) | pub(crate) struct RawEvidenceArgs {
type Budget (line 95) | pub(crate) struct Budget {
method from_args (line 105) | pub(crate) fn from_args(name: Option<&str>, max_chars: Option<usize>) ...
function parse (line 146) | pub(crate) fn parse<T>(arguments: Value) -> Result<T, String>
FILE: tools/stove-cli/src/mcp/contract.rs
type MethodName (line 2) | pub(crate) enum MethodName {
method from_str (line 10) | pub(crate) fn from_str(value: &str) -> Option<Self> {
type ToolName (line 22) | pub(crate) enum ToolName {
constant ALL (line 34) | pub(crate) const ALL: [Self; 8] = [
method as_str (line 45) | pub(crate) const fn as_str(self) -> &'static str {
method from_str (line 58) | pub(crate) fn from_str(value: &str) -> Option<Self> {
type ArgName (line 64) | pub(crate) enum ArgName {
method as_str (line 82) | pub(crate) const fn as_str(self) -> &'static str {
type BudgetValue (line 103) | pub(crate) enum BudgetValue {
constant ALL (line 110) | pub(crate) const ALL: [Self; 3] = [Self::Tiny, Self::Compact, Self::Fu...
method as_str (line 112) | pub(crate) const fn as_str(self) -> &'static str {
method from_str (line 120) | pub(crate) fn from_str(value: &str) -> Option<Self> {
type TimelineFocus (line 128) | pub(crate) enum TimelineFocus {
constant ALL (line 134) | pub(crate) const ALL: [Self; 2] = [Self::Failure, Self::All];
method as_str (line 136) | pub(crate) const fn as_str(self) -> &'static str {
type TraceView (line 145) | pub(crate) enum TraceView {
constant ALL (line 152) | pub(crate) const ALL: [Self; 3] = [Self::CriticalPath, Self::Exception...
method as_str (line 154) | pub(crate) const fn as_str(self) -> &'static str {
type RawEvidenceKind (line 164) | pub(crate) enum RawEvidenceKind {
constant ALL (line 171) | pub(crate) const ALL: [Self; 3] = [Self::Entry, Self::Span, Self::Snap...
method as_str (line 173) | pub(crate) const fn as_str(self) -> &'static str {
method from_str (line 181) | pub(crate) fn from_str(value: &str) -> Option<Self> {
type RunStatusValue (line 187) | pub(crate) enum RunStatusValue {
constant ALL (line 194) | pub(crate) const ALL: [Self; 3] = [Self::Running, Self::Passed, Self::...
method as_str (line 196) | pub(crate) const fn as_str(self) -> &'static str {
constant STATUS_ERROR (line 205) | pub(crate) const STATUS_ERROR: &str = "ERROR";
FILE: tools/stove-cli/src/mcp/mod.rs
function handle_get (line 22) | pub async fn handle_get(
function handle_post (line 40) | pub async fn handle_post(
function handle_request (line 78) | async fn handle_request(state: AppState, request: JsonRpcRequest) -> Res...
FILE: tools/stove-cli/src/mcp/protocol.rs
constant PROTOCOL_VERSION (line 9) | const PROTOCOL_VERSION: &str = "2025-06-18";
function initialize_result (line 11) | pub(crate) fn initialize_result() -> Value {
function tool_result (line 28) | pub(crate) fn tool_result(output: ToolOutput) -> Value {
function rpc_result (line 42) | pub(crate) fn rpc_result(id: Option<Value>, result: Value) -> Response {
function rpc_error (line 50) | pub(crate) fn rpc_error(
type JsonRpcRequest (line 77) | pub(crate) struct JsonRpcRequest {
type ToolCallParams (line 84) | pub(crate) struct ToolCallParams {
type RpcError (line 89) | pub(crate) struct RpcError {
method invalid_params (line 96) | pub(crate) fn invalid_params(message: String) -> Self {
method method_not_found (line 104) | pub(crate) fn method_not_found(message: String) -> Self {
method tool_error (line 112) | pub(crate) fn tool_error(message: String) -> Self {
FILE: tools/stove-cli/src/mcp/security.rs
function validate_accept_header (line 9) | pub(crate) fn validate_accept_header(headers: &HeaderMap) -> Option<Resp...
function validate_local_request (line 28) | pub(crate) fn validate_local_request(
function forbidden (line 54) | fn forbidden(message: &str) -> Response {
function host_without_port (line 64) | fn host_without_port(host: &str) -> Option<String> {
function origin_host (line 75) | fn origin_host(origin: &str) -> Option<String> {
function is_loopback_host (line 81) | fn is_loopback_host(host: &str) -> bool {
function host_parser_handles_ipv6_loopback (line 93) | fn host_parser_handles_ipv6_loopback() {
function origin_parser_rejects_remote_hosts (line 99) | fn origin_parser_rejects_remote_hosts() {
FILE: tools/stove-cli/src/mcp/tools.rs
function definitions (line 7) | pub(crate) fn definitions() -> Value {
type ToolSpec (line 17) | struct ToolSpec {
method for_tool (line 24) | fn for_tool(tool: ToolName) -> Self {
method to_json (line 111) | fn to_json(&self) -> Value {
type InputSchema (line 120) | struct InputSchema {
method from_fields (line 125) | fn from_fields(fields: &[FieldSpec]) -> Self {
method to_json (line 131) | fn to_json(&self) -> Value {
type FieldSpec (line 150) | struct FieldSpec {
method string (line 159) | fn string(name: ArgName) -> Self {
method string_enum (line 163) | fn string_enum(name: ArgName, enum_kind: SchemaEnum) -> Self {
method integer (line 167) | fn integer(name: ArgName) -> Self {
method limit (line 171) | fn limit() -> Self {
method budget (line 178) | fn budget() -> Self {
method max_chars (line 183) | fn max_chars() -> Self {
method new (line 190) | fn new(name: ArgName, kind: FieldKind) -> Self {
method required (line 200) | fn required(mut self) -> Self {
method description (line 205) | fn description(mut self, description: &'static str) -> Self {
method string_default (line 210) | fn string_default(mut self, default: &'static str) -> Self {
method integer_default (line 215) | fn integer_default(mut self, default: i64) -> Self {
method minimum (line 220) | fn minimum(mut self, minimum: i64) -> Self {
method maximum (line 232) | fn maximum(mut self, maximum: i64) -> Self {
method property_entry (line 244) | fn property_entry(&self) -> (String, Value) {
type FieldKind (line 275) | enum FieldKind {
type SchemaEnum (line 286) | enum SchemaEnum {
method values (line 295) | fn values(self) -> Vec<&'static str> {
function list_fields (line 318) | fn list_fields() -> Vec<FieldSpec> {
function exact_test_fields (line 326) | fn exact_test_fields() -> Vec<FieldSpec> {
function with_extra (line 339) | fn with_extra(mut fields: Vec<FieldSpec>, field: FieldSpec) -> Vec<Field...
function definitions_include_one_schema_per_tool (line 349) | fn definitions_include_one_schema_per_tool() {
function exact_test_tools_require_run_and_test_ids (line 362) | fn exact_test_tools_require_run_and_test_ids() {
function typed_fields_preserve_defaults_and_enums (line 371) | fn typed_fields_preserve_defaults_and_enums() {
function raw_evidence_schema_requires_kind_and_id (line 389) | fn raw_evidence_schema_requires_kind_and_id() {
FILE: tools/stove-cli/src/skills.rs
constant REPO_OWNER (line 26) | pub const REPO_OWNER: &str = "Trendyol";
constant REPO_NAME (line 27) | pub const REPO_NAME: &str = "stove";
constant REPO_REF (line 28) | pub const REPO_REF: &str = "main";
constant USER_AGENT (line 29) | pub const USER_AGENT: &str = concat!("stove-cli/", env!("STOVE_VERSION"));
constant REQUEST_TIMEOUT (line 31) | pub const REQUEST_TIMEOUT: Duration = Duration::from_secs(5);
constant SKILL_PATHS (line 37) | const SKILL_PATHS: &[&str] = &[
function handle_skills_command (line 47) | pub async fn handle_skills_command(config: &Config) -> anyhow::Result<bo...
function maybe_update_skills (line 60) | pub async fn maybe_update_skills(config: &Config) {
type SyncAction (line 92) | enum SyncAction {
function decide_sync_action (line 101) | async fn decide_sync_action(target: &Path, force_update: bool) -> SyncAc...
function apply_install (line 118) | fn apply_install(target: &Path, remote: &RemoteSkills) {
function install_skills_command (line 130) | async fn install_skills_command(force: bool) -> anyhow::Result<()> {
function current_git_root (line 157) | fn current_git_root() -> Option<PathBuf> {
function find_git_root (line 165) | pub fn find_git_root(start: &Path) -> Option<PathBuf> {
function resolve_local_target (line 177) | pub fn resolve_local_target(root: &Path) -> PathBuf {
type RemoteSkills (line 187) | pub struct RemoteSkills {
method is_empty (line 193) | pub fn is_empty(&self) -> bool {
method len (line 198) | pub fn len(&self) -> usize {
method iter (line 202) | pub fn iter(&self) -> impl Iterator<Item = (&String, &Vec<u8>)> {
type ContentsEntry (line 208) | struct ContentsEntry {
function fetch_remote_skills (line 219) | async fn fetch_remote_skills() -> anyhow::Result<RemoteSkills> {
function fetch_remote_skills_for_path (line 235) | async fn fetch_remote_skills_for_path(
function skills_match (line 288) | pub fn skills_match(target: &Path, remote: &RemoteSkills) -> bool {
function read_local_files (line 295) | fn read_local_files(target: &Path) -> Option<BTreeMap<String, Vec<u8>>> {
function install_skills (line 316) | pub fn install_skills(target: &Path, remote: &RemoteSkills) -> anyhow::R...
function write_staging (line 334) | fn write_staging(staging: &Path, remote: &RemoteSkills) -> anyhow::Resul...
function swap_into_place (line 344) | fn swap_into_place(staging: &Path, target: &Path, aside: &Path) -> anyho...
function prompt_yes_no (line 367) | fn prompt_yes_no(message: &str) -> anyhow::Result<bool> {
function remote_with (line 384) | fn remote_with(files: &[(&str, &[u8])]) -> RemoteSkills {
function find_git_root_detects_directory (line 394) | fn find_git_root_detects_directory() {
function find_git_root_detects_file_marker (line 405) | fn find_git_root_detects_file_marker() {
function find_git_root_returns_none_when_absent (line 416) | fn find_git_root_returns_none_when_absent() {
function resolve_local_target_prefers_existing_agents (line 424) | fn resolve_local_target_prefers_existing_agents() {
function resolve_local_target_falls_back_to_claude (line 435) | fn resolve_local_target_falls_back_to_claude() {
function resolve_local_target_defaults_to_agents_when_none_exist (line 445) | fn resolve_local_target_defaults_to_agents_when_none_exist() {
function skills_match_detects_missing_target (line 452) | fn skills_match_detects_missing_target() {
function skills_match_returns_true_for_identical_dirs (line 460) | fn skills_match_returns_true_for_identical_dirs() {
function skills_match_detects_content_drift (line 472) | fn skills_match_detects_content_drift() {
function skills_match_detects_extra_local_file (line 482) | fn skills_match_detects_extra_local_file() {
function install_skills_creates_target_when_missing (line 493) | fn install_skills_creates_target_when_missing() {
function install_skills_replaces_existing_target (line 505) | fn install_skills_replaces_existing_target() {
function install_skills_cleans_up_staging_artifacts (line 521) | fn install_skills_cleans_up_staging_artifacts() {
FILE: tools/stove-cli/src/sse/manager.rs
type SseManager (line 7) | pub struct SseManager {
method new (line 13) | pub fn new() -> Self {
method broadcast (line 21) | pub fn broadcast(&self, json: &str) {
method subscribe (line 29) | pub fn subscribe(&self) -> broadcast::Receiver<String> {
method default (line 35) | fn default() -> Self {
FILE: tools/stove-cli/src/storage/database.rs
constant MIGRATIONS (line 9) | const MIGRATIONS: &[(&str, &str)] = &[
type Database (line 25) | pub struct Database {
method open (line 35) | pub fn open(path: &str) -> Result<Self> {
method conn (line 50) | pub fn conn(&self) -> &Connection {
method conn_mut (line 55) | pub fn conn_mut(&mut self) -> &mut Connection {
method open_peer (line 63) | pub fn open_peer(&self) -> Result<Self> {
function run_migrations (line 78) | fn run_migrations(conn: &Connection) -> Result<()> {
function normalize_db_path (line 114) | fn normalize_db_path(path: &str) -> (String, bool) {
function open_connection (line 126) | fn open_connection(path: &str, use_uri: bool) -> Result<Connection> {
function apply_pragmas (line 140) | fn apply_pragmas(conn: &Connection, path: &str) -> Result<()> {
function open_in_memory_succeeds_and_creates_tables (line 157) | fn open_in_memory_succeeds_and_creates_tables() {
function migrations_are_idempotent (line 177) | fn migrations_are_idempotent() {
function open_upgrades_v1_database_with_run_stove_version_column (line 193) | fn open_upgrades_v1_database_with_run_stove_version_column() {
FILE: tools/stove-cli/src/storage/migrations/V1__initial_schema.sql
type runs (line 1) | CREATE TABLE IF NOT EXISTS runs (
type tests (line 14) | CREATE TABLE IF NOT EXISTS tests (
type entries (line 28) | CREATE TABLE IF NOT EXISTS entries (
type spans (line 46) | CREATE TABLE IF NOT EXISTS spans (
type snapshots (line 64) | CREATE TABLE IF NOT EXISTS snapshots (
type idx_tests_run_id (line 74) | CREATE INDEX IF NOT EXISTS idx_tests_run_id ON tests(run_id)
type idx_entries_run_test (line 75) | CREATE INDEX IF NOT EXISTS idx_entries_run_test ON entries(run_id, test_id)
type idx_spans_run_id (line 76) | CREATE INDEX IF NOT EXISTS idx_spans_run_id ON spans(run_id)
type idx_spans_trace_id (line 77) | CREATE INDEX IF NOT EXISTS idx_spans_trace_id ON spans(trace_id)
type idx_snapshots_run_test (line 78) | CREATE INDEX IF NOT EXISTS idx_snapshots_run_test ON snapshots(run_id, t...
type idx_runs_app_name (line 79) | CREATE INDEX IF NOT EXISTS idx_runs_app_name ON runs(app_name)
FILE: tools/stove-cli/src/storage/models.rs
type RunStatus (line 8) | pub enum RunStatus {
method fmt (line 31) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Err (line 18) | type Err = String;
method from_str (line 20) | fn from_str(s: &str) -> Result<Self, Self::Err> {
type TestStatus (line 42) | pub enum TestStatus {
method fmt (line 68) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Err (line 54) | type Err = String;
method from_str (line 56) | fn from_str(s: &str) -> Result<Self, Self::Err> {
type AppSummary (line 80) | pub struct AppSummary {
type Run (line 90) | pub struct Run {
type Test (line 106) | pub struct Test {
type Entry (line 121) | pub struct Entry {
type Span (line 140) | pub struct Span {
type Snapshot (line 159) | pub struct Snapshot {
type NewEntry (line 172) | pub struct NewEntry {
type NewSpan (line 190) | pub struct NewSpan {
FILE: tools/stove-cli/src/storage/repository.rs
type Repository (line 15) | pub struct Repository {
method new (line 21) | pub fn new(db: Database) -> Self {
method lock_write_db (line 31) | fn lock_write_db(&self) -> MutexGuard<'_, Database> {
method lock_read_db (line 35) | fn lock_read_db(&self) -> MutexGuard<'_, Database> {
method save_run_start (line 41) | pub fn save_run_start(
method save_run_start_with_version (line 51) | pub fn save_run_start_with_version(
method save_run_end (line 71) | pub fn save_run_end(
method save_test_start (line 93) | pub fn save_test_start(
method save_test_end (line 115) | pub fn save_test_end(
method save_entry (line 137) | pub fn save_entry(&self, entry: &NewEntry) -> Result<()> {
method save_span (line 143) | pub fn save_span(&self, span: &NewSpan) -> Result<()> {
method save_snapshot (line 149) | pub fn save_snapshot(
method clear_all (line 162) | pub fn clear_all(&self) -> Result<()> {
method apply_persisted_events (line 170) | pub fn apply_persisted_events(&self, events: &[PersistedDashboardEvent...
method get_apps (line 182) | pub fn get_apps(&self) -> Result<Vec<AppSummary>> {
method get_runs (line 211) | pub fn get_runs(&self, app_name: Option<&str>) -> Result<Vec<Run>> {
method get_run (line 227) | pub fn get_run(&self, run_id: &str) -> Result<Option<Run>> {
method get_tests_for_run (line 235) | pub fn get_tests_for_run(&self, run_id: &str) -> Result<Vec<Test>> {
method get_entries (line 247) | pub fn get_entries(&self, run_id: &str, test_id: &str) -> Result<Vec<E...
method get_spans_for_test (line 259) | pub fn get_spans_for_test(&self, run_id: &str, test_id: &str) -> Resul...
method get_trace (line 283) | pub fn get_trace(&self, trace_id: &str) -> Result<Vec<Span>> {
method get_snapshots (line 295) | pub fn get_snapshots(&self, run_id: &str, test_id: &str) -> Result<Vec...
function apply_persisted_event (line 308) | fn apply_persisted_event(
function save_run_start_on (line 381) | fn save_run_start_on(
function save_run_end_on (line 397) | fn save_run_end_on(
function save_test_start_on (line 418) | fn save_test_start_on(
function save_test_end_on (line 435) | fn save_test_end_on(
function save_entry_on (line 451) | fn save_entry_on(conn: &rusqlite::Connection, entry: &NewEntry) -> Resul...
function save_span_on (line 473) | fn save_span_on(conn: &rusqlite::Connection, span: &NewSpan) -> Result<(...
function save_snapshot_on (line 495) | fn save_snapshot_on(
constant RUN_COLUMNS (line 512) | const RUN_COLUMNS: &str = "id, app_name, started_at, ended_at, status, t...
constant SPAN_COLUMNS (line 513) | const SPAN_COLUMNS: &str = "id, run_id, trace_id, span_id, parent_span_i...
function non_empty (line 518) | fn non_empty(s: &str) -> Option<&str> {
function parse_run_status (line 523) | fn parse_run_status(s: &str) -> RunStatus {
function parse_test_status (line 528) | fn parse_test_status(s: &str) -> TestStatus {
function run_from_row (line 532) | fn run_from_row(row: &rusqlite::Row<'_>) -> rusqlite::Result<Run> {
function test_from_row (line 550) | fn test_from_row(row: &rusqlite::Row<'_>) -> rusqlite::Result<Test> {
function entry_from_row (line 567) | fn entry_from_row(row: &rusqlite::Row<'_>) -> rusqlite::Result<Entry> {
function snapshot_from_row (line 586) | fn snapshot_from_row(row: &rusqlite::Row<'_>) -> rusqlite::Result<Snapsh...
function span_from_row (line 597) | fn span_from_row(row: &rusqlite::Row<'_>) -> rusqlite::Result<Span> {
function test_repo (line 621) | fn test_repo() -> Repository {
function full_event_lifecycle (line 626) | fn full_event_lifecycle() {
function latest_app_version_comes_from_latest_run (line 745) | fn latest_app_version_comes_from_latest_run() {
function get_runs_filters_by_app_name (line 774) | fn get_runs_filters_by_app_name() {
function clear_all_removes_everything (line 792) | fn clear_all_removes_everything() {
function get_apps_returns_single_latest_run_when_started_at_ties (line 808) | fn get_apps_returns_single_latest_run_when_started_at_ties() {
function get_runs_orders_same_timestamp_runs_by_latest_inserted_first (line 826) | fn get_runs_orders_same_timestamp_runs_by_latest_inserted_first() {
function get_spans_for_test_does_not_cross_match_similar_test_ids (line 843) | fn get_spans_for_test_does_not_cross_match_similar_test_ids() {
FILE: tools/stove-cli/tests/api_e2e.rs
function ts (line 21) | fn ts(seconds: i64, nanos: i32) -> Option<prost_types::Timestamp> {
function run_started_event (line 25) | fn run_started_event(
function run_started_event_with_version (line 34) | fn run_started_event_with_version(
function run_ended_event (line 54) | fn run_ended_event(
function test_started_event (line 77) | fn test_started_event(
function test_ended_event (line 99) | fn test_ended_event(
function entry_recorded_event (line 122) | fn entry_recorded_event(
function span_recorded_event (line 152) | fn span_recorded_event(
function snapshot_event (line 181) | fn snapshot_event(
function send_event (line 201) | async fn send_event(
function flush_events (line 210) | async fn flush_events(service: &DashboardEventServiceImpl) {
function extract_sse_data_frame (line 217) | fn extract_sse_data_frame(frame: &str) -> Option<String> {
function next_sse_data (line 230) | async fn next_sse_data(
function meta_returns_cli_version (line 254) | async fn meta_returns_cli_version() {
function apps_returns_empty_when_no_data (line 270) | async fn apps_returns_empty_when_no_data() {
function apps_returns_app_summaries (line 278) | async fn apps_returns_app_summaries() {
function apps_returns_multiple_apps (line 293) | async fn apps_returns_multiple_apps() {
function apps_return_latest_run_stove_version (line 311) | async fn apps_return_latest_run_stove_version() {
function runs_returns_all_runs (line 340) | async fn runs_returns_all_runs() {
function runs_return_stove_version (line 363) | async fn runs_return_stove_version() {
function runs_filters_by_app_name (line 374) | async fn runs_filters_by_app_name() {
function runs_returns_empty_for_unknown_app (line 386) | async fn runs_returns_empty_for_unknown_app() {
function get_run_returns_single_run (line 399) | async fn get_run_returns_single_run() {
function get_run_returns_null_for_unknown_id (line 411) | async fn get_run_returns_null_for_unknown_id() {
function get_tests_returns_tests_for_run (line 423) | async fn get_tests_returns_tests_for_run() {
function get_tests_returns_empty_for_unknown_run (line 443) | async fn get_tests_returns_empty_for_unknown_run() {
function concurrent_running_tests_are_visible_via_api_while_run_is_in_progress (line 451) | async fn concurrent_running_tests_are_visible_via_api_while_run_is_in_pr...
function concurrent_interleaved_test_lifecycle_remains_isolated_across_api_views (line 568) | async fn concurrent_interleaved_test_lifecycle_remains_isolated_across_a...
function get_entries_returns_entries_for_test (line 819) | async fn get_entries_returns_entries_for_test() {
function get_entries_isolates_by_test_id (line 835) | async fn get_entries_isolates_by_test_id() {
function get_entries_returns_empty_for_unknown_test (line 848) | async fn get_entries_returns_empty_for_unknown_test() {
function get_test_spans_returns_spans_linked_via_trace_id (line 863) | async fn get_test_spans_returns_spans_linked_via_trace_id() {
function get_test_spans_returns_empty_when_no_trace_linked (line 877) | async fn get_test_spans_returns_empty_when_no_trace_linked() {
function get_test_spans_returns_spans_linked_via_attribute (line 887) | async fn get_test_spans_returns_spans_linked_via_attribute() {
function get_test_spans_does_not_cross_match_similar_test_ids_in_attributes (line 927) | async fn get_test_spans_does_not_cross_match_similar_test_ids_in_attribu...
function get_test_spans_combines_entry_and_attribute_linked_traces (line 955) | async fn get_test_spans_combines_entry_and_attribute_linked_traces() {
function get_test_spans_returns_full_trace_when_one_span_has_attribute (line 1011) | async fn get_test_spans_returns_full_trace_when_one_span_has_attribute() {
function get_test_spans_ordered_by_start_time (line 1062) | async fn get_test_spans_ordered_by_start_time() {
function get_test_spans_includes_exception_data (line 1124) | async fn get_test_spans_includes_exception_data() {
function get_test_spans_isolates_between_tests (line 1173) | async fn get_test_spans_isolates_between_tests() {
function get_snapshots_returns_snapshots_for_test (line 1204) | async fn get_snapshots_returns_snapshots_for_test() {
function get_snapshots_returns_empty_for_test_without_snapshots (line 1220) | async fn get_snapshots_returns_empty_for_test_without_snapshots() {
function get_snapshots_returns_multiple_systems (line 1229) | async fn get_snapshots_returns_multiple_systems() {
function get_snapshots_isolates_between_tests (line 1271) | async fn get_snapshots_isolates_between_tests() {
function snapshot_state_json_preserves_complex_json (line 1294) | async fn snapshot_state_json_preserves_complex_json() {
function get_trace_returns_all_spans_for_trace (line 1314) | async fn get_trace_returns_all_spans_for_trace() {
function get_trace_returns_empty_for_unknown_trace (line 1330) | async fn get_trace_returns_empty_for_unknown_trace() {
function sse_endpoint_returns_200_with_event_stream_content_type (line 1342) | async fn sse_endpoint_returns_200_with_event_stream_content_type() {
function sse_stream_pushes_full_events_before_database_flush (line 1360) | async fn sse_stream_pushes_full_events_before_database_flush() {
function sse_broadcast_data_is_readable_after_notification (line 1427) | async fn sse_broadcast_data_is_readable_after_notification() {
function sse_stream_sends_keep_alive (line 1461) | async fn sse_stream_sends_keep_alive() {
function sse_stream_delivers_interleaved_notifications_for_concurrent_test_load (line 1485) | async fn sse_stream_delivers_interleaved_notifications_for_concurrent_te...
function cors_headers_are_present (line 1675) | async fn cors_headers_are_present() {
function in_progress_run_has_running_status (line 1690) | async fn in_progress_run_has_running_status() {
function spa_fallback_serves_index_html_for_unknown_paths (line 1708) | async fn spa_fallback_serves_index_html_for_unknown_paths() {
function missing_spa_assets_return_404_instead_of_index_html (line 1726) | async fn missing_spa_assets_return_404_instead_of_index_html() {
function runs_are_ordered_by_started_at_desc (line 1739) | async fn runs_are_ordered_by_started_at_desc() {
function runs_with_same_started_at_use_latest_inserted_as_tie_breaker (line 1752) | async fn runs_with_same_started_at_use_latest_inserted_as_tie_breaker() {
function apps_returns_latest_run_id_for_multi_run_app (line 1766) | async fn apps_returns_latest_run_id_for_multi_run_app() {
function apps_does_not_duplicate_app_when_latest_runs_share_same_timestamp (line 1779) | async fn apps_does_not_duplicate_app_when_latest_runs_share_same_timesta...
FILE: tools/stove-cli/tests/common/mod.rs
type TestServer (line 20) | pub struct TestServer {
method start (line 29) | pub async fn start() -> Self {
method start_with_ingestor (line 59) | pub async fn start_with_ingestor() -> (Self, EventIngestor) {
method url (line 95) | pub fn url(&self, path: &str) -> String {
method mcp_url (line 99) | pub fn mcp_url(&self) -> String {
method get (line 103) | pub async fn get(&self, path: &str) -> reqwest::Response {
method get_json (line 112) | pub async fn get_json(&self, path: &str) -> Value {
method seed_run (line 127) | pub fn seed_run(&self, run_id: &str, app_name: &str) {
method seed_run_with_version (line 131) | pub fn seed_run_with_version(&self, run_id: &str, app_name: &str, stov...
method seed_run_at (line 142) | pub fn seed_run_at(&self, run_id: &str, app_name: &str, started_at: &s...
method seed_run_at_with_version (line 146) | pub fn seed_run_at_with_version(
method end_run (line 162) | pub fn end_run(&self, run_id: &str, passed: i32, failed: i32, duration...
method seed_test (line 177) | pub fn seed_test(&self, run_id: &str, test_id: &str, name: &str, spec:...
method end_test (line 185) | pub fn end_test(&self, run_id: &str, test_id: &str, duration_ms: i64) {
method end_test_failed (line 200) | pub fn end_test_failed(&self, run_id: &str, test_id: &str, duration_ms...
method seed_entry (line 215) | pub fn seed_entry(&self, run_id: &str, test_id: &str, system: &str, ac...
method seed_entry_full (line 223) | pub fn seed_entry_full(
method seed_span (line 258) | pub fn seed_span(
method seed_span_timed (line 287) | pub fn seed_span_timed(
method seed_span_with_exception (line 319) | pub fn seed_span_with_exception(
method seed_snapshot (line 353) | pub fn seed_snapshot(
method seed_full_run (line 371) | pub fn seed_full_run(&self) {
FILE: tools/stove-cli/tests/mcp_e2e.rs
function mcp_call (line 13) | async fn mcp_call(server: &TestServer, method: &str, params: Value) -> V...
function mcp_tool (line 31) | async fn mcp_tool(server: &TestServer, name: &str, arguments: Value) -> ...
function mcp_lists_tools_and_initializes (line 44) | async fn mcp_lists_tools_and_initializes() {
function failures_are_grouped_by_app_and_run_with_exact_detail_calls (line 71) | async fn failures_are_grouped_by_app_and_run_with_exact_detail_calls() {
function failure_detail_includes_timeline_trace_and_snapshot_summaries (line 92) | async fn failure_detail_includes_timeline_trace_and_snapshot_summaries() {
function mcp_handles_no_failures_and_caps_oversized_detail (line 180) | async fn mcp_handles_no_failures_and_caps_oversized_detail() {
function mcp_flushes_pending_ingest_before_reads (line 246) | async fn mcp_flushes_pending_ingest_before_reads() {
function mcp_rejects_non_local_host_headers (line 271) | async fn mcp_rejects_non_local_host_headers() {
function seed_multi_app_failures (line 290) | fn seed_multi_app_failures(server: &TestServer) {
function send_event (line 347) | async fn send_event(service: &DashboardEventServiceImpl, event: proto::D...
function timestamp (line 353) | fn timestamp() -> Option<prost_types::Timestamp> {
function run_started (line 360) | fn run_started(run_id: &str, app_name: &str) -> proto::DashboardEvent {
function test_started (line 374) | fn test_started(run_id: &str, test_id: &str) -> proto::DashboardEvent {
function test_ended_failed (line 389) | fn test_ended_failed(run_id: &str, test_id: &str) -> proto::DashboardEve...
Condensed preview — 1062 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,092K chars).
[
{
"path": ".claude/skills/stove/SKILL.md",
"chars": 15049,
"preview": "---\nname: stove\ndescription: Use when adding Stove e2e tests to a project, configuring Stove systems (HTTP, PostgreSQL, "
},
{
"path": ".claude/skills/stove/container.md",
"chars": 9460,
"preview": "# Container AUT — `stove-container`\n\nUse `stove-container` when the application under test should run as a Docker image."
},
{
"path": ".claude/skills/stove/custom-systems.md",
"chars": 4540,
"preview": "# Writing Your Own Stove System\n\n## Contents\n- [Implement PluggedSystem](#1-implement-pluggedsystem)\n- [Create a listene"
},
{
"path": ".claude/skills/stove/go-setup.md",
"chars": 18190,
"preview": "# Go Application Setup with Stove\n\nComplete guide for testing Go applications with Stove. Covers HTTP, PostgreSQL, Kafka"
},
{
"path": ".claude/skills/stove/gradle-config.md",
"chars": 5616,
"preview": "# Gradle Configuration\n\n## Contents\n- [Dependencies (BOM)](#dependencies-bom)\n- [Register test-e2e source set](#register"
},
{
"path": ".claude/skills/stove/mcp.md",
"chars": 4957,
"preview": "# Stove MCP — Agent Triage\n\nThe Stove CLI exposes a local **Model Context Protocol** endpoint at `http://localhost:4040/"
},
{
"path": ".claude/skills/stove/other-languages.md",
"chars": 10665,
"preview": "# Testing Non-JVM Applications with Stove\n\nStove can test any application that speaks HTTP, databases, and messaging ---"
},
{
"path": ".claude/skills/stove/system-setup.md",
"chars": 39432,
"preview": "# System Setup Reference\n\n## Contents\n- [Process Application (non-JVM apps)](#process-application-non-jvm-apps)\n- [Provi"
},
{
"path": ".claude/skills/stove/tracing.md",
"chars": 3164,
"preview": "# Tracing Configuration\n\nTracing captures the full execution call chain inside your application, shown on test failure. "
},
{
"path": ".claude/skills/stove/writing-tests.md",
"chars": 21251,
"preview": "# Writing Tests Reference\n\n## Contents\n- [HTTP requests](#http-requests)\n- [HTTP streaming](#http-streaming)\n- [PostgreS"
},
{
"path": ".editorconfig",
"chars": 794,
"preview": "root = true\n\n[*]\ninsert_final_newline = true\nktlint_standard_package-name = disabled\nktlint_standard_filename = disabled"
},
{
"path": ".gitattributes",
"chars": 214,
"preview": "#\n# https://help.github.com/articles/dealing-with-line-endings/\n#\n# Linux start script should use lf\n/gradlew tex"
},
{
"path": ".github/workflows/build-jvm-recipes.yml",
"chars": 2594,
"preview": "name: Build JVM Recipes\n\non:\n push:\n branches: [main]\n paths:\n - 'recipes/jvm/**'\n pull_request:\n branch"
},
{
"path": ".github/workflows/build-process-recipes.yml",
"chars": 3182,
"preview": "name: Build Process Recipes\n\non:\n push:\n branches: [main]\n paths:\n - 'recipes/process/**'\n pull_request:\n "
},
{
"path": ".github/workflows/build.yml",
"chars": 3074,
"preview": "name: Build\n\non:\n push:\n branches: [main]\n paths:\n - 'examples/**'\n - 'lib/**'\n - 'starters/**'\n "
},
{
"path": ".github/workflows/gradle-publish-release.yml",
"chars": 2152,
"preview": "name: Release\n\non:\n workflow_dispatch:\n\njobs:\n release:\n runs-on: ubuntu-latest\n if: github.repository == 'Trend"
},
{
"path": ".github/workflows/gradle-publish-snapshot.yml",
"chars": 1160,
"preview": "name: Publish to Snapshot Maven\non:\n workflow_dispatch:\njobs:\n publish:\n runs-on: ubuntu-latest\n if: github.repo"
},
{
"path": ".github/workflows/publish-to-ghpages.yml",
"chars": 669,
"preview": "name: Publish MkDocs to GitHub Pages\n\non:\n push:\n branches: [ main ]\n paths: [ 'docs/**' ]\n\njobs:\n deploy:\n r"
},
{
"path": ".github/workflows/scorecard.yml",
"chars": 1790,
"preview": "\nname: Scorecard supply-chain security\n\non:\n branch_protection_rule:\n schedule:\n - cron: '29 23 * * 3'\n push:\n "
},
{
"path": ".github/workflows/stove-cli-ci.yml",
"chars": 1675,
"preview": "name: Stove CLI CI\n\non:\n push:\n branches: [main]\n paths:\n - 'tools/stove-cli/**'\n - 'lib/stove-dashboar"
},
{
"path": ".github/workflows/stove-cli-release.yml",
"chars": 6019,
"preview": "name: Stove CLI Build\n\non:\n push:\n tags:\n - 'v*'\n workflow_dispatch:\n inputs:\n version:\n descri"
},
{
"path": ".gitignore",
"chars": 2015,
"preview": ".DS_Store\n/site\n/.idea\n.idea/shelf\n/confluence/target\n/dependencies/repo\n/android.tests.dependencies\n/dependencies/andro"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 25885,
"preview": "<h1 align=\"center\">Stove</h1>\n\n<p align=\"center\">\n End-to-end testing framework for the JVM.<br/>\n Test your applicati"
},
{
"path": "api/stove.api",
"chars": 0,
"preview": ""
},
{
"path": "build.gradle.kts",
"chars": 6764,
"preview": "import org.gradle.plugins.ide.idea.model.IdeaModel\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\n\nplugins {\n kotlin("
},
{
"path": "buildSrc/build.gradle.kts",
"chars": 104,
"preview": "plugins {\n `kotlin-dsl`\n}\n\nrepositories {\n mavenCentral()\n google()\n gradlePluginPortal()\n}\n"
},
{
"path": "buildSrc/settings.gradle.kts",
"chars": 213,
"preview": "rootProject.name = \"buildSrc\"\n\ndependencyResolutionManagement {\n versionCatalogs {\n create(\"libs\").from(files("
},
{
"path": "buildSrc/src/main/kotlin/BuildConstants.kt",
"chars": 52,
"preview": "object TestFolders {\n const val e2e = \"test-e2e\"\n}\n"
},
{
"path": "buildSrc/src/main/kotlin/CI.kt",
"chars": 840,
"preview": "import org.gradle.api.Project\n\nval Project.hasSigningKey: Boolean\n get() =\n rootProject.findProperty(\"signing.keyId\""
},
{
"path": "buildSrc/src/main/kotlin/GenerateDashboardVersionSourceTask.kt",
"chars": 889,
"preview": "import org.gradle.api.DefaultTask\nimport org.gradle.api.file.DirectoryProperty\nimport org.gradle.api.provider.Property\ni"
},
{
"path": "buildSrc/src/main/kotlin/Helpers.kt",
"chars": 1441,
"preview": "import org.gradle.api.*\nimport org.gradle.api.provider.Property\nimport org.gradle.api.provider.Provider\nimport org.gradl"
},
{
"path": "buildSrc/src/main/kotlin/com/trendyol/stove/gradle/StoveTracingConfiguration.kt",
"chars": 13652,
"preview": "@file:Suppress(\"TooManyFunctions\")\n\npackage com.trendyol.stove.gradle\n\nimport org.gradle.api.Project\nimport org.gradle.a"
},
{
"path": "buildSrc/src/main/kotlin/stove-publishing.gradle.kts",
"chars": 2704,
"preview": "plugins {\n `maven-publish`\n signing\n java\n}\nfun getProperty(\n projectKey: String,\n environmentKey: String\n): String"
},
{
"path": "codecov.yml",
"chars": 637,
"preview": "ignore:\n - \"lib/stove/src/main/kotlin/com/trendyol/stove/functional\"\n - \"lib/stove/src/main/kotlin/com/trendyol/stove/"
},
{
"path": "detekt.yml",
"chars": 16749,
"preview": "build:\n maxIssues: 0\n excludeCorrectable: false\n\nconfig:\n validation: true\n warningsAsErrors: true\n excludes: ''\n\np"
},
{
"path": "docs/Components/01-couchbase.md",
"chars": 9258,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Couchbase</span>\n\n=== \"Gradle\"\n\n ``` kotlin\n dependencies "
},
{
"path": "docs/Components/02-kafka.md",
"chars": 14653,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Kafka</span>\n\nStove supports Kafka in two ways: <span data-rn=\"highl"
},
{
"path": "docs/Components/03-elasticsearch.md",
"chars": 14510,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Elasticsearch</span>\n\n=== \"Gradle\"\n\n ``` kotlin\n dependenc"
},
{
"path": "docs/Components/04-wiremock.md",
"chars": 23223,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">WireMock</span>\n\n=== \"Gradle\"\n\n ``` kotlin\n dependencies {"
},
{
"path": "docs/Components/05-http.md",
"chars": 17220,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">HTTP Client</span>\n\n=== \"Gradle\"\n\n ``` kotlin\n dependencie"
},
{
"path": "docs/Components/06-postgresql.md",
"chars": 17828,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">PostgreSQL</span>\n\n=== \"Gradle\"\n\n ``` kotlin\n dependencies"
},
{
"path": "docs/Components/07-mongodb.md",
"chars": 17736,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">MongoDB</span>\n\n=== \"Gradle\"\n\n ``` kotlin\n dependencies {\n"
},
{
"path": "docs/Components/08-mssql.md",
"chars": 17644,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Microsoft SQL Server (MSSQL)</span>\n\n=== \"Gradle\"\n\n ``` kotlin\n "
},
{
"path": "docs/Components/09-redis.md",
"chars": 13247,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Redis</span>\n\n=== \"Gradle\"\n\n ``` kotlin\n dependencies {\n "
},
{
"path": "docs/Components/10-bridge.md",
"chars": 21416,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Bridge</span>\n\nThe Bridge component gives you <span data-rn=\"highlig"
},
{
"path": "docs/Components/11-provided-instances.md",
"chars": 28101,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Provided Instances</span> (Testcontainer-less Mode)\n\nStove supports "
},
{
"path": "docs/Components/12-grpc.md",
"chars": 10263,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">gRPC</span>\n\n=== \"Gradle\"\n\n ``` kotlin\n dependencies {\n "
},
{
"path": "docs/Components/13-reporting.md",
"chars": 12364,
"preview": "# Reporting\n\nWhen tests fail, you want to know what went wrong. Stove's reporting system <span data-rn=\"highlight\" data-"
},
{
"path": "docs/Components/14-grpc-mock.md",
"chars": 13873,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">gRPC Mock</span>\n\n`stove-grpc-mock` provides a native gRPC mock serv"
},
{
"path": "docs/Components/15-tracing.md",
"chars": 17168,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Tracing</span>\n\nYour end-to-end test just failed. Now what?\n\nYou sta"
},
{
"path": "docs/Components/16-mysql.md",
"chars": 5539,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">MySQL</span>\n\n=== \"Gradle\"\n\n ``` kotlin\n dependencies {\n "
},
{
"path": "docs/Components/17-cassandra.md",
"chars": 9738,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Cassandra</span>\n\n=== \"Gradle\"\n\n ``` kotlin\n dependencies "
},
{
"path": "docs/Components/18-dashboard.md",
"chars": 15189,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Dashboard</span>\n\nYour end-to-end tests pass. But do you *see* what "
},
{
"path": "docs/Components/19-provided-application.md",
"chars": 5605,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Provided Application</span> (Black-Box Testing)\n\nStove normally star"
},
{
"path": "docs/Components/20-multiple-systems.md",
"chars": 6668,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Multiple Systems</span>\n\nBy default, Stove registers one instance pe"
},
{
"path": "docs/Components/21-mcp.md",
"chars": 5416,
"preview": "# MCP\n\nStove CLI exposes a local MCP endpoint for AI agents. It lets agents inspect failed end-to-end tests through comp"
},
{
"path": "docs/Components/22-container.md",
"chars": 10144,
"preview": "# Container AUT (`stove-container`)\n\n`stove-container` runs the application under test as a Docker image. It works with "
},
{
"path": "docs/Components/index.md",
"chars": 13768,
"preview": "# Components\n\nStove uses a pluggable architecture. Each physical dependency is a separate module you add only when the t"
},
{
"path": "docs/assets/rough-notation.iife.js",
"chars": 10781,
"preview": "var RoughNotation=function(t){\"use strict\";const e=\"http://www.w3.org/2000/svg\";class s{constructor(t){this.seed=t}next("
},
{
"path": "docs/best-practices.md",
"chars": 20663,
"preview": "# Best Practices\n\nHere are some practices we've found helpful when writing end-to-end tests with Stove. These aren't har"
},
{
"path": "docs/blog/dashboard-0.23.0.md",
"chars": 4171,
"preview": "# Stove Dashboard in 0.23.0: See Your E2E Runs Live\n\nEnd-to-end tests usually answer one question: pass or fail. When th"
},
{
"path": "docs/blog/polyglot-0.24.0.md",
"chars": 15892,
"preview": "# Stove 0.24.0 — Going Polyglot, and an MCP for AI Triage\n\nStove started as a JVM end-to-end testing framework. Spring B"
},
{
"path": "docs/blog/tracing-0.21.0.md",
"chars": 20380,
"preview": "# Execution Tracing in Stove 0.21.0\n\nIf you've spent any time debugging e2e test failures, you know the routine. The tes"
},
{
"path": "docs/css/custom.css",
"chars": 1377,
"preview": "/* Wider content area */\n.md-grid {\n max-width: 1400px;\n margin-top: 0;\n}\n\n/* Responsive images */\nimg {\n width"
},
{
"path": "docs/frameworks/index.md",
"chars": 2559,
"preview": "# Supported Frameworks\n\nStove keeps the testing model consistent across frameworks, but application startup is framework"
},
{
"path": "docs/frameworks/ktor.md",
"chars": 4448,
"preview": "# Ktor\n\n`stove-ktor` is the starter for applications built on Ktor. Stove starts the real Ktor application and keeps the"
},
{
"path": "docs/frameworks/micronaut.md",
"chars": 1332,
"preview": "# Micronaut\n\n`stove-micronaut` is the starter for applications built on Micronaut. It uses the same Stove DSL as the oth"
},
{
"path": "docs/frameworks/quarkus.md",
"chars": 2089,
"preview": "# Quarkus\n\n`stove-quarkus` lets Stove start a Quarkus application in the same JVM as your test run, so reporting and `st"
},
{
"path": "docs/frameworks/spring-boot.md",
"chars": 2162,
"preview": "# Spring Boot\n\n`stove-spring` is the starter for applications built on Spring Boot. It supports `bridge()` for direct be"
},
{
"path": "docs/getting-started.md",
"chars": 20825,
"preview": "# Getting Started\n\nGet Stove running in your project in just a few minutes. Stove helps you write end-to-end tests by sp"
},
{
"path": "docs/index.md",
"chars": 5660,
"preview": "# Stove\n\nStove is an end-to-end testing framework for JVM applications. It boots your real application together with the"
},
{
"path": "docs/js/rough-notation-mkdocs.js",
"chars": 4152,
"preview": "/**\n * Declarative RoughNotation for MkDocs\n *\n * Standalone annotation (animates when scrolled into view):\n * <span d"
},
{
"path": "docs/other-languages/go-container.md",
"chars": 11248,
"preview": "# Go — Container Mode\n\nRun the Go application as a Docker image instead of a host binary using `stove-container` and the"
},
{
"path": "docs/other-languages/go-process.md",
"chars": 21853,
"preview": "# Go — Process Mode\n\nRun the Go binary directly as the application under test using `stove-process` and the `goApp()` DS"
},
{
"path": "docs/other-languages/go.md",
"chars": 3420,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">Go</span>\n\nStove treats Go as a <span data-rn=\"highlight\" data-rn-co"
},
{
"path": "docs/other-languages/index.md",
"chars": 6769,
"preview": "# Other Languages & Stacks\n\nStove ships with JVM framework starters (Spring Boot, Ktor, Micronaut, Quarkus), but the cor"
},
{
"path": "docs/release-notes/0.15.0.md",
"chars": 2870,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">0.15.0</span>\n\n## From 0.14.x to 0.15.x\n\n### Breaking Changes\n\nThe m"
},
{
"path": "docs/release-notes/0.19.0.md",
"chars": 9041,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">0.19.0</span>\n\n**Release Date:** December 2025\n\nThis release introdu"
},
{
"path": "docs/release-notes/0.20.0.md",
"chars": 23913,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">0.20.0</span>\n\n**Release Date:** January 2026\n\nThis release introduc"
},
{
"path": "docs/release-notes/0.21.0.md",
"chars": 11997,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">0.21.0</span>\n\n**Released:** February 2026\n\nThis release introduces:"
},
{
"path": "docs/release-notes/0.21.2.md",
"chars": 3234,
"preview": "# <span data-rn=\"underline\" data-rn-color=\"#ff9800\">0.21.2</span>\n\n**Released:** February 2026\n\nThis release introduces "
},
{
"path": "docs/release-notes/0.22.2.md",
"chars": 2580,
"preview": "# 0.22.0 – 0.22.2\n\n**Released:** March 2026\n\nThis release introduces **Quarkus support** as a new framework starter, imp"
},
{
"path": "docs/release-notes/0.23.0.md",
"chars": 4479,
"preview": "# 0.23.0\n\n**Released:** March 2026\n\n## New Features\n\n### Cassandra Support\n\nNew `stove-cassandra` module for testing app"
},
{
"path": "docs/release-notes/0.24.0.md",
"chars": 14818,
"preview": "# 0.24.0\n\n**Released:** May 2026\n\nThis release makes Stove a polyglot end-to-end testing framework and rounds out the bl"
},
{
"path": "docs/troubleshooting.md",
"chars": 19884,
"preview": "# Troubleshooting & FAQ\n\nHaving issues? This guide covers the most common problems and how to fix them. If you don't fin"
},
{
"path": "docs/writing-custom-systems.md",
"chars": 7122,
"preview": "# Writing Custom Systems\n\nStove's built-in systems cover databases, Kafka, HTTP, gRPC, and more, but your application is"
},
{
"path": "examples/build.gradle.kts",
"chars": 358,
"preview": "subprojects {\n configurations.configureEach {\n this.resolutionStrategy {\n eachDependency {\n if (requeste"
},
{
"path": "examples/ktor-example/build.gradle.kts",
"chars": 2736,
"preview": "@file:Suppress(\"UnstableApiUsage\")\n\nimport com.trendyol.stove.gradle.stoveTracing\n\nplugins {\n kotlin(\"jvm\") version lib"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/Application.kt",
"chars": 1607,
"preview": "@file:Suppress(\"ExtractKtorModule\")\n\npackage stove.ktor.example\n\nimport io.ktor.serialization.kotlinx.json.*\nimport io.k"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/app/app.kt",
"chars": 823,
"preview": "package stove.ktor.example.app\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport org.koin.dsl.*\nimport stove.kt"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/app/configuration.kt",
"chars": 2298,
"preview": "package stove.ktor.example.app\n\nimport com.sksamuel.hoplite.*\nimport com.sksamuel.hoplite.env.Environment\n\n@OptIn(Experi"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/app/database.kt",
"chars": 669,
"preview": "package stove.ktor.example.app\n\nimport io.r2dbc.postgresql.*\nimport org.koin.core.context.GlobalContext.get\nimport org.k"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/app/kafka.kt",
"chars": 2445,
"preview": "package stove.ktor.example.app\n\nimport com.fasterxml.jackson.module.kotlin.readValue\nimport org.apache.kafka.clients.con"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/app/routing.kt",
"chars": 1017,
"preview": "package stove.ktor.example.app\n\nimport io.ktor.http.*\nimport io.ktor.server.application.*\nimport io.ktor.server.request."
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/application/ExampleAppConsumer.kt",
"chars": 1197,
"preview": "package stove.ktor.example.application\n\nimport kotlinx.coroutines.*\nimport kotlinx.coroutines.flow.*\nimport org.apache.k"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/application/LockProvider.kt",
"chars": 519,
"preview": "package stove.ktor.example.application\n\nimport kotlinx.coroutines.sync.Mutex\nimport java.time.Duration\n\ninterface LockPr"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/application/ProductService.kt",
"chars": 3091,
"preview": "package stove.ktor.example.application\n\nimport org.apache.kafka.clients.producer.*\nimport stove.ktor.example.domain.*\nim"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/application/UpdateProductRequest.kt",
"chars": 181,
"preview": "package stove.ktor.example.application\n\nimport kotlinx.serialization.Serializable\n\n@Serializable\ndata class UpdateProduc"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/domain/Product.kt",
"chars": 216,
"preview": "package stove.ktor.example.domain\n\ndata class Product(\n val id: Int,\n var name: String\n)\n\nobject DomainEvents {\n data"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/domain/ProductRepository.kt",
"chars": 1534,
"preview": "package stove.ktor.example.domain\n\nimport io.r2dbc.postgresql.PostgresqlConnectionFactory\nimport io.r2dbc.postgresql.api"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/infrastructure/FeatureToggleClient.kt",
"chars": 1474,
"preview": "package stove.ktor.example.infrastructure\n\nimport io.grpc.ManagedChannel\nimport io.grpc.ManagedChannelBuilder\nimport sto"
},
{
"path": "examples/ktor-example/src/main/kotlin/stove/ktor/example/infrastructure/PricingClient.kt",
"chars": 1623,
"preview": "package stove.ktor.example.infrastructure\n\nimport io.grpc.ManagedChannel\nimport io.grpc.ManagedChannelBuilder\nimport sto"
},
{
"path": "examples/ktor-example/src/main/proto/feature_toggle.proto",
"chars": 984,
"preview": "syntax = \"proto3\";\n\npackage featuretoggle;\n\noption java_package = \"stove.ktor.example.grpc\";\noption java_multiple_files "
},
{
"path": "examples/ktor-example/src/main/proto/pricing.proto",
"chars": 1071,
"preview": "syntax = \"proto3\";\n\npackage pricing;\n\noption java_package = \"stove.ktor.example.grpc\";\noption java_multiple_files = true"
},
{
"path": "examples/ktor-example/src/main/resources/application.yaml",
"chars": 377,
"preview": "port: 8080\ndatabase:\n jdbcUrl: \"jdbc:postgresql://localhost:5432/stove\"\n host: \"localhost\"\n port: 1234\n name: \"stove"
},
{
"path": "examples/ktor-example/src/main/resources/logback.xml",
"chars": 427,
"preview": "<configuration>\n <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n <encoder>\n <"
},
{
"path": "examples/ktor-example/src/test/kotlin/com/stove/ktor/example/e2e/ExampleTest.kt",
"chars": 8699,
"preview": "package com.stove.ktor.example.e2e\n\nimport arrow.core.*\nimport com.trendyol.stove.http.http\nimport com.trendyol.stove.ka"
},
{
"path": "examples/ktor-example/src/test/kotlin/com/stove/ktor/example/e2e/StoveConfig.kt",
"chars": 3191,
"preview": "package com.stove.ktor.example.e2e\n\nimport com.trendyol.stove.dashboard.*\nimport com.trendyol.stove.extensions.kotest.St"
},
{
"path": "examples/ktor-example/src/test/kotlin/com/stove/ktor/example/e2e/TestStub.kt",
"chars": 532,
"preview": "package com.stove.ktor.example.e2e\n\nimport org.koin.core.module.Module\nimport org.koin.dsl.*\nimport stove.ktor.example.a"
},
{
"path": "examples/ktor-example/src/test/resources/kotest.properties",
"chars": 67,
"preview": "kotest.framework.config.fqn=com.stove.ktor.example.e2e.StoveConfig\n"
},
{
"path": "examples/ktor-example/src/test/resources/logback-test.xml",
"chars": 716,
"preview": "<configuration>\n\n <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n <encoder>\n "
},
{
"path": "examples/micronaut-example/build.gradle.kts",
"chars": 2353,
"preview": "import com.trendyol.stove.gradle.stoveTracing\n\nplugins {\n kotlin(\"jvm\") version libs.versions.kotlin\n kotlin(\"plugin.s"
},
{
"path": "examples/micronaut-example/src/main/kotlin/stove/micronaut/example/Application.kt",
"chars": 530,
"preview": "package stove.micronaut.example\n\nimport io.micronaut.context.ApplicationContext\nimport io.micronaut.runtime.EmbeddedAppl"
},
{
"path": "examples/micronaut-example/src/main/kotlin/stove/micronaut/example/application/domain/Product.kt",
"chars": 524,
"preview": "package stove.micronaut.example.application.domain\n\nimport io.micronaut.serde.annotation.Serdeable\nimport java.util.*\n\n@"
},
{
"path": "examples/micronaut-example/src/main/kotlin/stove/micronaut/example/application/repository/ProductRepository.kt",
"chars": 237,
"preview": "package stove.micronaut.example.application.repository\n\nimport stove.micronaut.example.application.domain.Product\n\ninter"
},
{
"path": "examples/micronaut-example/src/main/kotlin/stove/micronaut/example/application/services/ProductService.kt",
"chars": 741,
"preview": "package stove.micronaut.example.application.services\n\nimport jakarta.inject.Singleton\nimport stove.micronaut.example.app"
},
{
"path": "examples/micronaut-example/src/main/kotlin/stove/micronaut/example/application/services/SupplierService.kt",
"chars": 297,
"preview": "package stove.micronaut.example.application.services\n\nimport io.micronaut.serde.annotation.Serdeable\n\n@Serdeable\ndata cl"
},
{
"path": "examples/micronaut-example/src/main/kotlin/stove/micronaut/example/infrastructure/ObjectMapperConfig.kt",
"chars": 937,
"preview": "package stove.micronaut.example.infrastructure\n\nimport com.fasterxml.jackson.annotation.JsonInclude\nimport com.fasterxml"
},
{
"path": "examples/micronaut-example/src/main/kotlin/stove/micronaut/example/infrastructure/api/ProductController.kt",
"chars": 755,
"preview": "package stove.micronaut.example.infrastructure.api\n\nimport io.micronaut.http.annotation.*\nimport stove.micronaut.example"
},
{
"path": "examples/micronaut-example/src/main/kotlin/stove/micronaut/example/infrastructure/api/model/request/CreateProductRequest.kt",
"chars": 221,
"preview": "package stove.micronaut.example.infrastructure.api.model.request\n\nimport io.micronaut.serde.annotation.Serdeable\n\n@Serde"
},
{
"path": "examples/micronaut-example/src/main/kotlin/stove/micronaut/example/infrastructure/http/SupplierHttpService.kt",
"chars": 1018,
"preview": "package stove.micronaut.example.infrastructure.http\n\nimport io.micronaut.http.annotation.Get\nimport io.micronaut.http.cl"
},
{
"path": "examples/micronaut-example/src/main/kotlin/stove/micronaut/example/infrastructure/persistence/ProductJdbcRepository.kt",
"chars": 2509,
"preview": "package stove.micronaut.example.infrastructure.persistence\n\nimport io.r2dbc.spi.ConnectionFactory\nimport jakarta.inject."
},
{
"path": "examples/micronaut-example/src/main/kotlin/stove/micronaut/example/infrastructure/postgres/PostgresConfiguration.kt",
"chars": 330,
"preview": "package stove.micronaut.example.infrastructure.postgres\n\nimport io.micronaut.context.annotation.Factory\nimport io.r2dbc."
},
{
"path": "examples/micronaut-example/src/main/resources/application.yml",
"chars": 444,
"preview": "micronaut:\n application:\n name: micronaut-example\n server:\n port: 8080\n http:\n services:\n lookup-api:\n "
},
{
"path": "examples/micronaut-example/src/main/resources/logback.xml",
"chars": 483,
"preview": "<configuration>\n\n <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n <!-- encoders are assi"
},
{
"path": "examples/micronaut-example/src/test/kotlin/com/stove/micronaut/example/e2e/CreateProductsTableMigration.kt",
"chars": 961,
"preview": "package com.stove.micronaut.example.e2e\n\nimport com.trendyol.stove.postgres.PostgresSqlMigrationContext\nimport com.trend"
},
{
"path": "examples/micronaut-example/src/test/kotlin/com/stove/micronaut/example/e2e/ProductControllerTest.kt",
"chars": 2714,
"preview": "package com.stove.micronaut.example.e2e\n\nimport arrow.core.some\nimport com.trendyol.stove.http.http\nimport com.trendyol."
},
{
"path": "examples/micronaut-example/src/test/kotlin/com/stove/micronaut/example/e2e/StoveConfig.kt",
"chars": 2525,
"preview": "package com.stove.micronaut.example.e2e\n\nimport com.trendyol.stove.dashboard.DashboardSystemOptions\nimport com.trendyol."
},
{
"path": "examples/micronaut-example/src/test/resources/kotest.properties",
"chars": 72,
"preview": "kotest.framework.config.fqn=com.stove.micronaut.example.e2e.StoveConfig\n"
},
{
"path": "examples/micronaut-example/src/test/resources/logback-test.xml",
"chars": 716,
"preview": "<configuration>\n\n <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n <encoder>\n "
},
{
"path": "examples/quarkus-example/build.gradle.kts",
"chars": 2163,
"preview": "import com.trendyol.stove.gradle.stoveTracing\n\n@DisableCachingByDefault(because = \"Creates an empty classes directory re"
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/QuarkusMainApp.kt",
"chars": 229,
"preview": "package stove.quarkus.example\n\nimport io.quarkus.runtime.Quarkus\nimport io.quarkus.runtime.annotations.QuarkusMain\n\n@Qua"
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/StoveStartupSignal.kt",
"chars": 566,
"preview": "package stove.quarkus.example\n\nimport io.quarkus.runtime.ShutdownEvent\nimport io.quarkus.runtime.StartupEvent\nimport jak"
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/api/ProductResource.kt",
"chars": 643,
"preview": "package stove.quarkus.example.api\n\nimport jakarta.ws.rs.*\nimport jakarta.ws.rs.core.MediaType\nimport stove.quarkus.examp"
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/application/Models.kt",
"chars": 701,
"preview": "package stove.quarkus.example.application\n\ndata class ProductCreateRequest(\n val id: Long,\n val name: String,\n val su"
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/application/ProductCreator.kt",
"chars": 1037,
"preview": "package stove.quarkus.example.application\n\nimport io.opentelemetry.instrumentation.annotations.WithSpan\nimport jakarta.e"
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/infrastructure/http/SupplierHttpService.kt",
"chars": 1513,
"preview": "package stove.quarkus.example.infrastructure.http\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport io.opentele"
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/infrastructure/kafka/CustomProducerInterceptor.kt",
"chars": 884,
"preview": "package stove.quarkus.example.infrastructure.kafka\n\nimport org.apache.kafka.clients.producer.ProducerInterceptor\nimport "
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/infrastructure/kafka/KafkaClientConfiguration.kt",
"chars": 1133,
"preview": "package stove.quarkus.example.infrastructure.kafka\n\nimport io.smallrye.common.annotation.Identifier\nimport jakarta.enter"
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/infrastructure/kafka/KafkaSerde.kt",
"chars": 521,
"preview": "package stove.quarkus.example.infrastructure.kafka\n\nimport io.quarkus.kafka.client.serialization.ObjectMapperDeserialize"
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/infrastructure/kafka/ProductCommandConsumer.kt",
"chars": 739,
"preview": "package stove.quarkus.example.infrastructure.kafka\n\nimport io.opentelemetry.instrumentation.annotations.WithSpan\nimport "
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/infrastructure/kafka/ProductEventPublisher.kt",
"chars": 808,
"preview": "package stove.quarkus.example.infrastructure.kafka\n\nimport io.opentelemetry.instrumentation.annotations.WithSpan\nimport "
},
{
"path": "examples/quarkus-example/src/main/kotlin/stove/quarkus/example/infrastructure/postgres/ProductRepository.kt",
"chars": 1064,
"preview": "package stove.quarkus.example.infrastructure.postgres\n\nimport io.opentelemetry.instrumentation.annotations.WithSpan\nimpo"
},
{
"path": "examples/quarkus-example/src/main/resources/application.properties",
"chars": 1277,
"preview": "quarkus.console.enabled=false\nquarkus.class-loading.parent-first-artifacts=org.apache.kafka:kafka-clients\nquarkus.http.p"
},
{
"path": "examples/quarkus-example/src/main/resources/db/migration/V1__create_products.sql",
"chars": 126,
"preview": "CREATE TABLE IF NOT EXISTS products (\n id BIGINT PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n supplier_id BIGINT NOT NU"
},
{
"path": "examples/quarkus-example/src/test/kotlin/com/stove/quarkus/example/e2e/ExampleTest.kt",
"chars": 8226,
"preview": "package com.stove.quarkus.example.e2e\n\nimport arrow.core.some\nimport com.trendyol.stove.http.*\nimport com.trendyol.stove"
},
{
"path": "examples/quarkus-example/src/test/kotlin/com/stove/quarkus/example/e2e/StoveConfig.kt",
"chars": 3045,
"preview": "package com.stove.quarkus.example.e2e\n\nimport com.trendyol.stove.dashboard.DashboardSystemOptions\nimport com.trendyol.st"
},
{
"path": "examples/quarkus-example/src/test/resources/kotest.properties",
"chars": 70,
"preview": "kotest.framework.config.fqn=com.stove.quarkus.example.e2e.StoveConfig\n"
},
{
"path": "examples/quarkus-example/src/test/resources/logback-test.xml",
"chars": 716,
"preview": "<configuration>\n\n <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n <encoder>\n "
},
{
"path": "examples/spring-4x-example/build.gradle.kts",
"chars": 1794,
"preview": "import com.trendyol.stove.gradle.stoveTracing\n\nplugins {\n alias(libs.plugins.spring.plugin)\n alias(libs.plugins.spring"
},
{
"path": "examples/spring-4x-example/src/main/kotlin/stove/spring/example4x/ExampleApp.kt",
"chars": 853,
"preview": "package stove.spring.example4x\n\nimport org.springframework.boot.SpringApplication\nimport org.springframework.boot.autoco"
},
{
"path": "examples/spring-4x-example/src/main/kotlin/stove/spring/example4x/application/handlers/ProductCreator.kt",
"chars": 586,
"preview": "package stove.spring.example4x.application.handlers\n\nimport io.opentelemetry.instrumentation.annotations.WithSpan\nimport"
},
{
"path": "examples/spring-4x-example/src/main/kotlin/stove/spring/example4x/infrastructure/api/ProductController.kt",
"chars": 1136,
"preview": "package stove.spring.example4x.infrastructure.api\n\nimport io.opentelemetry.instrumentation.annotations.WithSpan\nimport o"
},
{
"path": "examples/spring-4x-example/src/main/kotlin/stove/spring/example4x/infrastructure/messaging/kafka/KafkaConfiguration.kt",
"chars": 2847,
"preview": "@file:Suppress(\"DEPRECATION\")\n\npackage stove.spring.example4x.infrastructure.messaging.kafka\n\nimport org.apache.kafka.cl"
},
{
"path": "examples/spring-4x-example/src/main/kotlin/stove/spring/example4x/infrastructure/messaging/kafka/KafkaProducer.kt",
"chars": 696,
"preview": "package stove.spring.example4x.infrastructure.messaging.kafka\n\nimport io.opentelemetry.instrumentation.annotations.WithS"
},
{
"path": "examples/spring-4x-example/src/main/kotlin/stove/spring/example4x/infrastructure/messaging/kafka/ProductCreateConsumer.kt",
"chars": 1598,
"preview": "package stove.spring.example4x.infrastructure.messaging.kafka\n\nimport org.slf4j.*\nimport org.springframework.kafka.annot"
},
{
"path": "examples/spring-4x-example/src/main/resources/application.yml",
"chars": 247,
"preview": "spring:\n application:\n name: \"stove-spring-4x-example\"\n\nserver:\n port: 8001\n\nkafka:\n bootstrapServers: localhost:9"
},
{
"path": "examples/spring-4x-example/src/test/kotlin/com/stove/spring/example4x/e2e/ExampleTest.kt",
"chars": 2550,
"preview": "package com.stove.spring.example4x.e2e\n\nimport arrow.core.some\nimport com.trendyol.stove.http.http\nimport com.trendyol.s"
},
{
"path": "examples/spring-4x-example/src/test/kotlin/com/stove/spring/example4x/e2e/StoveConfig.kt",
"chars": 2903,
"preview": "package com.stove.spring.example4x.e2e\n\nimport com.trendyol.stove.dashboard.DashboardSystemOptions\nimport com.trendyol.s"
},
{
"path": "examples/spring-4x-example/src/test/kotlin/com/stove/spring/example4x/e2e/jackson3.kt",
"chars": 1274,
"preview": "package com.stove.spring.example4x.e2e\n\nimport com.trendyol.stove.serialization.StoveSerde\nimport org.slf4j.LoggerFactor"
},
{
"path": "examples/spring-4x-example/src/test/resources/kotest.properties",
"chars": 71,
"preview": "kotest.framework.config.fqn=com.stove.spring.example4x.e2e.StoveConfig\n"
},
{
"path": "examples/spring-4x-example/src/test/resources/logback-test.xml",
"chars": 716,
"preview": "<configuration>\n\n <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n <encoder>\n "
},
{
"path": "examples/spring-example/build.gradle.kts",
"chars": 1862,
"preview": "import com.trendyol.stove.gradle.stoveTracing\n\nplugins {\n alias(libs.plugins.spring.plugin)\n alias(libs.plugins.spring"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/ExampleApp.kt",
"chars": 959,
"preview": "package stove.spring.example\n\nimport org.springframework.boot.SpringApplication\nimport org.springframework.boot.autoconf"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/application/handlers/ProductCreator.kt",
"chars": 2243,
"preview": "package stove.spring.example.application.handlers\n\nimport org.jetbrains.exposed.v1.jdbc.insert\nimport org.jetbrains.expo"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/application/services/SupplierService.kt",
"chars": 230,
"preview": "package stove.spring.example.application.services\n\ndata class SupplierPermission(\n val supplierId: Long,\n val isAllowe"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/domain/ProductTable.kt",
"chars": 357,
"preview": "package stove.spring.example.domain\n\nimport org.jetbrains.exposed.v1.core.Table\nimport org.jetbrains.exposed.v1.javatime"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/Constants.kt",
"chars": 1075,
"preview": "package stove.spring.example.infrastructure\n\nimport org.slf4j.MDC\nimport java.net.InetAddress\nimport java.net.UnknownHos"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/ObjectMapperConfig.kt",
"chars": 1041,
"preview": "package stove.spring.example.infrastructure\n\nimport com.fasterxml.jackson.annotation.JsonInclude\nimport com.fasterxml.ja"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/api/ProductController.kt",
"chars": 1587,
"preview": "package stove.spring.example.infrastructure.api\n\nimport kotlinx.coroutines.reactive.*\nimport kotlinx.coroutines.reactor."
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/http/SupplierHttpService.kt",
"chars": 568,
"preview": "package stove.spring.example.infrastructure.http\n\nimport io.ktor.client.*\nimport io.ktor.client.call.*\nimport io.ktor.cl"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/http/WebClientConfiguration.kt",
"chars": 1249,
"preview": "package stove.spring.example.infrastructure.http\n\nimport io.ktor.client.*\nimport io.ktor.client.engine.okhttp.*\nimport i"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/http/WebClientConfigurationProperties.kt",
"chars": 483,
"preview": "package stove.spring.example.infrastructure.http\n\nimport org.springframework.boot.context.properties.ConfigurationProper"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/KafkaProducer.kt",
"chars": 1091,
"preview": "package stove.spring.example.infrastructure.messaging.kafka\n\nimport kotlinx.coroutines.future.await\nimport org.apache.ka"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/configuration/ConsumerSettings.kt",
"chars": 3526,
"preview": "package stove.spring.example.infrastructure.messaging.kafka.configuration\n\nimport org.apache.kafka.clients.consumer.Cons"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/configuration/KafkaConsumerConfiguration.kt",
"chars": 2282,
"preview": "package stove.spring.example.infrastructure.messaging.kafka.configuration\n\nimport org.springframework.context.annotation"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/configuration/KafkaProducerConfiguration.kt",
"chars": 1782,
"preview": "package stove.spring.example.infrastructure.messaging.kafka.configuration\n\nimport org.apache.kafka.clients.producer.Prod"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/configuration/KafkaProperties.kt",
"chars": 784,
"preview": "package stove.spring.example.infrastructure.messaging.kafka.configuration\n\nimport org.springframework.boot.context.prope"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/configuration/MapBasedSettings.kt",
"chars": 141,
"preview": "package stove.spring.example.infrastructure.messaging.kafka.configuration\n\ninterface MapBasedSettings {\n fun settings()"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/configuration/ProducerSettings.kt",
"chars": 1645,
"preview": "package stove.spring.example.infrastructure.messaging.kafka.configuration\n\nimport org.apache.kafka.clients.producer.Prod"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/consumers/FailingProductCreateConsumer.kt",
"chars": 1257,
"preview": "package stove.spring.example.infrastructure.messaging.kafka.consumers\n\nimport kotlinx.coroutines.runBlocking\nimport kotl"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/consumers/JobTopicConfig.kt",
"chars": 824,
"preview": "package stove.spring.example.infrastructure.messaging.kafka.consumers\n\nimport org.springframework.boot.context.propertie"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/consumers/ProductCreateConsumers.kt",
"chars": 1774,
"preview": "package stove.spring.example.infrastructure.messaging.kafka.consumers\n\nimport com.fasterxml.jackson.databind.ObjectMappe"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/interceptors/CustomConsumerInterceptor.kt",
"chars": 1016,
"preview": "package stove.spring.example.infrastructure.messaging.kafka.interceptors\n\nimport org.apache.kafka.clients.consumer.*\nimp"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/messaging/kafka/interceptors/CustomProducerInterceptor.kt",
"chars": 1559,
"preview": "package stove.spring.example.infrastructure.messaging.kafka.interceptors\n\nimport org.apache.kafka.clients.producer.Produ"
},
{
"path": "examples/spring-example/src/main/kotlin/stove/spring/example/infrastructure/postgres/ExposedConfiguration.kt",
"chars": 819,
"preview": "package stove.spring.example.infrastructure.postgres\n\nimport com.zaxxer.hikari.HikariDataSource\nimport org.jetbrains.exp"
},
{
"path": "examples/spring-example/src/main/resources/application.yml",
"chars": 1521,
"preview": "spring:\n application:\n name: \"stove\"\n servlet:\n multipart:\n max-request-size: 10MB\n datasource:\n url: j"
},
{
"path": "examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/CreateProductsTableMigration.kt",
"chars": 899,
"preview": "package com.stove.spring.example.e2e\n\nimport com.trendyol.stove.postgres.PostgresSqlMigrationContext\nimport com.trendyol"
},
{
"path": "examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/ExampleTest.kt",
"chars": 7462,
"preview": "package com.stove.spring.example.e2e\n\nimport arrow.core.some\nimport com.trendyol.stove.http.*\nimport com.trendyol.stove."
},
{
"path": "examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/StoveConfig.kt",
"chars": 3176,
"preview": "package com.stove.spring.example.e2e\n\nimport com.trendyol.stove.dashboard.*\nimport com.trendyol.stove.extensions.kotest."
},
{
"path": "examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/TestSystemInitializer.kt",
"chars": 566,
"preview": "package com.stove.spring.example.e2e\n\nimport com.trendyol.stove.kafka.TestSystemKafkaInterceptor\nimport com.trendyol.sto"
},
{
"path": "examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/TracingValidationTest.kt",
"chars": 3609,
"preview": "package com.stove.spring.example.e2e\n\nimport arrow.core.some\nimport com.trendyol.stove.http.*\nimport com.trendyol.stove."
},
{
"path": "examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/hierarchy/BehaviorSpecHierarchyTest.kt",
"chars": 1594,
"preview": "package com.stove.spring.example.e2e.hierarchy\n\nimport com.trendyol.stove.http.*\nimport com.trendyol.stove.system.stove\n"
},
{
"path": "examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/hierarchy/DescribeSpecHierarchyTest.kt",
"chars": 1412,
"preview": "package com.stove.spring.example.e2e.hierarchy\n\nimport com.trendyol.stove.http.*\nimport com.trendyol.stove.system.stove\n"
},
{
"path": "examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/hierarchy/FunSpecContextHierarchyTest.kt",
"chars": 1308,
"preview": "package com.stove.spring.example.e2e.hierarchy\n\nimport com.trendyol.stove.http.*\nimport com.trendyol.stove.system.stove\n"
},
{
"path": "examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/hierarchy/NestedJunitHierarchyTest.kt",
"chars": 1840,
"preview": "package com.stove.spring.example.e2e.hierarchy\n\nimport com.trendyol.stove.extensions.junit.StoveJUnitExtension\nimport co"
},
{
"path": "examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/hierarchy/StringSpecHierarchyTest.kt",
"chars": 914,
"preview": "package com.stove.spring.example.e2e.hierarchy\n\nimport com.trendyol.stove.http.*\nimport com.trendyol.stove.system.stove\n"
},
{
"path": "examples/spring-example/src/test/resources/kotest.properties",
"chars": 69,
"preview": "kotest.framework.config.fqn=com.stove.spring.example.e2e.StoveConfig\n"
},
{
"path": "examples/spring-example/src/test/resources/logback-test.xml",
"chars": 716,
"preview": "<configuration>\n\n <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n <encoder>\n "
}
]
// ... and 862 more files (download for full content)
About this extraction
This page contains the full source code of the Trendyol/stove GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1062 files (3.6 MB), approximately 1.0M tokens, and a symbol index with 1135 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.