From 5d8b2a84fd151dad8bf06b8b085e430cf80b868a Mon Sep 17 00:00:00 2001
From: Sarthak160
Date: Mon, 10 Nov 2025 13:25:10 +0000
Subject: [PATCH 1/3] feat: integrate keploy for ci-cd
Signed-off-by: Sarthak160
---
.github/workflows/keploy-e2e.yml | 89 +
coverage/.coverage.order_service.combined | Bin 69632 -> 53248 bytes
coverage/.coverage.user_service.combined | Bin 53248 -> 53248 bytes
keploy.yml | 98 +
keploy/.cz.toml | 5 +
keploy/.github/CLA.md | 25 +
keploy/.github/CODEOWNERS | 1 +
keploy/.github/CONTRIBUTING.md | 101 +
keploy/.github/FUNDING.yml | 1 +
.../.github/ISSUE_TEMPLATE/--bug-report.yaml | 167 +
.../--documentation-update.yaml | 48 +
.../ISSUE_TEMPLATE/--feature-request.yaml | 60 +
keploy/.github/ISSUE_TEMPLATE/config.yml | 7 +
keploy/.github/License-Apache_2.0-blue.svg | 1 +
keploy/.github/PULL_REQUEST_TEMPLATE.md | 70 +
.../actions/download-binary/action.yml | 28 +
.../.github/actions/download-image/action.yml | 12 +
keploy/.github/actions/tester/action.yml | 112 +
keploy/.github/actions/tester/install.sh | 172 +
keploy/.github/docs.svg | 21 +
keploy/.github/slack.svg | 20 +
keploy/.github/workflows/bug.yml | 38 +
keploy/.github/workflows/cla.yml | 42 +
keploy/.github/workflows/codeql.yml | 84 +
keploy/.github/workflows/coverage_stage.yml | 57 +
keploy/.github/workflows/docker-publish.yml | 96 +
keploy/.github/workflows/docs.yml | 38 +
keploy/.github/workflows/feat.yml | 38 +
keploy/.github/workflows/go.yml | 31 +
keploy/.github/workflows/go_macos.yaml | 25 +
keploy/.github/workflows/go_windows.yml | 31 +
.../workflows/golang_docker-compose.yml | 49 +
keploy/.github/workflows/golang_docker.yml | 49 +
.../.github/workflows/golang_http_linux.yml | 53 +
keploy/.github/workflows/golang_linux.yml | 46 +
.../.github/workflows/golang_mysql_linux.yml | 46 +
keploy/.github/workflows/golangci-lint.yml | 48 +
keploy/.github/workflows/greetings.yml | 17 +
keploy/.github/workflows/java_linux.yml | 58 +
keploy/.github/workflows/main.yml | 26 +
keploy/.github/workflows/node_docker.yml | 50 +
keploy/.github/workflows/node_encoding.yaml | 48 +
keploy/.github/workflows/node_linux.yml | 47 +
keploy/.github/workflows/prepare_and_run.yml | 74 +
keploy/.github/workflows/python_docker.yml | 49 +
keploy/.github/workflows/python_linux.yml | 45 +
keploy/.github/workflows/release.yml | 101 +
keploy/.github/workflows/sample-run.yml | 25 +
keploy/.github/workflows/test-go-mongo-1.yml | 60 +
keploy/.github/workflows/test-go-mongo-2.yml | 58 +
.../check-deprecated-deps.sh | 25 +
.../golang-docker-compose.sh | 127 +
.../test_workflow_scripts/golang-docker.sh | 134 +
.../golang-http-linux.sh | 134 +
.../test_workflow_scripts/golang-linux.sh | 144 +
.../golang-mysql-linux.sh | 178 +
.../test_workflow_scripts/java-linux.sh | 255 +
.../test_workflow_scripts/node-docker.sh | 127 +
.../test_workflow_scripts/node-encoding.sh | 165 +
.../test_workflow_scripts/node-linux.sh | 203 +
.../test_workflow_scripts/python-docker.sh | 118 +
.../test_workflow_scripts/python-linux.sh | 128 +
.../test_workflow_scripts/test-iid.sh | 4 +
.../test_workflow_scripts/update-docker.sh | 38 +
.../test_workflow_scripts/update-java.sh | 8 +
keploy/.github/write-good.yml | 3 +
keploy/.gitignore | 61 +
keploy/.golangci.yml | 24 +
keploy/.pre-commit-config.yaml | 7 +
keploy/.releaserc.json | 13 +
keploy/CITATION.cff | 7 +
keploy/CODE_OF_CONDUCT.md | 126 +
keploy/CONTRIBUTING.md | 122 +
keploy/DEBUG.md | 79 +
keploy/Dockerfile | 57 +
keploy/HACKTOBERFEST_GUIDE.md | 82 +
keploy/LICENSE | 201 +
keploy/README-UnitGen.md | 278 +
keploy/README.md | 172 +
keploy/READMEes-Es.md | 243 +
keploy/READMEja-JP.md | 160 +
keploy/SECURITY.md | 9 +
keploy/cli/README.md | 5 +
keploy/cli/cli.go | 22 +
keploy/cli/config.go | 75 +
keploy/cli/contract.go | 134 +
keploy/cli/examples.go | 47 +
keploy/cli/export.go | 63 +
keploy/cli/import.go | 72 +
keploy/cli/load.go | 66 +
keploy/cli/login.go | 41 +
keploy/cli/mock.go | 94 +
keploy/cli/normalise.go | 50 +
keploy/cli/provider/cmd.go | 1079 +
keploy/cli/provider/common.go | 20 +
keploy/cli/provider/compat_linux.go | 27 +
keploy/cli/provider/compat_others.go | 11 +
keploy/cli/provider/core_service_linux.go | 125 +
keploy/cli/provider/core_service_others.go | 83 +
keploy/cli/provider/docker.go | 297 +
keploy/cli/provider/service.go | 60 +
keploy/cli/provider/util.go | 101 +
keploy/cli/record.go | 55 +
keploy/cli/report.go | 55 +
keploy/cli/rerecord.go | 56 +
keploy/cli/root.go | 55 +
keploy/cli/secure.go | 286 +
keploy/cli/service.go | 17 +
keploy/cli/templatize.go | 53 +
keploy/cli/test.go | 63 +
keploy/cli/testsuite.go | 60 +
keploy/cli/update.go | 51 +
keploy/cli/utgen.go | 56 +
keploy/config/config.go | 259 +
keploy/config/default.go | 134 +
keploy/entrypoint.sh | 7 +
keploy/go.mod | 159 +
keploy/go.sum | 470 +
keploy/gon.json | 13 +
keploy/goreleaser.yaml | 60 +
keploy/gsoc25_performance&security-testing.md | 558 +
keploy/keploy.sh | 295 +
keploy/main.go | 117 +
keploy/oss-pledge.json | 14 +
keploy/package-lock.json | 6 +
keploy/pkg/README.md | 6 +
keploy/pkg/core/app/app.go | 582 +
keploy/pkg/core/app/util.go | 103 +
keploy/pkg/core/core_linux.go | 279 +
keploy/pkg/core/core_others.go | 61 +
keploy/pkg/core/core_others_test.go | 92 +
keploy/pkg/core/hooks/README.md | 6 +
keploy/pkg/core/hooks/bpf_arm64_bpfel.go | 289 +
keploy/pkg/core/hooks/bpf_arm64_bpfel.o | Bin 0 -> 381808 bytes
keploy/pkg/core/hooks/bpf_x86_bpfel.go | 289 +
keploy/pkg/core/hooks/bpf_x86_bpfel.o | Bin 0 -> 381144 bytes
keploy/pkg/core/hooks/conn/README.md | 5 +
keploy/pkg/core/hooks/conn/conn.go | 125 +
keploy/pkg/core/hooks/conn/factory.go | 154 +
keploy/pkg/core/hooks/conn/socket.go | 264 +
keploy/pkg/core/hooks/conn/tracker.go | 571 +
keploy/pkg/core/hooks/conn/util.go | 329 +
keploy/pkg/core/hooks/hooks.go | 713 +
keploy/pkg/core/hooks/hooks_test.go | 273 +
keploy/pkg/core/hooks/kernelComm.go | 131 +
keploy/pkg/core/hooks/structs/structs.go | 78 +
keploy/pkg/core/hooks/util.go | 126 +
keploy/pkg/core/proxy/README.md | 5 +
keploy/pkg/core/proxy/dns.go | 345 +
keploy/pkg/core/proxy/integrations/README.md | 3 +
.../core/proxy/integrations/generic/README.md | 0
.../core/proxy/integrations/generic/decode.go | 124 +
.../core/proxy/integrations/generic/encode.go | 200 +
.../proxy/integrations/generic/generic.go | 69 +
.../core/proxy/integrations/generic/match.go | 155 +
.../core/proxy/integrations/grpc/decode.go | 33 +
.../core/proxy/integrations/grpc/encode.go | 86 +
.../pkg/core/proxy/integrations/grpc/frame.go | 215 +
.../pkg/core/proxy/integrations/grpc/grpc.go | 71 +
.../pkg/core/proxy/integrations/grpc/match.go | 227 +
.../core/proxy/integrations/grpc/stream.go | 160 +
.../proxy/integrations/grpc/transcoder.go | 379 +
.../pkg/core/proxy/integrations/grpc/util.go | 10 +
.../core/proxy/integrations/grpcV2/codec.go | 76 +
.../core/proxy/integrations/grpcV2/grpc.go | 92 +
.../core/proxy/integrations/grpcV2/helper.go | 46 +
.../proxy/integrations/grpcV2/listener.go | 64 +
.../core/proxy/integrations/grpcV2/match.go | 291 +
.../core/proxy/integrations/grpcV2/mock.go | 213 +
.../core/proxy/integrations/grpcV2/record.go | 428 +
.../proxy/integrations/grpcV2/replayconn.go | 28 +
.../core/proxy/integrations/http/README.md | 6 +
.../pkg/core/proxy/integrations/http/chunk.go | 348 +
.../core/proxy/integrations/http/decode.go | 206 +
.../core/proxy/integrations/http/encode.go | 262 +
.../pkg/core/proxy/integrations/http/http.go | 212 +
.../pkg/core/proxy/integrations/http/match.go | 349 +
.../pkg/core/proxy/integrations/http/util.go | 46 +
.../core/proxy/integrations/integrations.go | 54 +
.../core/proxy/integrations/mongo/README.md | 6 +
.../core/proxy/integrations/mongo/command.go | 98 +
.../core/proxy/integrations/mongo/decode.go | 345 +
.../core/proxy/integrations/mongo/encode.go | 288 +
.../core/proxy/integrations/mongo/match.go | 244 +
.../core/proxy/integrations/mongo/mongo.go | 128 +
.../proxy/integrations/mongo/operation.go | 1575 +
.../proxy/integrations/mongo/scramAuth.go | 512 +
.../pkg/core/proxy/integrations/mongo/util.go | 36 +
.../core/proxy/integrations/mysql/README.md | 52 +
.../core/proxy/integrations/mysql/mysql.go | 62 +
.../integrations/mysql/panic_fix_test.go | 192 +
.../proxy/integrations/mysql/recorder/conn.go | 730 +
.../integrations/mysql/recorder/query.go | 559 +
.../integrations/mysql/recorder/record.go | 131 +
.../proxy/integrations/mysql/replayer/conn.go | 719 +
.../integrations/mysql/replayer/match.go | 749 +
.../integrations/mysql/replayer/query.go | 184 +
.../integrations/mysql/replayer/replay.go | 124 +
.../proxy/integrations/mysql/utils/util.go | 360 +
.../integrations/mysql/utils/util_test.go | 211 +
.../proxy/integrations/mysql/wire/decode.go | 433 +
.../proxy/integrations/mysql/wire/encode.go | 179 +
.../wire/phase/conn/authMoreDataPacket.go | 43 +
.../wire/phase/conn/authNextFactorPacket.go | 56 +
.../phase/conn/authSwitchRequestPacket.go | 59 +
.../phase/conn/authSwitchResponsePacket.go | 31 +
.../phase/conn/handshakeResponse41Packet.go | 272 +
.../conn/handshakeResponse41Packet_test.go | 234 +
.../wire/phase/conn/handshakeV10Packet.go | 167 +
.../integrations/mysql/wire/phase/generic.go | 205 +
.../mysql/wire/phase/query/ResultSetPacket.go | 142 +
.../query/preparedstmt/StmtPrepareOkPacket.go | 153 +
.../query/preparedstmt/stmtClosePacket.go | 26 +
.../query/preparedstmt/stmtExecutePacket.go | 237 +
.../preparedstmt/stmtExecutePacket_test.go | 110 +
.../query/preparedstmt/stmtFetchPacket.go | 29 +
.../query/preparedstmt/stmtPreparePacket.go | 22 +
.../query/preparedstmt/stmtResetPacket.go | 25 +
.../preparedstmt/stmtSendLongDataPacket.go | 28 +
.../mysql/wire/phase/query/queryPacket.go | 225 +
.../query/rowscols/binaryProtocolRowPacket.go | 497 +
.../phase/query/rowscols/columnCountPacket.go | 32 +
.../wire/phase/query/rowscols/columnPacket.go | 201 +
.../phase/query/rowscols/textRowPacket.go | 105 +
.../wire/phase/query/utility/initDbPacket.go | 20 +
.../phase/query/utility/setOptionPacket.go | 26 +
.../proxy/integrations/mysql/wire/util.go | 175 +
.../proxy/integrations/postgres/v1/decode.go | 152 +
.../proxy/integrations/postgres/v1/encode.go | 401 +
.../proxy/integrations/postgres/v1/match.go | 867 +
.../integrations/postgres/v1/postgres.go | 87 +
.../integrations/postgres/v1/transcoder.go | 313 +
.../proxy/integrations/postgres/v1/util.go | 500 +
.../core/proxy/integrations/redis/decode.go | 125 +
.../core/proxy/integrations/redis/encode.go | 188 +
.../core/proxy/integrations/redis/match.go | 151 +
.../core/proxy/integrations/redis/redis.go | 78 +
.../core/proxy/integrations/scram/scram.go | 137 +
.../pkg/core/proxy/integrations/scram/util.go | 90 +
.../pkg/core/proxy/integrations/util/util.go | 88 +
keploy/pkg/core/proxy/mockmanager.go | 514 +
keploy/pkg/core/proxy/options.go | 12 +
keploy/pkg/core/proxy/parsers.go | 15 +
keploy/pkg/core/proxy/proxy.go | 731 +
keploy/pkg/core/proxy/tls/asset/ca.crt | 10 +
keploy/pkg/core/proxy/tls/asset/ca.key | 5 +
keploy/pkg/core/proxy/tls/asset/setup_ca.sh | 108 +
keploy/pkg/core/proxy/tls/ca.go | 345 +
keploy/pkg/core/proxy/tls/tls.go | 58 +
keploy/pkg/core/proxy/treedb.go | 88 +
keploy/pkg/core/proxy/util.go | 75 +
keploy/pkg/core/proxy/util/util.go | 648 +
keploy/pkg/core/record.go | 24 +
keploy/pkg/core/replay.go | 19 +
keploy/pkg/core/service.go | 142 +
keploy/pkg/core/tester/tester.go | 134 +
keploy/pkg/http2.go | 741 +
keploy/pkg/matcher/grpc/canonical.go | 282 +
keploy/pkg/matcher/grpc/match.go | 234 +
keploy/pkg/matcher/http/absmatch.go | 472 +
keploy/pkg/matcher/http/match.go | 536 +
keploy/pkg/matcher/http/match_test.go | 236 +
keploy/pkg/matcher/http/utils.go | 103 +
keploy/pkg/matcher/schema/match.go | 262 +
keploy/pkg/matcher/utils.go | 1207 +
keploy/pkg/models/README.md | 3 +
keploy/pkg/models/assertions.go | 17 +
keploy/pkg/models/auth.go | 14 +
keploy/pkg/models/config.go | 33 +
keploy/pkg/models/const.go | 128 +
keploy/pkg/models/errors.go | 27 +
keploy/pkg/models/generic.go | 13 +
keploy/pkg/models/github.go | 20 +
keploy/pkg/models/grpc.go | 79 +
keploy/pkg/models/http.go | 48 +
keploy/pkg/models/instrument.go | 63 +
keploy/pkg/models/mock.go | 92 +
keploy/pkg/models/mock_Secret.go | 29 +
keploy/pkg/models/mode.go | 52 +
keploy/pkg/models/mongo.go | 285 +
keploy/pkg/models/mysql/comm.go | 228 +
keploy/pkg/models/mysql/conn.go | 68 +
keploy/pkg/models/mysql/const.go | 267 +
keploy/pkg/models/mysql/generic.go | 30 +
keploy/pkg/models/mysql/mysql.go | 61 +
keploy/pkg/models/openapi.go | 142 +
keploy/pkg/models/postgres.go | 114 +
keploy/pkg/models/redis.go | 13 +
keploy/pkg/models/tele.go | 14 +
keploy/pkg/models/testcase.go | 73 +
keploy/pkg/models/testcompare.go | 42 +
keploy/pkg/models/testrun.go | 155 +
keploy/pkg/models/ut.go | 133 +
keploy/pkg/platform/README.md | 12 +
keploy/pkg/platform/auth/auth.go | 237 +
keploy/pkg/platform/coverage/csharp/csharp.go | 130 +
keploy/pkg/platform/coverage/csharp/utils.go | 140 +
keploy/pkg/platform/coverage/golang/golang.go | 151 +
keploy/pkg/platform/coverage/golang/utils.go | 56 +
keploy/pkg/platform/coverage/java/java.go | 145 +
keploy/pkg/platform/coverage/java/utils.go | 171 +
.../coverage/javascript/javascript.go | 174 +
.../pkg/platform/coverage/javascript/utils.go | 48 +
keploy/pkg/platform/coverage/python/python.go | 173 +
keploy/pkg/platform/coverage/python/utils.go | 166 +
keploy/pkg/platform/coverage/service.go | 18 +
keploy/pkg/platform/docker/docker.go | 585 +
keploy/pkg/platform/docker/service.go | 38 +
keploy/pkg/platform/docker/util.go | 53 +
keploy/pkg/platform/storage/storage.go | 204 +
keploy/pkg/platform/telemetry/telemetry.go | 185 +
keploy/pkg/platform/telemetry/utils.go | 52 +
.../pkg/platform/yaml/configdb/testset/db.go | 96 +
keploy/pkg/platform/yaml/configdb/user/db.go | 107 +
keploy/pkg/platform/yaml/mockdb/db.go | 319 +
keploy/pkg/platform/yaml/mockdb/util.go | 714 +
keploy/pkg/platform/yaml/openapidb/db.go | 152 +
keploy/pkg/platform/yaml/reportdb/db.go | 138 +
keploy/pkg/platform/yaml/testdb/db.go | 262 +
keploy/pkg/platform/yaml/testdb/util.go | 384 +
keploy/pkg/platform/yaml/utils.go | 403 +
keploy/pkg/platform/yaml/yaml.go | 232 +
keploy/pkg/service/README.md | 5 +
.../pkg/service/contract/consumer/consumer.go | 298 +
.../pkg/service/contract/consumer/service.go | 8 +
keploy/pkg/service/contract/contract.go | 685 +
.../pkg/service/contract/provider/provider.go | 127 +
.../pkg/service/contract/provider/service.go | 10 +
keploy/pkg/service/contract/schema.go | 111 +
keploy/pkg/service/contract/service.go | 30 +
keploy/pkg/service/contract/utils.go | 261 +
keploy/pkg/service/export/export.go | 269 +
keploy/pkg/service/import/import.go | 569 +
keploy/pkg/service/load/dashboard_exposer.go | 118 +
keploy/pkg/service/load/exporter.go | 205 +
keploy/pkg/service/load/load.go | 239 +
keploy/pkg/service/load/metrics_collector.go | 92 +
keploy/pkg/service/load/out/404.html | 1 +
keploy/pkg/service/load/out/404/index.html | 1 +
.../S6A95ZgpgxS8RHL4D2qnv/_buildManifest.js | 1 +
.../S6A95ZgpgxS8RHL4D2qnv/_ssgManifest.js | 1 +
.../chunks/4bd1b696-cf72ae8a39fa05aa.js | 1 +
.../static/chunks/547-586c3cf76649ec2c.js | 27 +
.../static/chunks/964-69097a61540f27b4.js | 1 +
.../app/_not-found/page-7c3ecdf160dc3360.js | 1 +
.../chunks/app/layout-bc503d5738af696e.js | 1 +
.../chunks/app/page-a3cd1a73fe99bbf3.js | 1 +
.../chunks/framework-7c95b8e5103c9e90.js | 1 +
.../static/chunks/main-a5da5fd7e32dc553.js | 1 +
.../chunks/main-app-b3d2ebcb2857645a.js | 1 +
.../chunks/pages/_app-0a0020ddd67f79cf.js | 1 +
.../chunks/pages/_error-03529f2c21436739.js | 1 +
.../chunks/polyfills-42372ed130431b0a.js | 1 +
.../static/chunks/webpack-cb07c2fe276ae3fa.js | 1 +
.../out/_next/static/css/bc1768f92951bee0.css | 3 +
.../static/media/26a46d62cd723877-s.woff2 | Bin 0 -> 18820 bytes
.../static/media/55c55f0601d81cf3-s.woff2 | Bin 0 -> 25908 bytes
.../static/media/581909926a08bbc8-s.woff2 | Bin 0 -> 19072 bytes
.../static/media/8e9860b6e62d6359-s.woff2 | Bin 0 -> 85272 bytes
.../static/media/97e0cb1ae144a2a9-s.woff2 | Bin 0 -> 11220 bytes
.../static/media/df0a9ae256c0569c-s.woff2 | Bin 0 -> 10280 bytes
.../static/media/e4af272ccee01ff0-s.p.woff2 | Bin 0 -> 48432 bytes
keploy/pkg/service/load/out/favicon.ico | Bin 0 -> 243774 bytes
keploy/pkg/service/load/out/index.html | 1 +
keploy/pkg/service/load/out/index.txt | 22 +
keploy/pkg/service/load/out/manifest.json | 17 +
keploy/pkg/service/load/out/sw.js | 188 +
keploy/pkg/service/load/scheduler.go | 138 +
keploy/pkg/service/load/service.go | 9 +
.../pkg/service/load/threshold_evaluator.go | 299 +
keploy/pkg/service/load/utils.go | 1 +
keploy/pkg/service/load/vu_worker.go | 118 +
.../pkg/service/orchestrator/orchestrator.go | 31 +
keploy/pkg/service/orchestrator/rerecord.go | 334 +
keploy/pkg/service/orchestrator/service.go | 7 +
keploy/pkg/service/record/record.go | 390 +
keploy/pkg/service/record/service.go | 51 +
keploy/pkg/service/record/utils.go | 3 +
keploy/pkg/service/replay/hooks.go | 282 +
keploy/pkg/service/replay/integration_test.go | 272 +
keploy/pkg/service/replay/mock.go | 297 +
keploy/pkg/service/replay/replay.go | 1793 +
keploy/pkg/service/replay/replay_test.go | 265 +
keploy/pkg/service/replay/service.go | 103 +
keploy/pkg/service/replay/service_test.go | 267 +
keploy/pkg/service/replay/utils.go | 118 +
keploy/pkg/service/report/report.go | 283 +
keploy/pkg/service/report/service.go | 20 +
keploy/pkg/service/secure/secure.go | 1222 +
keploy/pkg/service/secure/service.go | 11 +
keploy/pkg/service/secure/utils.go | 337 +
keploy/pkg/service/service.go | 9 +
keploy/pkg/service/testsuite/service.go | 11 +
keploy/pkg/service/testsuite/testsuite.go | 501 +
keploy/pkg/service/testsuite/utils.go | 109 +
keploy/pkg/service/tools/mock_Service.go | 149 +
keploy/pkg/service/tools/mock_TestDB.go | 143 +
.../pkg/service/tools/mock_TestSetConfig.go | 107 +
keploy/pkg/service/tools/service.go | 37 +
keploy/pkg/service/tools/templatize.go | 1106 +
keploy/pkg/service/tools/templatize_test.go | 162 +
keploy/pkg/service/tools/tools.go | 330 +
keploy/pkg/service/utgen/ai.go | 391 +
keploy/pkg/service/utgen/assets/config.go | 58 +
.../pkg/service/utgen/assets/indentation.toml | 41 +
.../pkg/service/utgen/assets/insert_line.toml | 37 +
keploy/pkg/service/utgen/assets/language.toml | 439 +
.../service/utgen/assets/test_generation.toml | 355 +
keploy/pkg/service/utgen/coverage.go | 249 +
keploy/pkg/service/utgen/gen.go | 813 +
keploy/pkg/service/utgen/injector.go | 830 +
keploy/pkg/service/utgen/prompt.go | 207 +
keploy/pkg/service/utgen/service.go | 13 +
keploy/pkg/service/utgen/utils.go | 330 +
keploy/pkg/util.go | 637 +
keploy/pkg/util_test.go | 247 +
keploy/utils/ctx.go | 69 +
keploy/utils/inc.go | 23 +
keploy/utils/log/colors.go | 48 +
keploy/utils/log/logger.go | 99 +
keploy/utils/log/time.go | 12 +
keploy/utils/mask_others.go | 13 +
keploy/utils/mask_windows.go | 11 +
keploy/utils/signal_others.go | 104 +
keploy/utils/signal_windows.go | 44 +
keploy/utils/utils.go | 1292 +
keploy/utils/utils_test.go | 348 +
scripts/user_service_flow.sh | 175 +
scripts/user_service_manual_tests.sh | 108 +
user_service/keploy-logs.txt | 15 +
user_service/keploy/atg/mocks.yaml | 121443 +++++++++++++++
user_service/keploy/atg/tests/test-1.yaml | 46 +
user_service/keploy/atg/tests/test-10.yaml | 47 +
user_service/keploy/atg/tests/test-100.yaml | 47 +
user_service/keploy/atg/tests/test-101.yaml | 47 +
user_service/keploy/atg/tests/test-102.yaml | 47 +
user_service/keploy/atg/tests/test-103.yaml | 47 +
user_service/keploy/atg/tests/test-104.yaml | 46 +
user_service/keploy/atg/tests/test-105.yaml | 47 +
user_service/keploy/atg/tests/test-106.yaml | 47 +
user_service/keploy/atg/tests/test-107.yaml | 44 +
user_service/keploy/atg/tests/test-108.yaml | 47 +
user_service/keploy/atg/tests/test-109.yaml | 43 +
user_service/keploy/atg/tests/test-11.yaml | 47 +
user_service/keploy/atg/tests/test-110.yaml | 43 +
user_service/keploy/atg/tests/test-111.yaml | 47 +
user_service/keploy/atg/tests/test-112.yaml | 47 +
user_service/keploy/atg/tests/test-113.yaml | 47 +
user_service/keploy/atg/tests/test-114.yaml | 47 +
user_service/keploy/atg/tests/test-115.yaml | 43 +
user_service/keploy/atg/tests/test-116.yaml | 47 +
user_service/keploy/atg/tests/test-117.yaml | 47 +
user_service/keploy/atg/tests/test-118.yaml | 47 +
user_service/keploy/atg/tests/test-119.yaml | 43 +
user_service/keploy/atg/tests/test-12.yaml | 47 +
user_service/keploy/atg/tests/test-120.yaml | 47 +
user_service/keploy/atg/tests/test-121.yaml | 47 +
user_service/keploy/atg/tests/test-122.yaml | 47 +
user_service/keploy/atg/tests/test-123.yaml | 47 +
user_service/keploy/atg/tests/test-124.yaml | 43 +
user_service/keploy/atg/tests/test-125.yaml | 47 +
user_service/keploy/atg/tests/test-126.yaml | 47 +
user_service/keploy/atg/tests/test-127.yaml | 47 +
user_service/keploy/atg/tests/test-128.yaml | 43 +
user_service/keploy/atg/tests/test-129.yaml | 47 +
user_service/keploy/atg/tests/test-13.yaml | 47 +
user_service/keploy/atg/tests/test-130.yaml | 44 +
user_service/keploy/atg/tests/test-131.yaml | 47 +
user_service/keploy/atg/tests/test-132.yaml | 43 +
user_service/keploy/atg/tests/test-133.yaml | 47 +
user_service/keploy/atg/tests/test-134.yaml | 47 +
user_service/keploy/atg/tests/test-135.yaml | 43 +
user_service/keploy/atg/tests/test-136.yaml | 43 +
user_service/keploy/atg/tests/test-137.yaml | 47 +
user_service/keploy/atg/tests/test-138.yaml | 47 +
user_service/keploy/atg/tests/test-139.yaml | 43 +
user_service/keploy/atg/tests/test-14.yaml | 47 +
user_service/keploy/atg/tests/test-140.yaml | 43 +
user_service/keploy/atg/tests/test-141.yaml | 47 +
user_service/keploy/atg/tests/test-142.yaml | 47 +
user_service/keploy/atg/tests/test-143.yaml | 47 +
user_service/keploy/atg/tests/test-144.yaml | 43 +
user_service/keploy/atg/tests/test-145.yaml | 47 +
user_service/keploy/atg/tests/test-146.yaml | 47 +
user_service/keploy/atg/tests/test-147.yaml | 47 +
user_service/keploy/atg/tests/test-148.yaml | 43 +
user_service/keploy/atg/tests/test-149.yaml | 47 +
user_service/keploy/atg/tests/test-15.yaml | 47 +
user_service/keploy/atg/tests/test-150.yaml | 47 +
user_service/keploy/atg/tests/test-151.yaml | 47 +
user_service/keploy/atg/tests/test-152.yaml | 43 +
user_service/keploy/atg/tests/test-153.yaml | 47 +
user_service/keploy/atg/tests/test-154.yaml | 47 +
user_service/keploy/atg/tests/test-155.yaml | 47 +
user_service/keploy/atg/tests/test-156.yaml | 47 +
user_service/keploy/atg/tests/test-157.yaml | 43 +
user_service/keploy/atg/tests/test-158.yaml | 47 +
user_service/keploy/atg/tests/test-159.yaml | 47 +
user_service/keploy/atg/tests/test-16.yaml | 47 +
user_service/keploy/atg/tests/test-160.yaml | 47 +
user_service/keploy/atg/tests/test-161.yaml | 47 +
user_service/keploy/atg/tests/test-162.yaml | 43 +
user_service/keploy/atg/tests/test-163.yaml | 47 +
user_service/keploy/atg/tests/test-164.yaml | 47 +
user_service/keploy/atg/tests/test-165.yaml | 47 +
user_service/keploy/atg/tests/test-166.yaml | 47 +
user_service/keploy/atg/tests/test-167.yaml | 43 +
user_service/keploy/atg/tests/test-168.yaml | 47 +
user_service/keploy/atg/tests/test-169.yaml | 47 +
user_service/keploy/atg/tests/test-17.yaml | 47 +
user_service/keploy/atg/tests/test-170.yaml | 47 +
user_service/keploy/atg/tests/test-171.yaml | 47 +
user_service/keploy/atg/tests/test-172.yaml | 50 +
user_service/keploy/atg/tests/test-173.yaml | 43 +
user_service/keploy/atg/tests/test-174.yaml | 47 +
user_service/keploy/atg/tests/test-175.yaml | 47 +
user_service/keploy/atg/tests/test-176.yaml | 47 +
user_service/keploy/atg/tests/test-177.yaml | 47 +
user_service/keploy/atg/tests/test-178.yaml | 43 +
user_service/keploy/atg/tests/test-179.yaml | 47 +
user_service/keploy/atg/tests/test-18.yaml | 47 +
user_service/keploy/atg/tests/test-180.yaml | 47 +
user_service/keploy/atg/tests/test-181.yaml | 47 +
user_service/keploy/atg/tests/test-182.yaml | 43 +
user_service/keploy/atg/tests/test-183.yaml | 47 +
user_service/keploy/atg/tests/test-184.yaml | 47 +
user_service/keploy/atg/tests/test-185.yaml | 47 +
user_service/keploy/atg/tests/test-186.yaml | 43 +
user_service/keploy/atg/tests/test-187.yaml | 47 +
user_service/keploy/atg/tests/test-188.yaml | 47 +
user_service/keploy/atg/tests/test-189.yaml | 48 +
user_service/keploy/atg/tests/test-19.yaml | 47 +
user_service/keploy/atg/tests/test-190.yaml | 43 +
user_service/keploy/atg/tests/test-191.yaml | 47 +
user_service/keploy/atg/tests/test-192.yaml | 47 +
user_service/keploy/atg/tests/test-193.yaml | 48 +
user_service/keploy/atg/tests/test-194.yaml | 43 +
user_service/keploy/atg/tests/test-195.yaml | 47 +
user_service/keploy/atg/tests/test-196.yaml | 47 +
user_service/keploy/atg/tests/test-197.yaml | 47 +
user_service/keploy/atg/tests/test-198.yaml | 47 +
user_service/keploy/atg/tests/test-199.yaml | 47 +
user_service/keploy/atg/tests/test-2.yaml | 45 +
user_service/keploy/atg/tests/test-20.yaml | 43 +
user_service/keploy/atg/tests/test-200.yaml | 47 +
user_service/keploy/atg/tests/test-201.yaml | 43 +
user_service/keploy/atg/tests/test-202.yaml | 47 +
user_service/keploy/atg/tests/test-203.yaml | 43 +
user_service/keploy/atg/tests/test-204.yaml | 47 +
user_service/keploy/atg/tests/test-205.yaml | 43 +
user_service/keploy/atg/tests/test-206.yaml | 47 +
user_service/keploy/atg/tests/test-207.yaml | 47 +
user_service/keploy/atg/tests/test-208.yaml | 51 +
user_service/keploy/atg/tests/test-209.yaml | 51 +
user_service/keploy/atg/tests/test-21.yaml | 47 +
user_service/keploy/atg/tests/test-210.yaml | 53 +
user_service/keploy/atg/tests/test-211.yaml | 49 +
user_service/keploy/atg/tests/test-212.yaml | 47 +
user_service/keploy/atg/tests/test-213.yaml | 47 +
user_service/keploy/atg/tests/test-214.yaml | 48 +
user_service/keploy/atg/tests/test-215.yaml | 48 +
user_service/keploy/atg/tests/test-216.yaml | 47 +
user_service/keploy/atg/tests/test-217.yaml | 47 +
user_service/keploy/atg/tests/test-218.yaml | 47 +
user_service/keploy/atg/tests/test-219.yaml | 43 +
user_service/keploy/atg/tests/test-22.yaml | 43 +
user_service/keploy/atg/tests/test-220.yaml | 43 +
user_service/keploy/atg/tests/test-221.yaml | 47 +
user_service/keploy/atg/tests/test-222.yaml | 47 +
user_service/keploy/atg/tests/test-223.yaml | 47 +
user_service/keploy/atg/tests/test-224.yaml | 43 +
user_service/keploy/atg/tests/test-225.yaml | 43 +
user_service/keploy/atg/tests/test-226.yaml | 47 +
user_service/keploy/atg/tests/test-227.yaml | 47 +
user_service/keploy/atg/tests/test-228.yaml | 47 +
user_service/keploy/atg/tests/test-229.yaml | 53 +
user_service/keploy/atg/tests/test-23.yaml | 47 +
user_service/keploy/atg/tests/test-230.yaml | 43 +
user_service/keploy/atg/tests/test-231.yaml | 47 +
user_service/keploy/atg/tests/test-232.yaml | 47 +
user_service/keploy/atg/tests/test-233.yaml | 47 +
user_service/keploy/atg/tests/test-234.yaml | 47 +
user_service/keploy/atg/tests/test-235.yaml | 43 +
user_service/keploy/atg/tests/test-236.yaml | 47 +
user_service/keploy/atg/tests/test-237.yaml | 47 +
user_service/keploy/atg/tests/test-238.yaml | 47 +
user_service/keploy/atg/tests/test-239.yaml | 53 +
user_service/keploy/atg/tests/test-24.yaml | 47 +
user_service/keploy/atg/tests/test-240.yaml | 43 +
user_service/keploy/atg/tests/test-241.yaml | 47 +
user_service/keploy/atg/tests/test-242.yaml | 47 +
user_service/keploy/atg/tests/test-243.yaml | 47 +
user_service/keploy/atg/tests/test-244.yaml | 47 +
user_service/keploy/atg/tests/test-245.yaml | 43 +
user_service/keploy/atg/tests/test-246.yaml | 47 +
user_service/keploy/atg/tests/test-247.yaml | 47 +
user_service/keploy/atg/tests/test-248.yaml | 47 +
user_service/keploy/atg/tests/test-249.yaml | 43 +
user_service/keploy/atg/tests/test-25.yaml | 47 +
user_service/keploy/atg/tests/test-250.yaml | 47 +
user_service/keploy/atg/tests/test-251.yaml | 47 +
user_service/keploy/atg/tests/test-252.yaml | 47 +
user_service/keploy/atg/tests/test-253.yaml | 43 +
user_service/keploy/atg/tests/test-254.yaml | 47 +
user_service/keploy/atg/tests/test-255.yaml | 47 +
user_service/keploy/atg/tests/test-256.yaml | 51 +
user_service/keploy/atg/tests/test-257.yaml | 43 +
user_service/keploy/atg/tests/test-258.yaml | 47 +
user_service/keploy/atg/tests/test-259.yaml | 47 +
user_service/keploy/atg/tests/test-26.yaml | 47 +
user_service/keploy/atg/tests/test-260.yaml | 51 +
user_service/keploy/atg/tests/test-261.yaml | 43 +
user_service/keploy/atg/tests/test-262.yaml | 47 +
user_service/keploy/atg/tests/test-263.yaml | 47 +
user_service/keploy/atg/tests/test-264.yaml | 47 +
user_service/keploy/atg/tests/test-265.yaml | 47 +
user_service/keploy/atg/tests/test-266.yaml | 47 +
user_service/keploy/atg/tests/test-267.yaml | 43 +
user_service/keploy/atg/tests/test-268.yaml | 47 +
user_service/keploy/atg/tests/test-269.yaml | 47 +
user_service/keploy/atg/tests/test-27.yaml | 47 +
user_service/keploy/atg/tests/test-270.yaml | 47 +
user_service/keploy/atg/tests/test-271.yaml | 43 +
user_service/keploy/atg/tests/test-272.yaml | 47 +
user_service/keploy/atg/tests/test-273.yaml | 47 +
user_service/keploy/atg/tests/test-274.yaml | 47 +
user_service/keploy/atg/tests/test-275.yaml | 43 +
user_service/keploy/atg/tests/test-276.yaml | 47 +
user_service/keploy/atg/tests/test-277.yaml | 47 +
user_service/keploy/atg/tests/test-278.yaml | 47 +
user_service/keploy/atg/tests/test-279.yaml | 43 +
user_service/keploy/atg/tests/test-28.yaml | 47 +
user_service/keploy/atg/tests/test-280.yaml | 47 +
user_service/keploy/atg/tests/test-281.yaml | 47 +
user_service/keploy/atg/tests/test-282.yaml | 47 +
user_service/keploy/atg/tests/test-283.yaml | 43 +
user_service/keploy/atg/tests/test-284.yaml | 47 +
user_service/keploy/atg/tests/test-285.yaml | 47 +
user_service/keploy/atg/tests/test-286.yaml | 47 +
user_service/keploy/atg/tests/test-287.yaml | 43 +
user_service/keploy/atg/tests/test-288.yaml | 47 +
user_service/keploy/atg/tests/test-289.yaml | 47 +
user_service/keploy/atg/tests/test-29.yaml | 47 +
user_service/keploy/atg/tests/test-290.yaml | 47 +
user_service/keploy/atg/tests/test-291.yaml | 43 +
user_service/keploy/atg/tests/test-292.yaml | 47 +
user_service/keploy/atg/tests/test-293.yaml | 47 +
user_service/keploy/atg/tests/test-294.yaml | 47 +
user_service/keploy/atg/tests/test-295.yaml | 43 +
user_service/keploy/atg/tests/test-296.yaml | 47 +
user_service/keploy/atg/tests/test-297.yaml | 47 +
user_service/keploy/atg/tests/test-298.yaml | 47 +
user_service/keploy/atg/tests/test-299.yaml | 47 +
user_service/keploy/atg/tests/test-3.yaml | 45 +
user_service/keploy/atg/tests/test-30.yaml | 47 +
user_service/keploy/atg/tests/test-300.yaml | 43 +
user_service/keploy/atg/tests/test-301.yaml | 47 +
user_service/keploy/atg/tests/test-302.yaml | 47 +
user_service/keploy/atg/tests/test-303.yaml | 43 +
user_service/keploy/atg/tests/test-304.yaml | 47 +
user_service/keploy/atg/tests/test-305.yaml | 47 +
user_service/keploy/atg/tests/test-306.yaml | 43 +
user_service/keploy/atg/tests/test-307.yaml | 43 +
user_service/keploy/atg/tests/test-308.yaml | 47 +
user_service/keploy/atg/tests/test-309.yaml | 47 +
user_service/keploy/atg/tests/test-31.yaml | 47 +
user_service/keploy/atg/tests/test-310.yaml | 47 +
user_service/keploy/atg/tests/test-311.yaml | 47 +
user_service/keploy/atg/tests/test-312.yaml | 43 +
user_service/keploy/atg/tests/test-313.yaml | 43 +
user_service/keploy/atg/tests/test-314.yaml | 47 +
user_service/keploy/atg/tests/test-315.yaml | 47 +
user_service/keploy/atg/tests/test-316.yaml | 47 +
user_service/keploy/atg/tests/test-317.yaml | 47 +
user_service/keploy/atg/tests/test-318.yaml | 43 +
user_service/keploy/atg/tests/test-319.yaml | 47 +
user_service/keploy/atg/tests/test-32.yaml | 47 +
user_service/keploy/atg/tests/test-320.yaml | 47 +
user_service/keploy/atg/tests/test-321.yaml | 47 +
user_service/keploy/atg/tests/test-322.yaml | 47 +
user_service/keploy/atg/tests/test-323.yaml | 43 +
user_service/keploy/atg/tests/test-324.yaml | 47 +
user_service/keploy/atg/tests/test-325.yaml | 43 +
user_service/keploy/atg/tests/test-326.yaml | 47 +
user_service/keploy/atg/tests/test-327.yaml | 47 +
user_service/keploy/atg/tests/test-328.yaml | 43 +
user_service/keploy/atg/tests/test-329.yaml | 43 +
user_service/keploy/atg/tests/test-33.yaml | 47 +
user_service/keploy/atg/tests/test-330.yaml | 47 +
user_service/keploy/atg/tests/test-331.yaml | 47 +
user_service/keploy/atg/tests/test-332.yaml | 49 +
user_service/keploy/atg/tests/test-333.yaml | 43 +
user_service/keploy/atg/tests/test-334.yaml | 51 +
user_service/keploy/atg/tests/test-335.yaml | 47 +
user_service/keploy/atg/tests/test-336.yaml | 47 +
user_service/keploy/atg/tests/test-337.yaml | 47 +
user_service/keploy/atg/tests/test-338.yaml | 47 +
user_service/keploy/atg/tests/test-339.yaml | 43 +
user_service/keploy/atg/tests/test-34.yaml | 47 +
user_service/keploy/atg/tests/test-340.yaml | 47 +
user_service/keploy/atg/tests/test-341.yaml | 47 +
user_service/keploy/atg/tests/test-342.yaml | 51 +
user_service/keploy/atg/tests/test-343.yaml | 43 +
user_service/keploy/atg/tests/test-344.yaml | 47 +
user_service/keploy/atg/tests/test-345.yaml | 47 +
user_service/keploy/atg/tests/test-346.yaml | 51 +
user_service/keploy/atg/tests/test-347.yaml | 43 +
user_service/keploy/atg/tests/test-348.yaml | 47 +
user_service/keploy/atg/tests/test-349.yaml | 47 +
user_service/keploy/atg/tests/test-35.yaml | 47 +
user_service/keploy/atg/tests/test-350.yaml | 51 +
user_service/keploy/atg/tests/test-351.yaml | 43 +
user_service/keploy/atg/tests/test-352.yaml | 47 +
user_service/keploy/atg/tests/test-353.yaml | 47 +
user_service/keploy/atg/tests/test-354.yaml | 47 +
user_service/keploy/atg/tests/test-355.yaml | 43 +
user_service/keploy/atg/tests/test-356.yaml | 47 +
user_service/keploy/atg/tests/test-357.yaml | 47 +
user_service/keploy/atg/tests/test-358.yaml | 47 +
user_service/keploy/atg/tests/test-359.yaml | 43 +
user_service/keploy/atg/tests/test-36.yaml | 47 +
user_service/keploy/atg/tests/test-360.yaml | 47 +
user_service/keploy/atg/tests/test-361.yaml | 47 +
user_service/keploy/atg/tests/test-362.yaml | 47 +
user_service/keploy/atg/tests/test-363.yaml | 43 +
user_service/keploy/atg/tests/test-364.yaml | 47 +
user_service/keploy/atg/tests/test-365.yaml | 47 +
user_service/keploy/atg/tests/test-366.yaml | 47 +
user_service/keploy/atg/tests/test-367.yaml | 43 +
user_service/keploy/atg/tests/test-368.yaml | 47 +
user_service/keploy/atg/tests/test-369.yaml | 47 +
user_service/keploy/atg/tests/test-37.yaml | 47 +
user_service/keploy/atg/tests/test-370.yaml | 47 +
user_service/keploy/atg/tests/test-371.yaml | 43 +
user_service/keploy/atg/tests/test-372.yaml | 47 +
user_service/keploy/atg/tests/test-373.yaml | 47 +
user_service/keploy/atg/tests/test-374.yaml | 47 +
user_service/keploy/atg/tests/test-375.yaml | 43 +
user_service/keploy/atg/tests/test-376.yaml | 47 +
user_service/keploy/atg/tests/test-377.yaml | 47 +
user_service/keploy/atg/tests/test-378.yaml | 47 +
user_service/keploy/atg/tests/test-379.yaml | 43 +
user_service/keploy/atg/tests/test-38.yaml | 47 +
user_service/keploy/atg/tests/test-380.yaml | 47 +
user_service/keploy/atg/tests/test-381.yaml | 47 +
user_service/keploy/atg/tests/test-382.yaml | 47 +
user_service/keploy/atg/tests/test-383.yaml | 43 +
user_service/keploy/atg/tests/test-384.yaml | 47 +
user_service/keploy/atg/tests/test-385.yaml | 47 +
user_service/keploy/atg/tests/test-386.yaml | 47 +
user_service/keploy/atg/tests/test-387.yaml | 43 +
user_service/keploy/atg/tests/test-388.yaml | 47 +
user_service/keploy/atg/tests/test-389.yaml | 47 +
user_service/keploy/atg/tests/test-39.yaml | 47 +
user_service/keploy/atg/tests/test-390.yaml | 47 +
user_service/keploy/atg/tests/test-391.yaml | 43 +
user_service/keploy/atg/tests/test-392.yaml | 47 +
user_service/keploy/atg/tests/test-393.yaml | 47 +
user_service/keploy/atg/tests/test-394.yaml | 47 +
user_service/keploy/atg/tests/test-395.yaml | 43 +
user_service/keploy/atg/tests/test-396.yaml | 47 +
user_service/keploy/atg/tests/test-397.yaml | 47 +
user_service/keploy/atg/tests/test-398.yaml | 47 +
user_service/keploy/atg/tests/test-399.yaml | 43 +
user_service/keploy/atg/tests/test-4.yaml | 45 +
user_service/keploy/atg/tests/test-40.yaml | 47 +
user_service/keploy/atg/tests/test-400.yaml | 47 +
user_service/keploy/atg/tests/test-401.yaml | 47 +
user_service/keploy/atg/tests/test-402.yaml | 47 +
user_service/keploy/atg/tests/test-403.yaml | 43 +
user_service/keploy/atg/tests/test-404.yaml | 47 +
user_service/keploy/atg/tests/test-405.yaml | 47 +
user_service/keploy/atg/tests/test-406.yaml | 47 +
user_service/keploy/atg/tests/test-407.yaml | 43 +
user_service/keploy/atg/tests/test-408.yaml | 47 +
user_service/keploy/atg/tests/test-409.yaml | 47 +
user_service/keploy/atg/tests/test-41.yaml | 47 +
user_service/keploy/atg/tests/test-410.yaml | 47 +
user_service/keploy/atg/tests/test-411.yaml | 43 +
user_service/keploy/atg/tests/test-412.yaml | 47 +
user_service/keploy/atg/tests/test-413.yaml | 47 +
user_service/keploy/atg/tests/test-414.yaml | 47 +
user_service/keploy/atg/tests/test-415.yaml | 43 +
user_service/keploy/atg/tests/test-416.yaml | 47 +
user_service/keploy/atg/tests/test-417.yaml | 47 +
user_service/keploy/atg/tests/test-418.yaml | 47 +
user_service/keploy/atg/tests/test-419.yaml | 43 +
user_service/keploy/atg/tests/test-42.yaml | 47 +
user_service/keploy/atg/tests/test-420.yaml | 47 +
user_service/keploy/atg/tests/test-421.yaml | 47 +
user_service/keploy/atg/tests/test-422.yaml | 47 +
user_service/keploy/atg/tests/test-423.yaml | 43 +
user_service/keploy/atg/tests/test-424.yaml | 47 +
user_service/keploy/atg/tests/test-425.yaml | 47 +
user_service/keploy/atg/tests/test-426.yaml | 47 +
user_service/keploy/atg/tests/test-427.yaml | 43 +
user_service/keploy/atg/tests/test-428.yaml | 47 +
user_service/keploy/atg/tests/test-429.yaml | 47 +
user_service/keploy/atg/tests/test-43.yaml | 47 +
user_service/keploy/atg/tests/test-430.yaml | 47 +
user_service/keploy/atg/tests/test-431.yaml | 43 +
user_service/keploy/atg/tests/test-432.yaml | 47 +
user_service/keploy/atg/tests/test-433.yaml | 47 +
user_service/keploy/atg/tests/test-434.yaml | 47 +
user_service/keploy/atg/tests/test-435.yaml | 43 +
user_service/keploy/atg/tests/test-436.yaml | 47 +
user_service/keploy/atg/tests/test-437.yaml | 47 +
user_service/keploy/atg/tests/test-438.yaml | 47 +
user_service/keploy/atg/tests/test-439.yaml | 43 +
user_service/keploy/atg/tests/test-44.yaml | 47 +
user_service/keploy/atg/tests/test-440.yaml | 47 +
user_service/keploy/atg/tests/test-441.yaml | 47 +
user_service/keploy/atg/tests/test-442.yaml | 47 +
user_service/keploy/atg/tests/test-443.yaml | 43 +
user_service/keploy/atg/tests/test-444.yaml | 47 +
user_service/keploy/atg/tests/test-445.yaml | 47 +
user_service/keploy/atg/tests/test-446.yaml | 47 +
user_service/keploy/atg/tests/test-447.yaml | 43 +
user_service/keploy/atg/tests/test-448.yaml | 47 +
user_service/keploy/atg/tests/test-449.yaml | 47 +
user_service/keploy/atg/tests/test-45.yaml | 47 +
user_service/keploy/atg/tests/test-450.yaml | 47 +
user_service/keploy/atg/tests/test-451.yaml | 43 +
user_service/keploy/atg/tests/test-452.yaml | 47 +
user_service/keploy/atg/tests/test-453.yaml | 47 +
user_service/keploy/atg/tests/test-454.yaml | 47 +
user_service/keploy/atg/tests/test-455.yaml | 43 +
user_service/keploy/atg/tests/test-456.yaml | 47 +
user_service/keploy/atg/tests/test-457.yaml | 47 +
user_service/keploy/atg/tests/test-458.yaml | 47 +
user_service/keploy/atg/tests/test-459.yaml | 43 +
user_service/keploy/atg/tests/test-46.yaml | 47 +
user_service/keploy/atg/tests/test-460.yaml | 51 +
user_service/keploy/atg/tests/test-461.yaml | 51 +
user_service/keploy/atg/tests/test-462.yaml | 47 +
user_service/keploy/atg/tests/test-463.yaml | 51 +
user_service/keploy/atg/tests/test-464.yaml | 51 +
user_service/keploy/atg/tests/test-465.yaml | 47 +
user_service/keploy/atg/tests/test-466.yaml | 51 +
user_service/keploy/atg/tests/test-467.yaml | 51 +
user_service/keploy/atg/tests/test-468.yaml | 47 +
user_service/keploy/atg/tests/test-469.yaml | 51 +
user_service/keploy/atg/tests/test-47.yaml | 47 +
user_service/keploy/atg/tests/test-470.yaml | 51 +
user_service/keploy/atg/tests/test-471.yaml | 47 +
user_service/keploy/atg/tests/test-472.yaml | 51 +
user_service/keploy/atg/tests/test-473.yaml | 51 +
user_service/keploy/atg/tests/test-474.yaml | 47 +
user_service/keploy/atg/tests/test-475.yaml | 51 +
user_service/keploy/atg/tests/test-476.yaml | 51 +
user_service/keploy/atg/tests/test-477.yaml | 47 +
user_service/keploy/atg/tests/test-478.yaml | 51 +
user_service/keploy/atg/tests/test-479.yaml | 51 +
user_service/keploy/atg/tests/test-48.yaml | 47 +
user_service/keploy/atg/tests/test-480.yaml | 47 +
user_service/keploy/atg/tests/test-481.yaml | 51 +
user_service/keploy/atg/tests/test-482.yaml | 51 +
user_service/keploy/atg/tests/test-483.yaml | 51 +
user_service/keploy/atg/tests/test-484.yaml | 47 +
user_service/keploy/atg/tests/test-485.yaml | 51 +
user_service/keploy/atg/tests/test-486.yaml | 51 +
user_service/keploy/atg/tests/test-487.yaml | 47 +
user_service/keploy/atg/tests/test-488.yaml | 51 +
user_service/keploy/atg/tests/test-489.yaml | 51 +
user_service/keploy/atg/tests/test-49.yaml | 47 +
user_service/keploy/atg/tests/test-490.yaml | 47 +
user_service/keploy/atg/tests/test-491.yaml | 51 +
user_service/keploy/atg/tests/test-492.yaml | 51 +
user_service/keploy/atg/tests/test-493.yaml | 47 +
user_service/keploy/atg/tests/test-494.yaml | 51 +
user_service/keploy/atg/tests/test-495.yaml | 51 +
user_service/keploy/atg/tests/test-496.yaml | 47 +
user_service/keploy/atg/tests/test-497.yaml | 51 +
user_service/keploy/atg/tests/test-498.yaml | 51 +
user_service/keploy/atg/tests/test-499.yaml | 51 +
user_service/keploy/atg/tests/test-5.yaml | 45 +
user_service/keploy/atg/tests/test-50.yaml | 47 +
user_service/keploy/atg/tests/test-500.yaml | 47 +
user_service/keploy/atg/tests/test-501.yaml | 49 +
user_service/keploy/atg/tests/test-502.yaml | 49 +
user_service/keploy/atg/tests/test-503.yaml | 51 +
user_service/keploy/atg/tests/test-504.yaml | 51 +
user_service/keploy/atg/tests/test-505.yaml | 51 +
user_service/keploy/atg/tests/test-506.yaml | 51 +
user_service/keploy/atg/tests/test-507.yaml | 49 +
user_service/keploy/atg/tests/test-508.yaml | 49 +
user_service/keploy/atg/tests/test-509.yaml | 50 +
user_service/keploy/atg/tests/test-51.yaml | 47 +
user_service/keploy/atg/tests/test-510.yaml | 50 +
user_service/keploy/atg/tests/test-511.yaml | 51 +
user_service/keploy/atg/tests/test-512.yaml | 51 +
user_service/keploy/atg/tests/test-513.yaml | 51 +
user_service/keploy/atg/tests/test-514.yaml | 51 +
user_service/keploy/atg/tests/test-515.yaml | 51 +
user_service/keploy/atg/tests/test-516.yaml | 51 +
user_service/keploy/atg/tests/test-517.yaml | 51 +
user_service/keploy/atg/tests/test-518.yaml | 51 +
user_service/keploy/atg/tests/test-519.yaml | 51 +
user_service/keploy/atg/tests/test-52.yaml | 47 +
user_service/keploy/atg/tests/test-520.yaml | 51 +
user_service/keploy/atg/tests/test-521.yaml | 51 +
user_service/keploy/atg/tests/test-522.yaml | 51 +
user_service/keploy/atg/tests/test-523.yaml | 51 +
user_service/keploy/atg/tests/test-524.yaml | 51 +
user_service/keploy/atg/tests/test-525.yaml | 51 +
user_service/keploy/atg/tests/test-526.yaml | 51 +
user_service/keploy/atg/tests/test-527.yaml | 51 +
user_service/keploy/atg/tests/test-528.yaml | 51 +
user_service/keploy/atg/tests/test-529.yaml | 51 +
user_service/keploy/atg/tests/test-53.yaml | 47 +
user_service/keploy/atg/tests/test-530.yaml | 51 +
user_service/keploy/atg/tests/test-531.yaml | 51 +
user_service/keploy/atg/tests/test-532.yaml | 51 +
user_service/keploy/atg/tests/test-533.yaml | 51 +
user_service/keploy/atg/tests/test-534.yaml | 51 +
user_service/keploy/atg/tests/test-535.yaml | 51 +
user_service/keploy/atg/tests/test-536.yaml | 51 +
user_service/keploy/atg/tests/test-537.yaml | 51 +
user_service/keploy/atg/tests/test-538.yaml | 51 +
user_service/keploy/atg/tests/test-539.yaml | 51 +
user_service/keploy/atg/tests/test-54.yaml | 47 +
user_service/keploy/atg/tests/test-540.yaml | 51 +
user_service/keploy/atg/tests/test-541.yaml | 47 +
user_service/keploy/atg/tests/test-542.yaml | 51 +
user_service/keploy/atg/tests/test-55.yaml | 47 +
user_service/keploy/atg/tests/test-56.yaml | 47 +
user_service/keploy/atg/tests/test-57.yaml | 47 +
user_service/keploy/atg/tests/test-58.yaml | 47 +
user_service/keploy/atg/tests/test-59.yaml | 47 +
user_service/keploy/atg/tests/test-6.yaml | 46 +
user_service/keploy/atg/tests/test-60.yaml | 47 +
user_service/keploy/atg/tests/test-61.yaml | 47 +
user_service/keploy/atg/tests/test-62.yaml | 47 +
user_service/keploy/atg/tests/test-63.yaml | 47 +
user_service/keploy/atg/tests/test-64.yaml | 47 +
user_service/keploy/atg/tests/test-65.yaml | 47 +
user_service/keploy/atg/tests/test-66.yaml | 47 +
user_service/keploy/atg/tests/test-67.yaml | 47 +
user_service/keploy/atg/tests/test-68.yaml | 47 +
user_service/keploy/atg/tests/test-69.yaml | 47 +
user_service/keploy/atg/tests/test-7.yaml | 47 +
user_service/keploy/atg/tests/test-70.yaml | 47 +
user_service/keploy/atg/tests/test-71.yaml | 47 +
user_service/keploy/atg/tests/test-72.yaml | 49 +
user_service/keploy/atg/tests/test-73.yaml | 49 +
user_service/keploy/atg/tests/test-74.yaml | 47 +
user_service/keploy/atg/tests/test-75.yaml | 47 +
user_service/keploy/atg/tests/test-76.yaml | 47 +
user_service/keploy/atg/tests/test-77.yaml | 47 +
user_service/keploy/atg/tests/test-78.yaml | 47 +
user_service/keploy/atg/tests/test-79.yaml | 51 +
user_service/keploy/atg/tests/test-8.yaml | 47 +
user_service/keploy/atg/tests/test-80.yaml | 47 +
user_service/keploy/atg/tests/test-81.yaml | 51 +
user_service/keploy/atg/tests/test-82.yaml | 47 +
user_service/keploy/atg/tests/test-83.yaml | 47 +
user_service/keploy/atg/tests/test-84.yaml | 47 +
user_service/keploy/atg/tests/test-85.yaml | 47 +
user_service/keploy/atg/tests/test-86.yaml | 48 +
user_service/keploy/atg/tests/test-87.yaml | 47 +
user_service/keploy/atg/tests/test-88.yaml | 48 +
user_service/keploy/atg/tests/test-89.yaml | 47 +
user_service/keploy/atg/tests/test-9.yaml | 47 +
user_service/keploy/atg/tests/test-90.yaml | 47 +
user_service/keploy/atg/tests/test-91.yaml | 47 +
user_service/keploy/atg/tests/test-92.yaml | 47 +
user_service/keploy/atg/tests/test-93.yaml | 47 +
user_service/keploy/atg/tests/test-94.yaml | 47 +
user_service/keploy/atg/tests/test-95.yaml | 47 +
user_service/keploy/atg/tests/test-96.yaml | 47 +
user_service/keploy/atg/tests/test-97.yaml | 47 +
user_service/keploy/atg/tests/test-98.yaml | 47 +
user_service/keploy/atg/tests/test-99.yaml | 49 +
.../load/reports/20251110_113538_suite-0.json | 56 +
.../load/reports/20251110_113651_suite-0.json | 56 +
.../load/reports/20251110_125123_suite-0.json | 128 +
user_service/keploy/testsuite/keploy.yml | 74 +
user_service/keploy/testsuite/suite-0.yaml | 122 +
.../20251110112827_report_suite-0.yaml | 55 +
979 files changed, 216546 insertions(+)
create mode 100644 .github/workflows/keploy-e2e.yml
create mode 100755 keploy.yml
create mode 100755 keploy/.cz.toml
create mode 100755 keploy/.github/CLA.md
create mode 100644 keploy/.github/CODEOWNERS
create mode 100755 keploy/.github/CONTRIBUTING.md
create mode 100755 keploy/.github/FUNDING.yml
create mode 100755 keploy/.github/ISSUE_TEMPLATE/--bug-report.yaml
create mode 100755 keploy/.github/ISSUE_TEMPLATE/--documentation-update.yaml
create mode 100755 keploy/.github/ISSUE_TEMPLATE/--feature-request.yaml
create mode 100755 keploy/.github/ISSUE_TEMPLATE/config.yml
create mode 100755 keploy/.github/License-Apache_2.0-blue.svg
create mode 100755 keploy/.github/PULL_REQUEST_TEMPLATE.md
create mode 100644 keploy/.github/actions/download-binary/action.yml
create mode 100644 keploy/.github/actions/download-image/action.yml
create mode 100644 keploy/.github/actions/tester/action.yml
create mode 100644 keploy/.github/actions/tester/install.sh
create mode 100755 keploy/.github/docs.svg
create mode 100755 keploy/.github/slack.svg
create mode 100755 keploy/.github/workflows/bug.yml
create mode 100755 keploy/.github/workflows/cla.yml
create mode 100755 keploy/.github/workflows/codeql.yml
create mode 100644 keploy/.github/workflows/coverage_stage.yml
create mode 100755 keploy/.github/workflows/docker-publish.yml
create mode 100755 keploy/.github/workflows/docs.yml
create mode 100755 keploy/.github/workflows/feat.yml
create mode 100755 keploy/.github/workflows/go.yml
create mode 100644 keploy/.github/workflows/go_macos.yaml
create mode 100644 keploy/.github/workflows/go_windows.yml
create mode 100644 keploy/.github/workflows/golang_docker-compose.yml
create mode 100755 keploy/.github/workflows/golang_docker.yml
create mode 100644 keploy/.github/workflows/golang_http_linux.yml
create mode 100644 keploy/.github/workflows/golang_linux.yml
create mode 100644 keploy/.github/workflows/golang_mysql_linux.yml
create mode 100644 keploy/.github/workflows/golangci-lint.yml
create mode 100755 keploy/.github/workflows/greetings.yml
create mode 100644 keploy/.github/workflows/java_linux.yml
create mode 100755 keploy/.github/workflows/main.yml
create mode 100755 keploy/.github/workflows/node_docker.yml
create mode 100644 keploy/.github/workflows/node_encoding.yaml
create mode 100644 keploy/.github/workflows/node_linux.yml
create mode 100644 keploy/.github/workflows/prepare_and_run.yml
create mode 100644 keploy/.github/workflows/python_docker.yml
create mode 100644 keploy/.github/workflows/python_linux.yml
create mode 100755 keploy/.github/workflows/release.yml
create mode 100755 keploy/.github/workflows/sample-run.yml
create mode 100644 keploy/.github/workflows/test-go-mongo-1.yml
create mode 100644 keploy/.github/workflows/test-go-mongo-2.yml
create mode 100644 keploy/.github/workflows/test_workflow_scripts/check-deprecated-deps.sh
create mode 100644 keploy/.github/workflows/test_workflow_scripts/golang-docker-compose.sh
create mode 100755 keploy/.github/workflows/test_workflow_scripts/golang-docker.sh
create mode 100644 keploy/.github/workflows/test_workflow_scripts/golang-http-linux.sh
create mode 100644 keploy/.github/workflows/test_workflow_scripts/golang-linux.sh
create mode 100644 keploy/.github/workflows/test_workflow_scripts/golang-mysql-linux.sh
create mode 100644 keploy/.github/workflows/test_workflow_scripts/java-linux.sh
create mode 100755 keploy/.github/workflows/test_workflow_scripts/node-docker.sh
create mode 100644 keploy/.github/workflows/test_workflow_scripts/node-encoding.sh
create mode 100755 keploy/.github/workflows/test_workflow_scripts/node-linux.sh
create mode 100755 keploy/.github/workflows/test_workflow_scripts/python-docker.sh
create mode 100644 keploy/.github/workflows/test_workflow_scripts/python-linux.sh
create mode 100644 keploy/.github/workflows/test_workflow_scripts/test-iid.sh
create mode 100644 keploy/.github/workflows/test_workflow_scripts/update-docker.sh
create mode 100644 keploy/.github/workflows/test_workflow_scripts/update-java.sh
create mode 100755 keploy/.github/write-good.yml
create mode 100755 keploy/.gitignore
create mode 100644 keploy/.golangci.yml
create mode 100755 keploy/.pre-commit-config.yaml
create mode 100755 keploy/.releaserc.json
create mode 100755 keploy/CITATION.cff
create mode 100755 keploy/CODE_OF_CONDUCT.md
create mode 100755 keploy/CONTRIBUTING.md
create mode 100644 keploy/DEBUG.md
create mode 100755 keploy/Dockerfile
create mode 100644 keploy/HACKTOBERFEST_GUIDE.md
create mode 100755 keploy/LICENSE
create mode 100644 keploy/README-UnitGen.md
create mode 100755 keploy/README.md
create mode 100644 keploy/READMEes-Es.md
create mode 100644 keploy/READMEja-JP.md
create mode 100755 keploy/SECURITY.md
create mode 100755 keploy/cli/README.md
create mode 100644 keploy/cli/cli.go
create mode 100644 keploy/cli/config.go
create mode 100644 keploy/cli/contract.go
create mode 100755 keploy/cli/examples.go
create mode 100644 keploy/cli/export.go
create mode 100644 keploy/cli/import.go
create mode 100644 keploy/cli/load.go
create mode 100644 keploy/cli/login.go
create mode 100644 keploy/cli/mock.go
create mode 100644 keploy/cli/normalise.go
create mode 100644 keploy/cli/provider/cmd.go
create mode 100644 keploy/cli/provider/common.go
create mode 100644 keploy/cli/provider/compat_linux.go
create mode 100644 keploy/cli/provider/compat_others.go
create mode 100644 keploy/cli/provider/core_service_linux.go
create mode 100644 keploy/cli/provider/core_service_others.go
create mode 100644 keploy/cli/provider/docker.go
create mode 100644 keploy/cli/provider/service.go
create mode 100644 keploy/cli/provider/util.go
create mode 100755 keploy/cli/record.go
create mode 100644 keploy/cli/report.go
create mode 100644 keploy/cli/rerecord.go
create mode 100755 keploy/cli/root.go
create mode 100644 keploy/cli/secure.go
create mode 100644 keploy/cli/service.go
create mode 100644 keploy/cli/templatize.go
create mode 100755 keploy/cli/test.go
create mode 100644 keploy/cli/testsuite.go
create mode 100644 keploy/cli/update.go
create mode 100644 keploy/cli/utgen.go
create mode 100644 keploy/config/config.go
create mode 100644 keploy/config/default.go
create mode 100755 keploy/entrypoint.sh
create mode 100755 keploy/go.mod
create mode 100644 keploy/go.sum
create mode 100755 keploy/gon.json
create mode 100755 keploy/goreleaser.yaml
create mode 100644 keploy/gsoc25_performance&security-testing.md
create mode 100644 keploy/keploy.sh
create mode 100755 keploy/main.go
create mode 100644 keploy/oss-pledge.json
create mode 100644 keploy/package-lock.json
create mode 100755 keploy/pkg/README.md
create mode 100644 keploy/pkg/core/app/app.go
create mode 100644 keploy/pkg/core/app/util.go
create mode 100644 keploy/pkg/core/core_linux.go
create mode 100644 keploy/pkg/core/core_others.go
create mode 100644 keploy/pkg/core/core_others_test.go
create mode 100755 keploy/pkg/core/hooks/README.md
create mode 100644 keploy/pkg/core/hooks/bpf_arm64_bpfel.go
create mode 100644 keploy/pkg/core/hooks/bpf_arm64_bpfel.o
create mode 100644 keploy/pkg/core/hooks/bpf_x86_bpfel.go
create mode 100644 keploy/pkg/core/hooks/bpf_x86_bpfel.o
create mode 100755 keploy/pkg/core/hooks/conn/README.md
create mode 100644 keploy/pkg/core/hooks/conn/conn.go
create mode 100755 keploy/pkg/core/hooks/conn/factory.go
create mode 100644 keploy/pkg/core/hooks/conn/socket.go
create mode 100755 keploy/pkg/core/hooks/conn/tracker.go
create mode 100755 keploy/pkg/core/hooks/conn/util.go
create mode 100644 keploy/pkg/core/hooks/hooks.go
create mode 100644 keploy/pkg/core/hooks/hooks_test.go
create mode 100644 keploy/pkg/core/hooks/kernelComm.go
create mode 100755 keploy/pkg/core/hooks/structs/structs.go
create mode 100644 keploy/pkg/core/hooks/util.go
create mode 100755 keploy/pkg/core/proxy/README.md
create mode 100644 keploy/pkg/core/proxy/dns.go
create mode 100755 keploy/pkg/core/proxy/integrations/README.md
create mode 100644 keploy/pkg/core/proxy/integrations/generic/README.md
create mode 100644 keploy/pkg/core/proxy/integrations/generic/decode.go
create mode 100644 keploy/pkg/core/proxy/integrations/generic/encode.go
create mode 100755 keploy/pkg/core/proxy/integrations/generic/generic.go
create mode 100755 keploy/pkg/core/proxy/integrations/generic/match.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpc/decode.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpc/encode.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpc/frame.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpc/grpc.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpc/match.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpc/stream.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpc/transcoder.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpc/util.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpcV2/codec.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpcV2/grpc.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpcV2/helper.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpcV2/listener.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpcV2/match.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpcV2/mock.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpcV2/record.go
create mode 100644 keploy/pkg/core/proxy/integrations/grpcV2/replayconn.go
create mode 100755 keploy/pkg/core/proxy/integrations/http/README.md
create mode 100644 keploy/pkg/core/proxy/integrations/http/chunk.go
create mode 100644 keploy/pkg/core/proxy/integrations/http/decode.go
create mode 100644 keploy/pkg/core/proxy/integrations/http/encode.go
create mode 100755 keploy/pkg/core/proxy/integrations/http/http.go
create mode 100644 keploy/pkg/core/proxy/integrations/http/match.go
create mode 100644 keploy/pkg/core/proxy/integrations/http/util.go
create mode 100644 keploy/pkg/core/proxy/integrations/integrations.go
create mode 100644 keploy/pkg/core/proxy/integrations/mongo/README.md
create mode 100644 keploy/pkg/core/proxy/integrations/mongo/command.go
create mode 100644 keploy/pkg/core/proxy/integrations/mongo/decode.go
create mode 100644 keploy/pkg/core/proxy/integrations/mongo/encode.go
create mode 100644 keploy/pkg/core/proxy/integrations/mongo/match.go
create mode 100644 keploy/pkg/core/proxy/integrations/mongo/mongo.go
create mode 100644 keploy/pkg/core/proxy/integrations/mongo/operation.go
create mode 100644 keploy/pkg/core/proxy/integrations/mongo/scramAuth.go
create mode 100644 keploy/pkg/core/proxy/integrations/mongo/util.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/README.md
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/mysql.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/panic_fix_test.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/recorder/conn.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/recorder/query.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/recorder/record.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/replayer/conn.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/replayer/match.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/replayer/query.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/replayer/replay.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/utils/util.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/utils/util_test.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/decode.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/encode.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/conn/authMoreDataPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/conn/authNextFactorPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/conn/authSwitchRequestPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/conn/authSwitchResponsePacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/conn/handshakeResponse41Packet.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/conn/handshakeResponse41Packet_test.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/conn/handshakeV10Packet.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/generic.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/ResultSetPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/preparedstmt/StmtPrepareOkPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/preparedstmt/stmtClosePacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/preparedstmt/stmtExecutePacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/preparedstmt/stmtExecutePacket_test.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/preparedstmt/stmtFetchPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/preparedstmt/stmtPreparePacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/preparedstmt/stmtResetPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/preparedstmt/stmtSendLongDataPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/queryPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/rowscols/binaryProtocolRowPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/rowscols/columnCountPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/rowscols/columnPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/rowscols/textRowPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/utility/initDbPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/phase/query/utility/setOptionPacket.go
create mode 100644 keploy/pkg/core/proxy/integrations/mysql/wire/util.go
create mode 100644 keploy/pkg/core/proxy/integrations/postgres/v1/decode.go
create mode 100755 keploy/pkg/core/proxy/integrations/postgres/v1/encode.go
create mode 100644 keploy/pkg/core/proxy/integrations/postgres/v1/match.go
create mode 100755 keploy/pkg/core/proxy/integrations/postgres/v1/postgres.go
create mode 100644 keploy/pkg/core/proxy/integrations/postgres/v1/transcoder.go
create mode 100755 keploy/pkg/core/proxy/integrations/postgres/v1/util.go
create mode 100644 keploy/pkg/core/proxy/integrations/redis/decode.go
create mode 100644 keploy/pkg/core/proxy/integrations/redis/encode.go
create mode 100755 keploy/pkg/core/proxy/integrations/redis/match.go
create mode 100755 keploy/pkg/core/proxy/integrations/redis/redis.go
create mode 100644 keploy/pkg/core/proxy/integrations/scram/scram.go
create mode 100644 keploy/pkg/core/proxy/integrations/scram/util.go
create mode 100644 keploy/pkg/core/proxy/integrations/util/util.go
create mode 100644 keploy/pkg/core/proxy/mockmanager.go
create mode 100755 keploy/pkg/core/proxy/options.go
create mode 100644 keploy/pkg/core/proxy/parsers.go
create mode 100755 keploy/pkg/core/proxy/proxy.go
create mode 100755 keploy/pkg/core/proxy/tls/asset/ca.crt
create mode 100755 keploy/pkg/core/proxy/tls/asset/ca.key
create mode 100755 keploy/pkg/core/proxy/tls/asset/setup_ca.sh
create mode 100644 keploy/pkg/core/proxy/tls/ca.go
create mode 100644 keploy/pkg/core/proxy/tls/tls.go
create mode 100644 keploy/pkg/core/proxy/treedb.go
create mode 100644 keploy/pkg/core/proxy/util.go
create mode 100755 keploy/pkg/core/proxy/util/util.go
create mode 100644 keploy/pkg/core/record.go
create mode 100644 keploy/pkg/core/replay.go
create mode 100644 keploy/pkg/core/service.go
create mode 100644 keploy/pkg/core/tester/tester.go
create mode 100644 keploy/pkg/http2.go
create mode 100644 keploy/pkg/matcher/grpc/canonical.go
create mode 100644 keploy/pkg/matcher/grpc/match.go
create mode 100644 keploy/pkg/matcher/http/absmatch.go
create mode 100644 keploy/pkg/matcher/http/match.go
create mode 100644 keploy/pkg/matcher/http/match_test.go
create mode 100644 keploy/pkg/matcher/http/utils.go
create mode 100644 keploy/pkg/matcher/schema/match.go
create mode 100644 keploy/pkg/matcher/utils.go
create mode 100755 keploy/pkg/models/README.md
create mode 100644 keploy/pkg/models/assertions.go
create mode 100644 keploy/pkg/models/auth.go
create mode 100644 keploy/pkg/models/config.go
create mode 100755 keploy/pkg/models/const.go
create mode 100644 keploy/pkg/models/errors.go
create mode 100644 keploy/pkg/models/generic.go
create mode 100644 keploy/pkg/models/github.go
create mode 100644 keploy/pkg/models/grpc.go
create mode 100755 keploy/pkg/models/http.go
create mode 100644 keploy/pkg/models/instrument.go
create mode 100755 keploy/pkg/models/mock.go
create mode 100644 keploy/pkg/models/mock_Secret.go
create mode 100755 keploy/pkg/models/mode.go
create mode 100755 keploy/pkg/models/mongo.go
create mode 100644 keploy/pkg/models/mysql/comm.go
create mode 100644 keploy/pkg/models/mysql/conn.go
create mode 100644 keploy/pkg/models/mysql/const.go
create mode 100644 keploy/pkg/models/mysql/generic.go
create mode 100644 keploy/pkg/models/mysql/mysql.go
create mode 100644 keploy/pkg/models/openapi.go
create mode 100755 keploy/pkg/models/postgres.go
create mode 100644 keploy/pkg/models/redis.go
create mode 100755 keploy/pkg/models/tele.go
create mode 100755 keploy/pkg/models/testcase.go
create mode 100644 keploy/pkg/models/testcompare.go
create mode 100755 keploy/pkg/models/testrun.go
create mode 100644 keploy/pkg/models/ut.go
create mode 100755 keploy/pkg/platform/README.md
create mode 100644 keploy/pkg/platform/auth/auth.go
create mode 100644 keploy/pkg/platform/coverage/csharp/csharp.go
create mode 100644 keploy/pkg/platform/coverage/csharp/utils.go
create mode 100644 keploy/pkg/platform/coverage/golang/golang.go
create mode 100644 keploy/pkg/platform/coverage/golang/utils.go
create mode 100644 keploy/pkg/platform/coverage/java/java.go
create mode 100644 keploy/pkg/platform/coverage/java/utils.go
create mode 100644 keploy/pkg/platform/coverage/javascript/javascript.go
create mode 100644 keploy/pkg/platform/coverage/javascript/utils.go
create mode 100644 keploy/pkg/platform/coverage/python/python.go
create mode 100644 keploy/pkg/platform/coverage/python/utils.go
create mode 100644 keploy/pkg/platform/coverage/service.go
create mode 100644 keploy/pkg/platform/docker/docker.go
create mode 100644 keploy/pkg/platform/docker/service.go
create mode 100644 keploy/pkg/platform/docker/util.go
create mode 100644 keploy/pkg/platform/storage/storage.go
create mode 100644 keploy/pkg/platform/telemetry/telemetry.go
create mode 100644 keploy/pkg/platform/telemetry/utils.go
create mode 100644 keploy/pkg/platform/yaml/configdb/testset/db.go
create mode 100644 keploy/pkg/platform/yaml/configdb/user/db.go
create mode 100644 keploy/pkg/platform/yaml/mockdb/db.go
create mode 100644 keploy/pkg/platform/yaml/mockdb/util.go
create mode 100644 keploy/pkg/platform/yaml/openapidb/db.go
create mode 100755 keploy/pkg/platform/yaml/reportdb/db.go
create mode 100644 keploy/pkg/platform/yaml/testdb/db.go
create mode 100644 keploy/pkg/platform/yaml/testdb/util.go
create mode 100755 keploy/pkg/platform/yaml/utils.go
create mode 100755 keploy/pkg/platform/yaml/yaml.go
create mode 100755 keploy/pkg/service/README.md
create mode 100644 keploy/pkg/service/contract/consumer/consumer.go
create mode 100644 keploy/pkg/service/contract/consumer/service.go
create mode 100644 keploy/pkg/service/contract/contract.go
create mode 100644 keploy/pkg/service/contract/provider/provider.go
create mode 100644 keploy/pkg/service/contract/provider/service.go
create mode 100644 keploy/pkg/service/contract/schema.go
create mode 100644 keploy/pkg/service/contract/service.go
create mode 100644 keploy/pkg/service/contract/utils.go
create mode 100644 keploy/pkg/service/export/export.go
create mode 100644 keploy/pkg/service/import/import.go
create mode 100644 keploy/pkg/service/load/dashboard_exposer.go
create mode 100644 keploy/pkg/service/load/exporter.go
create mode 100644 keploy/pkg/service/load/load.go
create mode 100644 keploy/pkg/service/load/metrics_collector.go
create mode 100644 keploy/pkg/service/load/out/404.html
create mode 100644 keploy/pkg/service/load/out/404/index.html
create mode 100644 keploy/pkg/service/load/out/_next/static/S6A95ZgpgxS8RHL4D2qnv/_buildManifest.js
create mode 100644 keploy/pkg/service/load/out/_next/static/S6A95ZgpgxS8RHL4D2qnv/_ssgManifest.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/4bd1b696-cf72ae8a39fa05aa.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/547-586c3cf76649ec2c.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/964-69097a61540f27b4.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/app/_not-found/page-7c3ecdf160dc3360.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/app/layout-bc503d5738af696e.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/app/page-a3cd1a73fe99bbf3.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/framework-7c95b8e5103c9e90.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/main-a5da5fd7e32dc553.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/main-app-b3d2ebcb2857645a.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/pages/_app-0a0020ddd67f79cf.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/pages/_error-03529f2c21436739.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/polyfills-42372ed130431b0a.js
create mode 100644 keploy/pkg/service/load/out/_next/static/chunks/webpack-cb07c2fe276ae3fa.js
create mode 100644 keploy/pkg/service/load/out/_next/static/css/bc1768f92951bee0.css
create mode 100644 keploy/pkg/service/load/out/_next/static/media/26a46d62cd723877-s.woff2
create mode 100644 keploy/pkg/service/load/out/_next/static/media/55c55f0601d81cf3-s.woff2
create mode 100644 keploy/pkg/service/load/out/_next/static/media/581909926a08bbc8-s.woff2
create mode 100644 keploy/pkg/service/load/out/_next/static/media/8e9860b6e62d6359-s.woff2
create mode 100644 keploy/pkg/service/load/out/_next/static/media/97e0cb1ae144a2a9-s.woff2
create mode 100644 keploy/pkg/service/load/out/_next/static/media/df0a9ae256c0569c-s.woff2
create mode 100644 keploy/pkg/service/load/out/_next/static/media/e4af272ccee01ff0-s.p.woff2
create mode 100644 keploy/pkg/service/load/out/favicon.ico
create mode 100644 keploy/pkg/service/load/out/index.html
create mode 100644 keploy/pkg/service/load/out/index.txt
create mode 100644 keploy/pkg/service/load/out/manifest.json
create mode 100644 keploy/pkg/service/load/out/sw.js
create mode 100644 keploy/pkg/service/load/scheduler.go
create mode 100644 keploy/pkg/service/load/service.go
create mode 100644 keploy/pkg/service/load/threshold_evaluator.go
create mode 100644 keploy/pkg/service/load/utils.go
create mode 100644 keploy/pkg/service/load/vu_worker.go
create mode 100644 keploy/pkg/service/orchestrator/orchestrator.go
create mode 100644 keploy/pkg/service/orchestrator/rerecord.go
create mode 100644 keploy/pkg/service/orchestrator/service.go
create mode 100755 keploy/pkg/service/record/record.go
create mode 100755 keploy/pkg/service/record/service.go
create mode 100644 keploy/pkg/service/record/utils.go
create mode 100644 keploy/pkg/service/replay/hooks.go
create mode 100644 keploy/pkg/service/replay/integration_test.go
create mode 100644 keploy/pkg/service/replay/mock.go
create mode 100644 keploy/pkg/service/replay/replay.go
create mode 100644 keploy/pkg/service/replay/replay_test.go
create mode 100644 keploy/pkg/service/replay/service.go
create mode 100644 keploy/pkg/service/replay/service_test.go
create mode 100644 keploy/pkg/service/replay/utils.go
create mode 100644 keploy/pkg/service/report/report.go
create mode 100644 keploy/pkg/service/report/service.go
create mode 100644 keploy/pkg/service/secure/secure.go
create mode 100644 keploy/pkg/service/secure/service.go
create mode 100644 keploy/pkg/service/secure/utils.go
create mode 100644 keploy/pkg/service/service.go
create mode 100644 keploy/pkg/service/testsuite/service.go
create mode 100644 keploy/pkg/service/testsuite/testsuite.go
create mode 100644 keploy/pkg/service/testsuite/utils.go
create mode 100644 keploy/pkg/service/tools/mock_Service.go
create mode 100644 keploy/pkg/service/tools/mock_TestDB.go
create mode 100644 keploy/pkg/service/tools/mock_TestSetConfig.go
create mode 100644 keploy/pkg/service/tools/service.go
create mode 100644 keploy/pkg/service/tools/templatize.go
create mode 100644 keploy/pkg/service/tools/templatize_test.go
create mode 100644 keploy/pkg/service/tools/tools.go
create mode 100644 keploy/pkg/service/utgen/ai.go
create mode 100644 keploy/pkg/service/utgen/assets/config.go
create mode 100644 keploy/pkg/service/utgen/assets/indentation.toml
create mode 100644 keploy/pkg/service/utgen/assets/insert_line.toml
create mode 100644 keploy/pkg/service/utgen/assets/language.toml
create mode 100644 keploy/pkg/service/utgen/assets/test_generation.toml
create mode 100644 keploy/pkg/service/utgen/coverage.go
create mode 100644 keploy/pkg/service/utgen/gen.go
create mode 100644 keploy/pkg/service/utgen/injector.go
create mode 100644 keploy/pkg/service/utgen/prompt.go
create mode 100644 keploy/pkg/service/utgen/service.go
create mode 100644 keploy/pkg/service/utgen/utils.go
create mode 100755 keploy/pkg/util.go
create mode 100644 keploy/pkg/util_test.go
create mode 100644 keploy/utils/ctx.go
create mode 100644 keploy/utils/inc.go
create mode 100644 keploy/utils/log/colors.go
create mode 100644 keploy/utils/log/logger.go
create mode 100644 keploy/utils/log/time.go
create mode 100644 keploy/utils/mask_others.go
create mode 100644 keploy/utils/mask_windows.go
create mode 100644 keploy/utils/signal_others.go
create mode 100644 keploy/utils/signal_windows.go
create mode 100644 keploy/utils/utils.go
create mode 100644 keploy/utils/utils_test.go
create mode 100755 scripts/user_service_flow.sh
create mode 100755 scripts/user_service_manual_tests.sh
create mode 100755 user_service/keploy-logs.txt
create mode 100755 user_service/keploy/atg/mocks.yaml
create mode 100755 user_service/keploy/atg/tests/test-1.yaml
create mode 100755 user_service/keploy/atg/tests/test-10.yaml
create mode 100755 user_service/keploy/atg/tests/test-100.yaml
create mode 100755 user_service/keploy/atg/tests/test-101.yaml
create mode 100755 user_service/keploy/atg/tests/test-102.yaml
create mode 100755 user_service/keploy/atg/tests/test-103.yaml
create mode 100755 user_service/keploy/atg/tests/test-104.yaml
create mode 100755 user_service/keploy/atg/tests/test-105.yaml
create mode 100755 user_service/keploy/atg/tests/test-106.yaml
create mode 100755 user_service/keploy/atg/tests/test-107.yaml
create mode 100755 user_service/keploy/atg/tests/test-108.yaml
create mode 100755 user_service/keploy/atg/tests/test-109.yaml
create mode 100755 user_service/keploy/atg/tests/test-11.yaml
create mode 100755 user_service/keploy/atg/tests/test-110.yaml
create mode 100755 user_service/keploy/atg/tests/test-111.yaml
create mode 100755 user_service/keploy/atg/tests/test-112.yaml
create mode 100755 user_service/keploy/atg/tests/test-113.yaml
create mode 100755 user_service/keploy/atg/tests/test-114.yaml
create mode 100755 user_service/keploy/atg/tests/test-115.yaml
create mode 100755 user_service/keploy/atg/tests/test-116.yaml
create mode 100755 user_service/keploy/atg/tests/test-117.yaml
create mode 100755 user_service/keploy/atg/tests/test-118.yaml
create mode 100755 user_service/keploy/atg/tests/test-119.yaml
create mode 100755 user_service/keploy/atg/tests/test-12.yaml
create mode 100755 user_service/keploy/atg/tests/test-120.yaml
create mode 100755 user_service/keploy/atg/tests/test-121.yaml
create mode 100755 user_service/keploy/atg/tests/test-122.yaml
create mode 100755 user_service/keploy/atg/tests/test-123.yaml
create mode 100755 user_service/keploy/atg/tests/test-124.yaml
create mode 100755 user_service/keploy/atg/tests/test-125.yaml
create mode 100755 user_service/keploy/atg/tests/test-126.yaml
create mode 100755 user_service/keploy/atg/tests/test-127.yaml
create mode 100755 user_service/keploy/atg/tests/test-128.yaml
create mode 100755 user_service/keploy/atg/tests/test-129.yaml
create mode 100755 user_service/keploy/atg/tests/test-13.yaml
create mode 100755 user_service/keploy/atg/tests/test-130.yaml
create mode 100755 user_service/keploy/atg/tests/test-131.yaml
create mode 100755 user_service/keploy/atg/tests/test-132.yaml
create mode 100755 user_service/keploy/atg/tests/test-133.yaml
create mode 100755 user_service/keploy/atg/tests/test-134.yaml
create mode 100755 user_service/keploy/atg/tests/test-135.yaml
create mode 100755 user_service/keploy/atg/tests/test-136.yaml
create mode 100755 user_service/keploy/atg/tests/test-137.yaml
create mode 100755 user_service/keploy/atg/tests/test-138.yaml
create mode 100755 user_service/keploy/atg/tests/test-139.yaml
create mode 100755 user_service/keploy/atg/tests/test-14.yaml
create mode 100755 user_service/keploy/atg/tests/test-140.yaml
create mode 100755 user_service/keploy/atg/tests/test-141.yaml
create mode 100755 user_service/keploy/atg/tests/test-142.yaml
create mode 100755 user_service/keploy/atg/tests/test-143.yaml
create mode 100755 user_service/keploy/atg/tests/test-144.yaml
create mode 100755 user_service/keploy/atg/tests/test-145.yaml
create mode 100755 user_service/keploy/atg/tests/test-146.yaml
create mode 100755 user_service/keploy/atg/tests/test-147.yaml
create mode 100755 user_service/keploy/atg/tests/test-148.yaml
create mode 100755 user_service/keploy/atg/tests/test-149.yaml
create mode 100755 user_service/keploy/atg/tests/test-15.yaml
create mode 100755 user_service/keploy/atg/tests/test-150.yaml
create mode 100755 user_service/keploy/atg/tests/test-151.yaml
create mode 100755 user_service/keploy/atg/tests/test-152.yaml
create mode 100755 user_service/keploy/atg/tests/test-153.yaml
create mode 100755 user_service/keploy/atg/tests/test-154.yaml
create mode 100755 user_service/keploy/atg/tests/test-155.yaml
create mode 100755 user_service/keploy/atg/tests/test-156.yaml
create mode 100755 user_service/keploy/atg/tests/test-157.yaml
create mode 100755 user_service/keploy/atg/tests/test-158.yaml
create mode 100755 user_service/keploy/atg/tests/test-159.yaml
create mode 100755 user_service/keploy/atg/tests/test-16.yaml
create mode 100755 user_service/keploy/atg/tests/test-160.yaml
create mode 100755 user_service/keploy/atg/tests/test-161.yaml
create mode 100755 user_service/keploy/atg/tests/test-162.yaml
create mode 100755 user_service/keploy/atg/tests/test-163.yaml
create mode 100755 user_service/keploy/atg/tests/test-164.yaml
create mode 100755 user_service/keploy/atg/tests/test-165.yaml
create mode 100755 user_service/keploy/atg/tests/test-166.yaml
create mode 100755 user_service/keploy/atg/tests/test-167.yaml
create mode 100755 user_service/keploy/atg/tests/test-168.yaml
create mode 100755 user_service/keploy/atg/tests/test-169.yaml
create mode 100755 user_service/keploy/atg/tests/test-17.yaml
create mode 100755 user_service/keploy/atg/tests/test-170.yaml
create mode 100755 user_service/keploy/atg/tests/test-171.yaml
create mode 100755 user_service/keploy/atg/tests/test-172.yaml
create mode 100755 user_service/keploy/atg/tests/test-173.yaml
create mode 100755 user_service/keploy/atg/tests/test-174.yaml
create mode 100755 user_service/keploy/atg/tests/test-175.yaml
create mode 100755 user_service/keploy/atg/tests/test-176.yaml
create mode 100755 user_service/keploy/atg/tests/test-177.yaml
create mode 100755 user_service/keploy/atg/tests/test-178.yaml
create mode 100755 user_service/keploy/atg/tests/test-179.yaml
create mode 100755 user_service/keploy/atg/tests/test-18.yaml
create mode 100755 user_service/keploy/atg/tests/test-180.yaml
create mode 100755 user_service/keploy/atg/tests/test-181.yaml
create mode 100755 user_service/keploy/atg/tests/test-182.yaml
create mode 100755 user_service/keploy/atg/tests/test-183.yaml
create mode 100755 user_service/keploy/atg/tests/test-184.yaml
create mode 100755 user_service/keploy/atg/tests/test-185.yaml
create mode 100755 user_service/keploy/atg/tests/test-186.yaml
create mode 100755 user_service/keploy/atg/tests/test-187.yaml
create mode 100755 user_service/keploy/atg/tests/test-188.yaml
create mode 100755 user_service/keploy/atg/tests/test-189.yaml
create mode 100755 user_service/keploy/atg/tests/test-19.yaml
create mode 100755 user_service/keploy/atg/tests/test-190.yaml
create mode 100755 user_service/keploy/atg/tests/test-191.yaml
create mode 100755 user_service/keploy/atg/tests/test-192.yaml
create mode 100755 user_service/keploy/atg/tests/test-193.yaml
create mode 100755 user_service/keploy/atg/tests/test-194.yaml
create mode 100755 user_service/keploy/atg/tests/test-195.yaml
create mode 100755 user_service/keploy/atg/tests/test-196.yaml
create mode 100755 user_service/keploy/atg/tests/test-197.yaml
create mode 100755 user_service/keploy/atg/tests/test-198.yaml
create mode 100755 user_service/keploy/atg/tests/test-199.yaml
create mode 100755 user_service/keploy/atg/tests/test-2.yaml
create mode 100755 user_service/keploy/atg/tests/test-20.yaml
create mode 100755 user_service/keploy/atg/tests/test-200.yaml
create mode 100755 user_service/keploy/atg/tests/test-201.yaml
create mode 100755 user_service/keploy/atg/tests/test-202.yaml
create mode 100755 user_service/keploy/atg/tests/test-203.yaml
create mode 100755 user_service/keploy/atg/tests/test-204.yaml
create mode 100755 user_service/keploy/atg/tests/test-205.yaml
create mode 100755 user_service/keploy/atg/tests/test-206.yaml
create mode 100755 user_service/keploy/atg/tests/test-207.yaml
create mode 100755 user_service/keploy/atg/tests/test-208.yaml
create mode 100755 user_service/keploy/atg/tests/test-209.yaml
create mode 100755 user_service/keploy/atg/tests/test-21.yaml
create mode 100755 user_service/keploy/atg/tests/test-210.yaml
create mode 100755 user_service/keploy/atg/tests/test-211.yaml
create mode 100755 user_service/keploy/atg/tests/test-212.yaml
create mode 100755 user_service/keploy/atg/tests/test-213.yaml
create mode 100755 user_service/keploy/atg/tests/test-214.yaml
create mode 100755 user_service/keploy/atg/tests/test-215.yaml
create mode 100755 user_service/keploy/atg/tests/test-216.yaml
create mode 100755 user_service/keploy/atg/tests/test-217.yaml
create mode 100755 user_service/keploy/atg/tests/test-218.yaml
create mode 100755 user_service/keploy/atg/tests/test-219.yaml
create mode 100755 user_service/keploy/atg/tests/test-22.yaml
create mode 100755 user_service/keploy/atg/tests/test-220.yaml
create mode 100755 user_service/keploy/atg/tests/test-221.yaml
create mode 100755 user_service/keploy/atg/tests/test-222.yaml
create mode 100755 user_service/keploy/atg/tests/test-223.yaml
create mode 100755 user_service/keploy/atg/tests/test-224.yaml
create mode 100755 user_service/keploy/atg/tests/test-225.yaml
create mode 100755 user_service/keploy/atg/tests/test-226.yaml
create mode 100755 user_service/keploy/atg/tests/test-227.yaml
create mode 100755 user_service/keploy/atg/tests/test-228.yaml
create mode 100755 user_service/keploy/atg/tests/test-229.yaml
create mode 100755 user_service/keploy/atg/tests/test-23.yaml
create mode 100755 user_service/keploy/atg/tests/test-230.yaml
create mode 100755 user_service/keploy/atg/tests/test-231.yaml
create mode 100755 user_service/keploy/atg/tests/test-232.yaml
create mode 100755 user_service/keploy/atg/tests/test-233.yaml
create mode 100755 user_service/keploy/atg/tests/test-234.yaml
create mode 100755 user_service/keploy/atg/tests/test-235.yaml
create mode 100755 user_service/keploy/atg/tests/test-236.yaml
create mode 100755 user_service/keploy/atg/tests/test-237.yaml
create mode 100755 user_service/keploy/atg/tests/test-238.yaml
create mode 100755 user_service/keploy/atg/tests/test-239.yaml
create mode 100755 user_service/keploy/atg/tests/test-24.yaml
create mode 100755 user_service/keploy/atg/tests/test-240.yaml
create mode 100755 user_service/keploy/atg/tests/test-241.yaml
create mode 100755 user_service/keploy/atg/tests/test-242.yaml
create mode 100755 user_service/keploy/atg/tests/test-243.yaml
create mode 100755 user_service/keploy/atg/tests/test-244.yaml
create mode 100755 user_service/keploy/atg/tests/test-245.yaml
create mode 100755 user_service/keploy/atg/tests/test-246.yaml
create mode 100755 user_service/keploy/atg/tests/test-247.yaml
create mode 100755 user_service/keploy/atg/tests/test-248.yaml
create mode 100755 user_service/keploy/atg/tests/test-249.yaml
create mode 100755 user_service/keploy/atg/tests/test-25.yaml
create mode 100755 user_service/keploy/atg/tests/test-250.yaml
create mode 100755 user_service/keploy/atg/tests/test-251.yaml
create mode 100755 user_service/keploy/atg/tests/test-252.yaml
create mode 100755 user_service/keploy/atg/tests/test-253.yaml
create mode 100755 user_service/keploy/atg/tests/test-254.yaml
create mode 100755 user_service/keploy/atg/tests/test-255.yaml
create mode 100755 user_service/keploy/atg/tests/test-256.yaml
create mode 100755 user_service/keploy/atg/tests/test-257.yaml
create mode 100755 user_service/keploy/atg/tests/test-258.yaml
create mode 100755 user_service/keploy/atg/tests/test-259.yaml
create mode 100755 user_service/keploy/atg/tests/test-26.yaml
create mode 100755 user_service/keploy/atg/tests/test-260.yaml
create mode 100755 user_service/keploy/atg/tests/test-261.yaml
create mode 100755 user_service/keploy/atg/tests/test-262.yaml
create mode 100755 user_service/keploy/atg/tests/test-263.yaml
create mode 100755 user_service/keploy/atg/tests/test-264.yaml
create mode 100755 user_service/keploy/atg/tests/test-265.yaml
create mode 100755 user_service/keploy/atg/tests/test-266.yaml
create mode 100755 user_service/keploy/atg/tests/test-267.yaml
create mode 100755 user_service/keploy/atg/tests/test-268.yaml
create mode 100755 user_service/keploy/atg/tests/test-269.yaml
create mode 100755 user_service/keploy/atg/tests/test-27.yaml
create mode 100755 user_service/keploy/atg/tests/test-270.yaml
create mode 100755 user_service/keploy/atg/tests/test-271.yaml
create mode 100755 user_service/keploy/atg/tests/test-272.yaml
create mode 100755 user_service/keploy/atg/tests/test-273.yaml
create mode 100755 user_service/keploy/atg/tests/test-274.yaml
create mode 100755 user_service/keploy/atg/tests/test-275.yaml
create mode 100755 user_service/keploy/atg/tests/test-276.yaml
create mode 100755 user_service/keploy/atg/tests/test-277.yaml
create mode 100755 user_service/keploy/atg/tests/test-278.yaml
create mode 100755 user_service/keploy/atg/tests/test-279.yaml
create mode 100755 user_service/keploy/atg/tests/test-28.yaml
create mode 100755 user_service/keploy/atg/tests/test-280.yaml
create mode 100755 user_service/keploy/atg/tests/test-281.yaml
create mode 100755 user_service/keploy/atg/tests/test-282.yaml
create mode 100755 user_service/keploy/atg/tests/test-283.yaml
create mode 100755 user_service/keploy/atg/tests/test-284.yaml
create mode 100755 user_service/keploy/atg/tests/test-285.yaml
create mode 100755 user_service/keploy/atg/tests/test-286.yaml
create mode 100755 user_service/keploy/atg/tests/test-287.yaml
create mode 100755 user_service/keploy/atg/tests/test-288.yaml
create mode 100755 user_service/keploy/atg/tests/test-289.yaml
create mode 100755 user_service/keploy/atg/tests/test-29.yaml
create mode 100755 user_service/keploy/atg/tests/test-290.yaml
create mode 100755 user_service/keploy/atg/tests/test-291.yaml
create mode 100755 user_service/keploy/atg/tests/test-292.yaml
create mode 100755 user_service/keploy/atg/tests/test-293.yaml
create mode 100755 user_service/keploy/atg/tests/test-294.yaml
create mode 100755 user_service/keploy/atg/tests/test-295.yaml
create mode 100755 user_service/keploy/atg/tests/test-296.yaml
create mode 100755 user_service/keploy/atg/tests/test-297.yaml
create mode 100755 user_service/keploy/atg/tests/test-298.yaml
create mode 100755 user_service/keploy/atg/tests/test-299.yaml
create mode 100755 user_service/keploy/atg/tests/test-3.yaml
create mode 100755 user_service/keploy/atg/tests/test-30.yaml
create mode 100755 user_service/keploy/atg/tests/test-300.yaml
create mode 100755 user_service/keploy/atg/tests/test-301.yaml
create mode 100755 user_service/keploy/atg/tests/test-302.yaml
create mode 100755 user_service/keploy/atg/tests/test-303.yaml
create mode 100755 user_service/keploy/atg/tests/test-304.yaml
create mode 100755 user_service/keploy/atg/tests/test-305.yaml
create mode 100755 user_service/keploy/atg/tests/test-306.yaml
create mode 100755 user_service/keploy/atg/tests/test-307.yaml
create mode 100755 user_service/keploy/atg/tests/test-308.yaml
create mode 100755 user_service/keploy/atg/tests/test-309.yaml
create mode 100755 user_service/keploy/atg/tests/test-31.yaml
create mode 100755 user_service/keploy/atg/tests/test-310.yaml
create mode 100755 user_service/keploy/atg/tests/test-311.yaml
create mode 100755 user_service/keploy/atg/tests/test-312.yaml
create mode 100755 user_service/keploy/atg/tests/test-313.yaml
create mode 100755 user_service/keploy/atg/tests/test-314.yaml
create mode 100755 user_service/keploy/atg/tests/test-315.yaml
create mode 100755 user_service/keploy/atg/tests/test-316.yaml
create mode 100755 user_service/keploy/atg/tests/test-317.yaml
create mode 100755 user_service/keploy/atg/tests/test-318.yaml
create mode 100755 user_service/keploy/atg/tests/test-319.yaml
create mode 100755 user_service/keploy/atg/tests/test-32.yaml
create mode 100755 user_service/keploy/atg/tests/test-320.yaml
create mode 100755 user_service/keploy/atg/tests/test-321.yaml
create mode 100755 user_service/keploy/atg/tests/test-322.yaml
create mode 100755 user_service/keploy/atg/tests/test-323.yaml
create mode 100755 user_service/keploy/atg/tests/test-324.yaml
create mode 100755 user_service/keploy/atg/tests/test-325.yaml
create mode 100755 user_service/keploy/atg/tests/test-326.yaml
create mode 100755 user_service/keploy/atg/tests/test-327.yaml
create mode 100755 user_service/keploy/atg/tests/test-328.yaml
create mode 100755 user_service/keploy/atg/tests/test-329.yaml
create mode 100755 user_service/keploy/atg/tests/test-33.yaml
create mode 100755 user_service/keploy/atg/tests/test-330.yaml
create mode 100755 user_service/keploy/atg/tests/test-331.yaml
create mode 100755 user_service/keploy/atg/tests/test-332.yaml
create mode 100755 user_service/keploy/atg/tests/test-333.yaml
create mode 100755 user_service/keploy/atg/tests/test-334.yaml
create mode 100755 user_service/keploy/atg/tests/test-335.yaml
create mode 100755 user_service/keploy/atg/tests/test-336.yaml
create mode 100755 user_service/keploy/atg/tests/test-337.yaml
create mode 100755 user_service/keploy/atg/tests/test-338.yaml
create mode 100755 user_service/keploy/atg/tests/test-339.yaml
create mode 100755 user_service/keploy/atg/tests/test-34.yaml
create mode 100755 user_service/keploy/atg/tests/test-340.yaml
create mode 100755 user_service/keploy/atg/tests/test-341.yaml
create mode 100755 user_service/keploy/atg/tests/test-342.yaml
create mode 100755 user_service/keploy/atg/tests/test-343.yaml
create mode 100755 user_service/keploy/atg/tests/test-344.yaml
create mode 100755 user_service/keploy/atg/tests/test-345.yaml
create mode 100755 user_service/keploy/atg/tests/test-346.yaml
create mode 100755 user_service/keploy/atg/tests/test-347.yaml
create mode 100755 user_service/keploy/atg/tests/test-348.yaml
create mode 100755 user_service/keploy/atg/tests/test-349.yaml
create mode 100755 user_service/keploy/atg/tests/test-35.yaml
create mode 100755 user_service/keploy/atg/tests/test-350.yaml
create mode 100755 user_service/keploy/atg/tests/test-351.yaml
create mode 100755 user_service/keploy/atg/tests/test-352.yaml
create mode 100755 user_service/keploy/atg/tests/test-353.yaml
create mode 100755 user_service/keploy/atg/tests/test-354.yaml
create mode 100755 user_service/keploy/atg/tests/test-355.yaml
create mode 100755 user_service/keploy/atg/tests/test-356.yaml
create mode 100755 user_service/keploy/atg/tests/test-357.yaml
create mode 100755 user_service/keploy/atg/tests/test-358.yaml
create mode 100755 user_service/keploy/atg/tests/test-359.yaml
create mode 100755 user_service/keploy/atg/tests/test-36.yaml
create mode 100755 user_service/keploy/atg/tests/test-360.yaml
create mode 100755 user_service/keploy/atg/tests/test-361.yaml
create mode 100755 user_service/keploy/atg/tests/test-362.yaml
create mode 100755 user_service/keploy/atg/tests/test-363.yaml
create mode 100755 user_service/keploy/atg/tests/test-364.yaml
create mode 100755 user_service/keploy/atg/tests/test-365.yaml
create mode 100755 user_service/keploy/atg/tests/test-366.yaml
create mode 100755 user_service/keploy/atg/tests/test-367.yaml
create mode 100755 user_service/keploy/atg/tests/test-368.yaml
create mode 100755 user_service/keploy/atg/tests/test-369.yaml
create mode 100755 user_service/keploy/atg/tests/test-37.yaml
create mode 100755 user_service/keploy/atg/tests/test-370.yaml
create mode 100755 user_service/keploy/atg/tests/test-371.yaml
create mode 100755 user_service/keploy/atg/tests/test-372.yaml
create mode 100755 user_service/keploy/atg/tests/test-373.yaml
create mode 100755 user_service/keploy/atg/tests/test-374.yaml
create mode 100755 user_service/keploy/atg/tests/test-375.yaml
create mode 100755 user_service/keploy/atg/tests/test-376.yaml
create mode 100755 user_service/keploy/atg/tests/test-377.yaml
create mode 100755 user_service/keploy/atg/tests/test-378.yaml
create mode 100755 user_service/keploy/atg/tests/test-379.yaml
create mode 100755 user_service/keploy/atg/tests/test-38.yaml
create mode 100755 user_service/keploy/atg/tests/test-380.yaml
create mode 100755 user_service/keploy/atg/tests/test-381.yaml
create mode 100755 user_service/keploy/atg/tests/test-382.yaml
create mode 100755 user_service/keploy/atg/tests/test-383.yaml
create mode 100755 user_service/keploy/atg/tests/test-384.yaml
create mode 100755 user_service/keploy/atg/tests/test-385.yaml
create mode 100755 user_service/keploy/atg/tests/test-386.yaml
create mode 100755 user_service/keploy/atg/tests/test-387.yaml
create mode 100755 user_service/keploy/atg/tests/test-388.yaml
create mode 100755 user_service/keploy/atg/tests/test-389.yaml
create mode 100755 user_service/keploy/atg/tests/test-39.yaml
create mode 100755 user_service/keploy/atg/tests/test-390.yaml
create mode 100755 user_service/keploy/atg/tests/test-391.yaml
create mode 100755 user_service/keploy/atg/tests/test-392.yaml
create mode 100755 user_service/keploy/atg/tests/test-393.yaml
create mode 100755 user_service/keploy/atg/tests/test-394.yaml
create mode 100755 user_service/keploy/atg/tests/test-395.yaml
create mode 100755 user_service/keploy/atg/tests/test-396.yaml
create mode 100755 user_service/keploy/atg/tests/test-397.yaml
create mode 100755 user_service/keploy/atg/tests/test-398.yaml
create mode 100755 user_service/keploy/atg/tests/test-399.yaml
create mode 100755 user_service/keploy/atg/tests/test-4.yaml
create mode 100755 user_service/keploy/atg/tests/test-40.yaml
create mode 100755 user_service/keploy/atg/tests/test-400.yaml
create mode 100755 user_service/keploy/atg/tests/test-401.yaml
create mode 100755 user_service/keploy/atg/tests/test-402.yaml
create mode 100755 user_service/keploy/atg/tests/test-403.yaml
create mode 100755 user_service/keploy/atg/tests/test-404.yaml
create mode 100755 user_service/keploy/atg/tests/test-405.yaml
create mode 100755 user_service/keploy/atg/tests/test-406.yaml
create mode 100755 user_service/keploy/atg/tests/test-407.yaml
create mode 100755 user_service/keploy/atg/tests/test-408.yaml
create mode 100755 user_service/keploy/atg/tests/test-409.yaml
create mode 100755 user_service/keploy/atg/tests/test-41.yaml
create mode 100755 user_service/keploy/atg/tests/test-410.yaml
create mode 100755 user_service/keploy/atg/tests/test-411.yaml
create mode 100755 user_service/keploy/atg/tests/test-412.yaml
create mode 100755 user_service/keploy/atg/tests/test-413.yaml
create mode 100755 user_service/keploy/atg/tests/test-414.yaml
create mode 100755 user_service/keploy/atg/tests/test-415.yaml
create mode 100755 user_service/keploy/atg/tests/test-416.yaml
create mode 100755 user_service/keploy/atg/tests/test-417.yaml
create mode 100755 user_service/keploy/atg/tests/test-418.yaml
create mode 100755 user_service/keploy/atg/tests/test-419.yaml
create mode 100755 user_service/keploy/atg/tests/test-42.yaml
create mode 100755 user_service/keploy/atg/tests/test-420.yaml
create mode 100755 user_service/keploy/atg/tests/test-421.yaml
create mode 100755 user_service/keploy/atg/tests/test-422.yaml
create mode 100755 user_service/keploy/atg/tests/test-423.yaml
create mode 100755 user_service/keploy/atg/tests/test-424.yaml
create mode 100755 user_service/keploy/atg/tests/test-425.yaml
create mode 100755 user_service/keploy/atg/tests/test-426.yaml
create mode 100755 user_service/keploy/atg/tests/test-427.yaml
create mode 100755 user_service/keploy/atg/tests/test-428.yaml
create mode 100755 user_service/keploy/atg/tests/test-429.yaml
create mode 100755 user_service/keploy/atg/tests/test-43.yaml
create mode 100755 user_service/keploy/atg/tests/test-430.yaml
create mode 100755 user_service/keploy/atg/tests/test-431.yaml
create mode 100755 user_service/keploy/atg/tests/test-432.yaml
create mode 100755 user_service/keploy/atg/tests/test-433.yaml
create mode 100755 user_service/keploy/atg/tests/test-434.yaml
create mode 100755 user_service/keploy/atg/tests/test-435.yaml
create mode 100755 user_service/keploy/atg/tests/test-436.yaml
create mode 100755 user_service/keploy/atg/tests/test-437.yaml
create mode 100755 user_service/keploy/atg/tests/test-438.yaml
create mode 100755 user_service/keploy/atg/tests/test-439.yaml
create mode 100755 user_service/keploy/atg/tests/test-44.yaml
create mode 100755 user_service/keploy/atg/tests/test-440.yaml
create mode 100755 user_service/keploy/atg/tests/test-441.yaml
create mode 100755 user_service/keploy/atg/tests/test-442.yaml
create mode 100755 user_service/keploy/atg/tests/test-443.yaml
create mode 100755 user_service/keploy/atg/tests/test-444.yaml
create mode 100755 user_service/keploy/atg/tests/test-445.yaml
create mode 100755 user_service/keploy/atg/tests/test-446.yaml
create mode 100755 user_service/keploy/atg/tests/test-447.yaml
create mode 100755 user_service/keploy/atg/tests/test-448.yaml
create mode 100755 user_service/keploy/atg/tests/test-449.yaml
create mode 100755 user_service/keploy/atg/tests/test-45.yaml
create mode 100755 user_service/keploy/atg/tests/test-450.yaml
create mode 100755 user_service/keploy/atg/tests/test-451.yaml
create mode 100755 user_service/keploy/atg/tests/test-452.yaml
create mode 100755 user_service/keploy/atg/tests/test-453.yaml
create mode 100755 user_service/keploy/atg/tests/test-454.yaml
create mode 100755 user_service/keploy/atg/tests/test-455.yaml
create mode 100755 user_service/keploy/atg/tests/test-456.yaml
create mode 100755 user_service/keploy/atg/tests/test-457.yaml
create mode 100755 user_service/keploy/atg/tests/test-458.yaml
create mode 100755 user_service/keploy/atg/tests/test-459.yaml
create mode 100755 user_service/keploy/atg/tests/test-46.yaml
create mode 100755 user_service/keploy/atg/tests/test-460.yaml
create mode 100755 user_service/keploy/atg/tests/test-461.yaml
create mode 100755 user_service/keploy/atg/tests/test-462.yaml
create mode 100755 user_service/keploy/atg/tests/test-463.yaml
create mode 100755 user_service/keploy/atg/tests/test-464.yaml
create mode 100755 user_service/keploy/atg/tests/test-465.yaml
create mode 100755 user_service/keploy/atg/tests/test-466.yaml
create mode 100755 user_service/keploy/atg/tests/test-467.yaml
create mode 100755 user_service/keploy/atg/tests/test-468.yaml
create mode 100755 user_service/keploy/atg/tests/test-469.yaml
create mode 100755 user_service/keploy/atg/tests/test-47.yaml
create mode 100755 user_service/keploy/atg/tests/test-470.yaml
create mode 100755 user_service/keploy/atg/tests/test-471.yaml
create mode 100755 user_service/keploy/atg/tests/test-472.yaml
create mode 100755 user_service/keploy/atg/tests/test-473.yaml
create mode 100755 user_service/keploy/atg/tests/test-474.yaml
create mode 100755 user_service/keploy/atg/tests/test-475.yaml
create mode 100755 user_service/keploy/atg/tests/test-476.yaml
create mode 100755 user_service/keploy/atg/tests/test-477.yaml
create mode 100755 user_service/keploy/atg/tests/test-478.yaml
create mode 100755 user_service/keploy/atg/tests/test-479.yaml
create mode 100755 user_service/keploy/atg/tests/test-48.yaml
create mode 100755 user_service/keploy/atg/tests/test-480.yaml
create mode 100755 user_service/keploy/atg/tests/test-481.yaml
create mode 100755 user_service/keploy/atg/tests/test-482.yaml
create mode 100755 user_service/keploy/atg/tests/test-483.yaml
create mode 100755 user_service/keploy/atg/tests/test-484.yaml
create mode 100755 user_service/keploy/atg/tests/test-485.yaml
create mode 100755 user_service/keploy/atg/tests/test-486.yaml
create mode 100755 user_service/keploy/atg/tests/test-487.yaml
create mode 100755 user_service/keploy/atg/tests/test-488.yaml
create mode 100755 user_service/keploy/atg/tests/test-489.yaml
create mode 100755 user_service/keploy/atg/tests/test-49.yaml
create mode 100755 user_service/keploy/atg/tests/test-490.yaml
create mode 100755 user_service/keploy/atg/tests/test-491.yaml
create mode 100755 user_service/keploy/atg/tests/test-492.yaml
create mode 100755 user_service/keploy/atg/tests/test-493.yaml
create mode 100755 user_service/keploy/atg/tests/test-494.yaml
create mode 100755 user_service/keploy/atg/tests/test-495.yaml
create mode 100755 user_service/keploy/atg/tests/test-496.yaml
create mode 100755 user_service/keploy/atg/tests/test-497.yaml
create mode 100755 user_service/keploy/atg/tests/test-498.yaml
create mode 100755 user_service/keploy/atg/tests/test-499.yaml
create mode 100755 user_service/keploy/atg/tests/test-5.yaml
create mode 100755 user_service/keploy/atg/tests/test-50.yaml
create mode 100755 user_service/keploy/atg/tests/test-500.yaml
create mode 100755 user_service/keploy/atg/tests/test-501.yaml
create mode 100755 user_service/keploy/atg/tests/test-502.yaml
create mode 100755 user_service/keploy/atg/tests/test-503.yaml
create mode 100755 user_service/keploy/atg/tests/test-504.yaml
create mode 100755 user_service/keploy/atg/tests/test-505.yaml
create mode 100755 user_service/keploy/atg/tests/test-506.yaml
create mode 100755 user_service/keploy/atg/tests/test-507.yaml
create mode 100755 user_service/keploy/atg/tests/test-508.yaml
create mode 100755 user_service/keploy/atg/tests/test-509.yaml
create mode 100755 user_service/keploy/atg/tests/test-51.yaml
create mode 100755 user_service/keploy/atg/tests/test-510.yaml
create mode 100755 user_service/keploy/atg/tests/test-511.yaml
create mode 100755 user_service/keploy/atg/tests/test-512.yaml
create mode 100755 user_service/keploy/atg/tests/test-513.yaml
create mode 100755 user_service/keploy/atg/tests/test-514.yaml
create mode 100755 user_service/keploy/atg/tests/test-515.yaml
create mode 100755 user_service/keploy/atg/tests/test-516.yaml
create mode 100755 user_service/keploy/atg/tests/test-517.yaml
create mode 100755 user_service/keploy/atg/tests/test-518.yaml
create mode 100755 user_service/keploy/atg/tests/test-519.yaml
create mode 100755 user_service/keploy/atg/tests/test-52.yaml
create mode 100755 user_service/keploy/atg/tests/test-520.yaml
create mode 100755 user_service/keploy/atg/tests/test-521.yaml
create mode 100755 user_service/keploy/atg/tests/test-522.yaml
create mode 100755 user_service/keploy/atg/tests/test-523.yaml
create mode 100755 user_service/keploy/atg/tests/test-524.yaml
create mode 100755 user_service/keploy/atg/tests/test-525.yaml
create mode 100755 user_service/keploy/atg/tests/test-526.yaml
create mode 100755 user_service/keploy/atg/tests/test-527.yaml
create mode 100755 user_service/keploy/atg/tests/test-528.yaml
create mode 100755 user_service/keploy/atg/tests/test-529.yaml
create mode 100755 user_service/keploy/atg/tests/test-53.yaml
create mode 100755 user_service/keploy/atg/tests/test-530.yaml
create mode 100755 user_service/keploy/atg/tests/test-531.yaml
create mode 100755 user_service/keploy/atg/tests/test-532.yaml
create mode 100755 user_service/keploy/atg/tests/test-533.yaml
create mode 100755 user_service/keploy/atg/tests/test-534.yaml
create mode 100755 user_service/keploy/atg/tests/test-535.yaml
create mode 100755 user_service/keploy/atg/tests/test-536.yaml
create mode 100755 user_service/keploy/atg/tests/test-537.yaml
create mode 100755 user_service/keploy/atg/tests/test-538.yaml
create mode 100755 user_service/keploy/atg/tests/test-539.yaml
create mode 100755 user_service/keploy/atg/tests/test-54.yaml
create mode 100755 user_service/keploy/atg/tests/test-540.yaml
create mode 100755 user_service/keploy/atg/tests/test-541.yaml
create mode 100755 user_service/keploy/atg/tests/test-542.yaml
create mode 100755 user_service/keploy/atg/tests/test-55.yaml
create mode 100755 user_service/keploy/atg/tests/test-56.yaml
create mode 100755 user_service/keploy/atg/tests/test-57.yaml
create mode 100755 user_service/keploy/atg/tests/test-58.yaml
create mode 100755 user_service/keploy/atg/tests/test-59.yaml
create mode 100755 user_service/keploy/atg/tests/test-6.yaml
create mode 100755 user_service/keploy/atg/tests/test-60.yaml
create mode 100755 user_service/keploy/atg/tests/test-61.yaml
create mode 100755 user_service/keploy/atg/tests/test-62.yaml
create mode 100755 user_service/keploy/atg/tests/test-63.yaml
create mode 100755 user_service/keploy/atg/tests/test-64.yaml
create mode 100755 user_service/keploy/atg/tests/test-65.yaml
create mode 100755 user_service/keploy/atg/tests/test-66.yaml
create mode 100755 user_service/keploy/atg/tests/test-67.yaml
create mode 100755 user_service/keploy/atg/tests/test-68.yaml
create mode 100755 user_service/keploy/atg/tests/test-69.yaml
create mode 100755 user_service/keploy/atg/tests/test-7.yaml
create mode 100755 user_service/keploy/atg/tests/test-70.yaml
create mode 100755 user_service/keploy/atg/tests/test-71.yaml
create mode 100755 user_service/keploy/atg/tests/test-72.yaml
create mode 100755 user_service/keploy/atg/tests/test-73.yaml
create mode 100755 user_service/keploy/atg/tests/test-74.yaml
create mode 100755 user_service/keploy/atg/tests/test-75.yaml
create mode 100755 user_service/keploy/atg/tests/test-76.yaml
create mode 100755 user_service/keploy/atg/tests/test-77.yaml
create mode 100755 user_service/keploy/atg/tests/test-78.yaml
create mode 100755 user_service/keploy/atg/tests/test-79.yaml
create mode 100755 user_service/keploy/atg/tests/test-8.yaml
create mode 100755 user_service/keploy/atg/tests/test-80.yaml
create mode 100755 user_service/keploy/atg/tests/test-81.yaml
create mode 100755 user_service/keploy/atg/tests/test-82.yaml
create mode 100755 user_service/keploy/atg/tests/test-83.yaml
create mode 100755 user_service/keploy/atg/tests/test-84.yaml
create mode 100755 user_service/keploy/atg/tests/test-85.yaml
create mode 100755 user_service/keploy/atg/tests/test-86.yaml
create mode 100755 user_service/keploy/atg/tests/test-87.yaml
create mode 100755 user_service/keploy/atg/tests/test-88.yaml
create mode 100755 user_service/keploy/atg/tests/test-89.yaml
create mode 100755 user_service/keploy/atg/tests/test-9.yaml
create mode 100755 user_service/keploy/atg/tests/test-90.yaml
create mode 100755 user_service/keploy/atg/tests/test-91.yaml
create mode 100755 user_service/keploy/atg/tests/test-92.yaml
create mode 100755 user_service/keploy/atg/tests/test-93.yaml
create mode 100755 user_service/keploy/atg/tests/test-94.yaml
create mode 100755 user_service/keploy/atg/tests/test-95.yaml
create mode 100755 user_service/keploy/atg/tests/test-96.yaml
create mode 100755 user_service/keploy/atg/tests/test-97.yaml
create mode 100755 user_service/keploy/atg/tests/test-98.yaml
create mode 100755 user_service/keploy/atg/tests/test-99.yaml
create mode 100644 user_service/keploy/load/reports/20251110_113538_suite-0.json
create mode 100644 user_service/keploy/load/reports/20251110_113651_suite-0.json
create mode 100644 user_service/keploy/load/reports/20251110_125123_suite-0.json
create mode 100755 user_service/keploy/testsuite/keploy.yml
create mode 100644 user_service/keploy/testsuite/suite-0.yaml
create mode 100644 user_service/keploy/testsuite/ts_reports/20251110112827_report_suite-0.yaml
diff --git a/.github/workflows/keploy-e2e.yml b/.github/workflows/keploy-e2e.yml
new file mode 100644
index 0000000..20697dc
--- /dev/null
+++ b/.github/workflows/keploy-e2e.yml
@@ -0,0 +1,89 @@
+name: Keploy E2E on PR
+
+on:
+ pull_request:
+ branches: [ main ]
+
+permissions:
+ contents: read
+
+concurrency:
+ group: keploy-e2e-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ keploy-e2e:
+ name: Install Keploy, build, and run tests
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Show Docker and Compose versions
+ run: |
+ docker --version
+ docker compose version
+
+ - name: Install Keploy CLI
+ shell: bash
+ run: |
+ set -euo pipefail
+ curl --silent -O -L https://keploy.io/install.sh
+ # Source in current shell so the script can modify PATH for this step if needed
+ source install.sh
+ # Verify installation
+ command -v keploy
+ keploy version || true
+
+ - name: Build Docker images (compose)
+ run: |
+ docker compose build --pull
+
+ - name: Run Keploy tests against docker compose
+ env:
+ # Increase Docker's default logs size to keep useful output (optional)
+ COMPOSE_HTTP_TIMEOUT: "200"
+ run: |
+ # Run keploy with the provided command and options
+ keploy test -c "docker compose up" \
+ --container-name="user_service" \
+ --path="./user_service" \
+ --config-path="./user_service" \
+ --delay 15 \
+ -t test-set-0
+
+ - name: Upload Keploy artifacts (user_service)
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: keploy-user_service-artifacts
+ path: |
+ user_service/keploy/**
+ if-no-files-found: warn
+ retention-days: 7
+
+ - name: Upload service coverage (optional)
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: service-coverage
+ path: |
+ coverage/**
+ apigateway/coverage/**
+ user_service/coverage/**
+ product_service/coverage/**
+ order_service/coverage/**
+ if-no-files-found: ignore
+ retention-days: 7
+
+ - name: Teardown docker compose
+ if: always()
+ run: |
+ docker compose ps
+ docker compose logs --no-color | tail -n 200 || true
+ docker compose down -v --remove-orphans
diff --git a/coverage/.coverage.order_service.combined b/coverage/.coverage.order_service.combined
index ec3d1fa24f8951a956cb9b1d763cb3e9015c1946..f8d77e960a98ca26e308930b90cf5cf28490f3d6 100644
GIT binary patch
delta 1518
zcmXxjPiP!<6u|L$t7d%3@4fCWDB3?sn)F|0(`>qJvrYOpX*T_r=8r;@9%cr?9t1@^
zh#+Pbg?KC0T$(>ki)||OA}ROn6eD1xxFh^X}-h*CrpK{gxT&TroBvu_>)!+u}&
z=EK#S553a`^kMhUH(YWYXV%mf-9KlZUg9hK7Qea?#`kQ{E
zAL%>#lJ3z*^ggZ9Ra&6e>1BGJF3~iN(=ZKCFFip-8S}6C!`wHuk8|066XEP5h?xwm
z!yTNlybbF(ZFviBUa(w&RXm@tc^OvloSiPiEj(+v1j~5F
z@)|7RM2k@dBB)};j;nAD$1Ue&dRnIuj#)-9kE6N};VH{drY9|Z2=Rnv06reqg#eG~
zG{mF28(=hIotNcd%Pi#ah-D74Sk{Fc4(T+HhjllHhY~tXX9EvfZa7UWSvF*PAYG7`
z4eCMziwPsAA4
zH9_pTPbTOqEqa?Z)1DN(;F#}ubFu&1!I!om|HHrYeUABS{(?W{4|tv5;J0~&uktH=
zS*jl6OFYFM{l}f0rw8;4w^5DmNY`J{XS7UL$fp+{e{bh)$~|_%Zc7M5Ag+{*+ppKdF}JPpCr(j(4<0
z_Ui)(kLd#+f}?5)e?%?i5342pBkDZl%IX~Chtyff9ZucK|BzbxKd5d*&PGXpG$DIH
zz2P(l)p7$xwH(2KTCQNfTF#(fU3cPwI(F)wy5_`v?ecd+2ILg>VT)1g81^Ov2>d;%
z!eF<~!dkJ5=8GR9;~lw&hyq2(h;{9m;9s
Ke|qp
literal 69632
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
Ui{)aPh?pAuFc33E#MIySKktxyp8x;=
diff --git a/coverage/.coverage.user_service.combined b/coverage/.coverage.user_service.combined
index 95fbddccceb85796b88a52ed661b3e877d63ed79..956af0ee1ec60eccdb765152b7b868a47a028c08 100644
GIT binary patch
delta 3884
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
J@w3CD{s&>mW%K|5
delta 1899
zcmYM!Uuc_E7{~E*QrqLRo^#TFTavDAlKn}SbV-_S?Y4I9x~~7zv`d?I)r)~i6qMnG
z$OJEZ$Dm%MRg|iTyJ)KDjo_xckcsGpqBLa)yRaZAR1n$R#QUTXmDJ)m7>r^}G5-
z{h+>6U#olSBlVtoTfL%gsu$EX^@N&JN!719RYdt!g971R(>u&mLJGl
z@-?||x3=bfwFUE)04^8L@L9NGIKwkAXE@DgVAe3jCm~}v!Ba3}nB)^MZ8*-8khb~o
zQ$7xt%{s=XVajlnkHMtj2p@%1nU;&U@e!CXa~qF9(lEl?VBFA$>zJX3`(V`2%RMkc
z2VNMa)dxd#>xDs^Y@hAHCBr?o4+Dm~xLOyT3w*bJy4Zyabg>V8hC6H*dJVVO4xBgK
zW?Rrh2iwq1>kgcw+if^&xQ@4H4AnqRE~TNpaXl
zdqOnYh_?%$jaXcGZA4?jV`C#K4%rB%dPP0?T@7!vgc0a0fo5EQjGHUpx@hCeLa
zm9T8xUN|r>V@%$3H@hEOZn@*Pa_%Yjkh{m7aJRba-Jt)~f9hZLrryx^^;i0yeqG&IL;a<-4(UeiQakE1^^N*TJ-{~KRnMuH)LW{co>E=vaW#ou
zwx|JBCx4c|sVe!m{9cyi7xEMA@*VlAye{Q4@~WJc!?Ih(Fw*PQR&|x#XgSh(GFEzp
zjG4}nvC~;HhMFN`sWW6ub=q;c)imuG>t!<5I_22n*y|)cz+O}TJ2FAWVv}S{cASjO
zj*&6iQL>MFN62388zy_WcgS%$+`;N9{F2~*D2C}?q6gUS0C}H#E|T}yZa;aK?Oz~c
zzI|lux0j3opC|9I?H=+r+vz56S#0|p{eTspC1b{C$e3{#d7YIy$r$o!@)|3hB4f%O
zWNbM>#+cj5tE?C=+bVzhB3q5o0}MJ!#-dM>G3hokCLJL!vBfZXku9AdW7Vw(@E33w
z$E-t8X1VL*j&j*!Wm!1pb4O{+AECos&`~BEaFovY9i=u}93@lDjuOcxN9~Ejj^gc&
yj$(12qiD?QXd~)@LGyQo(}$cfw0Y1`Fw{T_)H@0U>l|$cY90B*cdvc:
+ needs: [build-and-upload, upload-latest]
+ uses: ./.github/workflows/my_.yml
+ ```
+ Replace `` with the name of your workflow.
+ **Note:** *If your sample needs the pre‑built Docker image add `build-docker-image` to the `needs` list.*
+
+---
+
+## 5. Troubleshooting
+
+When a workflow fails, and log isn't visible, then it might be due to the error happening not in record/replay step, but somewhere else, could be that some referenced binary was not found (exit code 127), could be some permission issues, could be file not found. Look for the exit code to determine what failed (https://tldp.org/LDP/abs/html/exitcodes.html), and check the previous successful step to pipe out the line in bash script related to the workflow run.
+
+---
+
diff --git a/keploy/.github/FUNDING.yml b/keploy/.github/FUNDING.yml
new file mode 100755
index 0000000..24b566c
--- /dev/null
+++ b/keploy/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: [keploy]
diff --git a/keploy/.github/ISSUE_TEMPLATE/--bug-report.yaml b/keploy/.github/ISSUE_TEMPLATE/--bug-report.yaml
new file mode 100755
index 0000000..fc9d7bb
--- /dev/null
+++ b/keploy/.github/ISSUE_TEMPLATE/--bug-report.yaml
@@ -0,0 +1,167 @@
+name: Bug report
+description: Create a bug report to help us improve Keploy
+title: "[bug]: "
+labels: [bug]
+body:
+- type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out our bug report form 🙏
+
+- type: checkboxes
+ attributes:
+ label: "👀 Is there an existing issue for this?"
+ description: Please search to see if an issue already exists for the bug you encountered
+ options:
+ - label: I have searched and didn't find similar issue
+ required: true
+
+- type: textarea
+ attributes:
+ label: 👍 Current behavior
+ description: A concise description of what you're experiencing and what you expect
+ placeholder: |
+ When I do , happens and I see the error message attached below:
+ ```...```
+ What I expect is
+ validations:
+ required: true
+
+- type: textarea
+ attributes:
+ label: 👟 Steps to Replicate
+ description: How do you trigger this bug? Please walk us through it step by step.
+ placeholder: |
+ 1. Go to '...'
+ 2. Click on '....'
+ 3. Scroll down to '....'
+ 4. See error
+ validations:
+ required: true
+
+- type: textarea
+ id: logs
+ attributes:
+ label: 📜 Logs (if any)
+ description: Paste any relevant logs here. You can also upload `.log` files via drag & drop.
+ placeholder: |
+ ```
+ paste stack trace, application log, or keploy logs here
+ ```
+ validations:
+ required: false
+
+- type: dropdown
+ id: operating-system
+ attributes:
+ label: "💻 Operating system"
+ description: What OS is your server/device running on?
+ options:
+ - Linux
+ - MacOS
+ - Windows
+ - Something else
+ validations:
+ required: true
+
+- type: textarea
+ id: uname
+ attributes:
+ label: 🧾 System Info (`uname -a`)
+ description: Run "uname -a" on your terminal and paste the output (macOS/Linux only).
+ placeholder: |
+ Darwin MacBook-Pro.local 22.6.0 Darwin Kernel Version ...
+ validations:
+ required: false
+
+- type: textarea
+ id: os-release
+ attributes:
+ label: 📦 OS Release Info (`cat /etc/os-release`)
+ description: Run "cat /etc/os-release" (Linux only) and paste the full output here.
+ placeholder: |
+ NAME="Ubuntu"
+ VERSION="22.04.4 LTS (Jammy Jellyfish)"
+ ...
+ validations:
+ required: false
+
+- type: textarea
+ id: docker
+ attributes:
+ label: 🐳 Docker Info (if applicable)
+ description: |
+ Are you running inside Docker? If yes, please mention:
+ - Docker version
+ - Docker Desktop version (if used)
+ - Build image
+ - Runtime image
+ placeholder: |
+ Yes, running inside Docker.
+ - Docker version: 24.0.2
+ - Docker Desktop version: 4.24.2
+ - Build image: golang:1.21-alpine
+ - Runtime image: alpine:3.18
+ validations:
+ required: false
+
+- type: textarea
+ id: environment
+ attributes:
+ label: "🧱 Your Environment"
+ description: Include any infrastructure info (e.g., local machine, VM, cloud, Kubernetes etc.)
+ placeholder: |
+ - Running on Kubernetes (GKE)
+ - 3-node cluster
+ validations:
+ required: false
+
+- type: textarea
+ id: version
+ attributes:
+ label: 🎲 Version
+ description: |
+ You can find your current Keploy version by running "keploy -v" or "keploy --version" in your terminal.
+ placeholder: |
+ I am using latest version v2.5.2
+ validations:
+ required: true
+
+- type: dropdown
+ id: repository
+ attributes:
+ label: 📦 Repository
+ options:
+ - keploy
+ - go-sdk
+ - java-sdk
+ - python-sdk
+ - typescript-sdk
+ - docs
+ - website
+ - writers-program
+ - blog-website
+ - ui
+ - vscode-extension
+ - jetbrains-plugin
+ - samples-go
+ - samples-java
+ - samples-rust
+ - samples-python
+ - samples-csharp
+ - samples-typescript
+ validations:
+ required: true
+
+- type: textarea
+ id: use-case
+ attributes:
+ label: 🤔 What use case were you trying? (optional)
+ description: Tell us briefly what you were trying to achieve when this bug occurred.
+ placeholder: |
+ I was trying to capture test cases during a login API call...
+
+- type: markdown
+ attributes:
+ value: |
+ I have read the [Code of Conduct](https://github.com/keploy/keploy/blob/main/CODE_OF_CONDUCT.md) and followed [Contribution Guide](https://keploy.io/docs/keploy-explained/contribution-guide/)
\ No newline at end of file
diff --git a/keploy/.github/ISSUE_TEMPLATE/--documentation-update.yaml b/keploy/.github/ISSUE_TEMPLATE/--documentation-update.yaml
new file mode 100755
index 0000000..17b9460
--- /dev/null
+++ b/keploy/.github/ISSUE_TEMPLATE/--documentation-update.yaml
@@ -0,0 +1,48 @@
+name: Documentation Update📄
+description: Suggest an improvement/addition in the Keploy Server Docs.
+title: "[docs]: "
+labels: [Documentation]
+
+body:
+ - type: markdown
+ attributes:
+ value: Thank you for taking the time to our documentation better 🙏
+ - type: checkboxes
+ attributes:
+ label: "👀 Is there an existing issue for this?"
+ description: Please search to see if an issue already exists for the bug you encountered
+ options:
+ - label: I have searched and didn't find similar issue
+ required: true
+ - type: textarea
+ attributes:
+ label: "💭 Description"
+ description: "A clear and concise description of what the issue is."
+ placeholder: "Documentation is ..."
+ - type: dropdown
+ id: repository
+ attributes:
+ label: 💻 Repository
+ options:
+ - keploy
+ - go-sdk
+ - java-sdk
+ - python-sdk
+ - typescript-sdk
+ - docs
+ - website
+ - writers-program
+ - blog-website
+ - ui
+ - samples-go
+ - samples-java
+ - samples-rust
+ - samples-python
+ - samples-csharp
+ - samples-typescript
+ validations:
+ required: true
+ - type: markdown
+ attributes:
+ value: |
+ I have read the [Code of Conduct](https://github.com/keploy/keploy/blob/main/CODE_OF_CONDUCT.md)
\ No newline at end of file
diff --git a/keploy/.github/ISSUE_TEMPLATE/--feature-request.yaml b/keploy/.github/ISSUE_TEMPLATE/--feature-request.yaml
new file mode 100755
index 0000000..272386c
--- /dev/null
+++ b/keploy/.github/ISSUE_TEMPLATE/--feature-request.yaml
@@ -0,0 +1,60 @@
+name: Feature request
+description: Suggest a feature to improve Keploy
+title: "[feature]: "
+labels: [Enhancement]
+body:
+- type: markdown
+ attributes:
+ value: |
+ Thank you for taking the time to request a feature for Keploy🙏
+- type: checkboxes
+ attributes:
+ label: 👀 Is there an existing feature request for this?
+ description: Please search to see if an issue related to this feature request/feature request already exists
+ options:
+ - label: I have searched the existing issues
+ required: true
+- type: textarea
+ attributes:
+ label: 🔖 Enhancement description
+ description: A clear and concise description of what the enhancement is.
+ placeholder: |
+ "I would like to see ..."
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: 🎤 Why should this be worked on?
+ description: A concise description of the problems or use cases for this feature request
+ placeholder: "In my use-case, ..."
+ validations:
+ required: true
+- type: dropdown
+ id: repository
+ attributes:
+ label: 💻 Repository
+ options:
+ - keploy
+ - go-sdk
+ - java-sdk
+ - python-sdk
+ - typescript-sdk
+ - docs
+ - website
+ - writers-program
+ - blog-website
+ - ui
+ - vscode-extension
+ - jetbrains-plugin
+ - samples-go
+ - samples-java
+ - samples-rust
+ - samples-python
+ - samples-csharp
+ - samples-typescript
+ validations:
+ required: true
+- type: markdown
+ attributes:
+ value: |
+ I have read the [Code of Conduct](https://github.com/keploy/keploy/blob/main/CODE_OF_CONDUCT.md)
diff --git a/keploy/.github/ISSUE_TEMPLATE/config.yml b/keploy/.github/ISSUE_TEMPLATE/config.yml
new file mode 100755
index 0000000..6d0c5b2
--- /dev/null
+++ b/keploy/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,7 @@
+contact_links:
+ - name: Help and support
+ url: https://github.com/keploy/keploy#community-support
+ about: Reach out to us on our Slack channel or Discourse discussions or GitHub discussions.
+ - name: Dedicated support
+ url: mailto:hello@keploy.io
+ about: Write to us if you'd like dedicated support using Keploy
\ No newline at end of file
diff --git a/keploy/.github/License-Apache_2.0-blue.svg b/keploy/.github/License-Apache_2.0-blue.svg
new file mode 100755
index 0000000..ca466ca
--- /dev/null
+++ b/keploy/.github/License-Apache_2.0-blue.svg
@@ -0,0 +1 @@
+License: Apache 2.0 License License Apache 2.0 Apache 2.0
\ No newline at end of file
diff --git a/keploy/.github/PULL_REQUEST_TEMPLATE.md b/keploy/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100755
index 0000000..7fdf2b3
--- /dev/null
+++ b/keploy/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,70 @@
+## Describe the changes that are made
+-
+
+## Links & References
+
+**Closes:** #[issue number that will be closed through this PR]
+- NA (if very small change like typo, linting, etc.)
+
+### 🔗 Related PRs
+- NA
+### 🐞 Related Issues
+- NA
+### 📄 Related Documents
+- NA
+
+## What type of PR is this? (check all applicable)
+- [ ] 📦 Chore
+- [ ] 🍕 Feature
+- [ ] 🐞 Bug Fix
+- [ ] 📝 Documentation Update
+- [ ] 🎨 Style
+- [ ] 🧑💻 Code Refactor
+- [ ] 🔥 Performance Improvements
+- [ ] ✅ Test
+- [ ] 🔁 CI
+- [ ] ⏩ Revert
+
+## Added e2e test pipeline?
+- [ ] 👍 yes
+- [ ] 🙅 no, because they aren't needed
+- [ ] 🙋 no, because I need help
+
+## Added comments for hard-to-understand areas?
+- [ ] 👍 yes
+- [ ] 🙅 no, because the code is self-explanatory
+
+## Added to documentation?
+- [ ] 📜 README.md
+- [ ] 📓 Wiki
+- [ ] 🙅 no documentation needed
+
+## Are there any sample code or steps to test the changes?
+- [ ] 👍 yes, mentioned below
+- [ ] 🙅 no, because it is not needed
+
+## Self Review done?
+- [ ] ✅ yes
+- [ ] ❌ no, because I need help
+
+## Any relevant screenshots, recordings or logs?
+- NA
+
+## 🧠 Semantics for PR Title & Branch Name
+
+Please ensure your PR title and branch name follow the Keploy semantics:
+
+📌 [PR Semantics Guide](https://github.com/keploy/keploy/wiki/PR-Semantics)
+📌 [Branch Semantics Guide](https://github.com/keploy/keploy/wiki/Branch-Semantics)
+
+**Examples:**
+
+- **PR Title**: `fix: patch MongoDB document update bug`
+- **Branch Name**: `feat/#1-login-flow` (You may skip mentioning the issue number in the branch name if the change is small and the PR description clearly explains it.)
+
+---
+
+## Additional checklist:
+- [ ] Have you read the [Contributing Guidelines on issues](https://keploy.io/docs/keploy-explained/contribution-guide/)?
+- [ ] Have you followed the [PR Semantics guide](https://github.com/keploy/keploy/wiki/PR-Semantics) for naming this PR?
+- [ ] Have you followed the [Branch Semantics guide](https://github.com/keploy/keploy/wiki/Branch-Semantics) for naming your branch?
\ No newline at end of file
diff --git a/keploy/.github/actions/download-binary/action.yml b/keploy/.github/actions/download-binary/action.yml
new file mode 100644
index 0000000..9ec0942
--- /dev/null
+++ b/keploy/.github/actions/download-binary/action.yml
@@ -0,0 +1,28 @@
+name: get-binary
+description: "Download build or latest Keploy and expose its absolute path"
+
+inputs:
+ src:
+ required: true
+ description: "build | latest"
+
+outputs:
+ path:
+ description: "binary path"
+ value: ${{ steps.set-path.outputs.path }}
+
+runs:
+ using: composite
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ name: ${{ inputs.src }}
+ path: ${{ inputs.src }}
+
+ - name: Set binary path
+ id: set-path
+ shell: bash
+ run: |
+ ls -l
+ chmod +x ${{ inputs.src }}/keploy
+ echo "path=$PWD/${{ inputs.src }}/keploy" >> $GITHUB_OUTPUT
diff --git a/keploy/.github/actions/download-image/action.yml b/keploy/.github/actions/download-image/action.yml
new file mode 100644
index 0000000..bccc056
--- /dev/null
+++ b/keploy/.github/actions/download-image/action.yml
@@ -0,0 +1,12 @@
+name: pull-image
+description: "Pull Image"
+runs:
+ using: composite
+ steps:
+ - name: Pull Image
+ shell: bash
+ run: |
+ docker pull ttl.sh/keploy/keploy:1h
+ # since keploy expects the image to be tagged as ghcr.io/keploy/keploy:v2-dev so we need to rename it
+ # https://stackoverflow.com/questions/70531871/how-pull-a-docker-image-and-give-it-a-diferente-name
+ docker tag ttl.sh/keploy/keploy:1h ghcr.io/keploy/keploy:v2-dev
\ No newline at end of file
diff --git a/keploy/.github/actions/tester/action.yml b/keploy/.github/actions/tester/action.yml
new file mode 100644
index 0000000..2ae1353
--- /dev/null
+++ b/keploy/.github/actions/tester/action.yml
@@ -0,0 +1,112 @@
+name: 'Keploy test Keploy'
+description: "An action to test keploy with keploy"
+author: gouravkrosx
+branding:
+ icon: 'refresh-cw'
+ color: 'orange'
+
+inputs:
+ working-directory:
+ description: Relative path where your application code is located
+ required: true
+ command:
+ description: Command to run the application
+ required: true
+ keploy-path:
+ description: Path to keploy pre-recorded test cases
+ required: true
+ default: .
+ config-path:
+ description: Path to the config file
+ required: true
+ default: .
+ delay:
+ description: Time to start application
+ required: true
+ default: 10
+ keploy-record-bin:
+ description: Path to keploy record binary (Build or Released)
+ required: true
+ keploy-test-bin:
+ description: Path to keploy test binary (Build or Released)
+ required: true
+ mode:
+ description: Mode to identify the workflow type
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - name: Print the input data.
+ run: |
+ echo "Working Directory: ${{ inputs.working-directory }}"
+ echo "Command: ${{ inputs.command }}"
+ echo "Keploy Path: ${{ inputs.keploy-path }}"
+ echo "Config Path: ${{ inputs.config-path }}"
+ echo "Delay: ${{ inputs.delay }}"
+ echo "Keploy Record Binary: ${{ inputs.keploy-record-bin }}"
+ echo "Keploy Test Binary: ${{ inputs.keploy-test-bin }}"
+ echo "Mode: ${{ inputs.mode }}"
+ shell: bash
+
+ - name: Grant permissions
+ run: chmod +x ${GITHUB_ACTION_PATH}/install.sh
+ shell: sh
+ - id: keploy-test-keploy
+ name: Run Script
+ run: |
+ ${GITHUB_ACTION_PATH}/install.sh
+
+ shell: bash
+ env:
+ WORKDIR: ${{ inputs.working-directory }}
+ COMMAND : ${{ inputs.command }}
+ KEPLOY_PATH: ${{inputs.keploy-path}}
+ CONFIG_PATH: ${{inputs.config-path}}
+ DELAY: ${{ inputs.delay }}
+ KEPLOY_RECORD_BIN: ${{ inputs.keploy-record-bin }}
+ KEPLOY_TEST_BIN: ${{ inputs.keploy-test-bin }}
+ MODE: ${{ inputs.mode }}
+
+ - name: Get coverage data and zip it
+ if: success()
+ id: coverage-zip # Unique ID for the step
+ run: |
+ echo "Output from script: ${{ steps.keploy-test-keploy.outputs.script_output }}"
+ workdir="${{ inputs.working-directory }}/"
+ workdir="${workdir//\//-}"
+ workdir="${workdir%-}"
+
+ coverage="coverage-reports-${workdir}-${{ inputs.mode }}"
+ mv ${{ inputs.working-directory }}/coverage-reports ./$coverage
+ echo "coverage=$coverage"
+ echo "::set-output name=coverage_name::$coverage"
+ zip -r ${coverage}.zip $coverage
+ shell: bash
+
+ - name: Upload coverage zip as artifact
+ if: steps.coverage-zip.outputs.coverage_name != ''
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ steps.coverage-zip.outputs.coverage_name }}
+ path: ${{ steps.coverage-zip.outputs.coverage_name }}.zip
+
+ # - name: Comment on PR
+ # if: success()
+ # uses: actions/github-script@v6
+ # env:
+ # KEPLOY_REPORT: ${{ steps.keploy-test-report.outputs.KEPLOY_REPORT }}
+ # with:
+ # github-token: ${{ github.token }}
+ # script: |
+ # if (!process.env.KEPLOY_REPORT) {
+ # console.error('Error: KEPLOY_REPORT not found.');
+ # process.exit(1);
+ # }
+ # github.rest.issues.createComment({
+ # issue_number: context.issue.number,
+ # owner: context.repo.owner,
+ # repo: context.repo.repo,
+ # body: process.env.KEPLOY_REPORT
+ # })
+ # shell: bash
\ No newline at end of file
diff --git a/keploy/.github/actions/tester/install.sh b/keploy/.github/actions/tester/install.sh
new file mode 100644
index 0000000..5bff962
--- /dev/null
+++ b/keploy/.github/actions/tester/install.sh
@@ -0,0 +1,172 @@
+#Print the current github workspace
+echo Github_workspace "${GITHUB_WORKSPACE}"
+
+# Add fake installation-id for the workflow.
+source ${GITHUB_WORKSPACE}/.github/workflows/test_workflow_scripts/test-iid.sh
+
+delete_if_exists() {
+ local path=$1
+ if [ -e "$path" ]; then
+ sudo rm -rf "$path"
+ fi
+}
+
+
+check_test_status() {
+ local path=$1
+ local fixed_index=$2 # Boolean to determine if index should be fixed to 0
+ local overallStatus=1 # true
+ local idx=0 # Initialize index
+ for dir in $test_sets; do
+ if [ "$fixed_index" -eq 1 ]; then
+ local report_file="$path/keploy/reports/test-run-0/$dir-report.yaml"
+ else
+ local report_file="$path/keploy/reports/test-run-$idx/$dir-report.yaml"
+ idx=$((idx + 1))
+ fi
+
+ local test_status=$(grep 'status:' "$report_file" | head -n 1 | awk '{print $2}')
+
+ if [ "$test_status" != "PASSED" ]; then
+ overallStatus=0 # false
+ fi
+ done
+ echo $overallStatus
+}
+
+
+# Go to the working directory
+echo "Working directory: ${WORKDIR}"
+cd "${WORKDIR}"
+
+# Make a directory to dump coverage
+mkdir coverage-reports
+
+# Set GOCOVERDIR to the coverage directory
+export GOCOVERDIR="./coverage-reports"
+
+#### Recording Phase of test-bench ####
+pre_rec="${KEPLOY_PATH}"
+
+# Delete the reports directory if it exists
+delete_if_exists "$pre_rec/keploy/reports"
+
+# Get all directories except the 'reports' directory and maintain order
+test_sets=$(find "$pre_rec/keploy/" -mindepth 1 -maxdepth 1 -type d ! -name "reports" | sort | xargs -n 1 basename)
+
+
+# Check the exit status of the find command
+if [ $? -ne 0 ]; then
+ echo "Error: No such file or directory."
+ echo "::set-output name=script_output::failure"
+ exit 1
+fi
+
+# Count the number of directories found
+num_test_sets=$(echo "$test_sets" | wc -l)
+
+# Check if the number of directories is zero
+if [ "$num_test_sets" -eq 0 ]; then
+ echo "No test sets found."
+ echo "::set-output name=script_output::failure"
+ exit 1
+fi
+
+# Loop over each directory stored in 'test_sets'
+for dir in $test_sets; do
+ echo "Recording and replaying for (test-set): $dir"
+# MODE (0, recordHosted,testBuild) , (1, recordBuild, testHosted)
+ if [ "$MODE" -eq 0 ]; then
+ echo "Latest version of keploy is being used for recording, Build version of keploy is being used for testing"
+ else
+ echo "Build version of keploy is being used for recording, Latest version of keploy is being used for testing"
+ fi
+
+ sudo -E env PATH=$PATH ${KEPLOY_RECORD_BIN} record -c "sudo -E env PATH=$PATH ${KEPLOY_TEST_BIN} test -c '${COMMAND}' --proxyPort 56789 --dnsPort 46789 --delay=${DELAY} --testsets $dir --configPath '${CONFIG_PATH}' --path '$pre_rec' --enableTesting " --path "./test-bench/" --proxyPort=36789 --dnsPort 26789 --configPath "${CONFIG_PATH}" --enableTesting
+ # Wait for 1 second before new test-set
+ sleep 1
+done
+
+sleep 5
+
+# Check whether the original tests passed or failed
+overallStatus=$(check_test_status "$pre_rec" 0)
+echo "Overall TestRun status for pre-recorded testscase ran via test-bench: $overallStatus"
+if [ "$overallStatus" -eq 0 ]; then
+ echo "Pre-recorded testcases failed. Exiting..."
+ delete_if_exists "$pre_rec/keploy/reports"
+ echo "::set-output name=script_output::failure"
+ exit 1
+fi
+
+echo "Successfully recorded tests and mocks via test-bench 🎉"
+
+#### Testing Phase of test-bench ####
+test_bench_rec="./test-bench"
+
+## Test assertion
+pilot -test-assert -preRecPath $pre_rec -testBenchPath $test_bench_rec
+exit_status=$?
+echo "Test assertion exit status: $exit_status"
+if [ $exit_status -eq 1 ]; then
+ echo "Test assertion failed with exit status $exit_status."
+ echo "::set-output name=script_output::failure"
+ exit 1
+fi
+
+echo "Tests are asserted successfully 🎉"
+
+
+## Mock assertion preparation
+
+pilot -mock-assert -preRecPath $pre_rec -testBenchPath $test_bench_rec
+exit_status=$?
+echo "Mock assertion preparation exit status: $exit_status"
+if [ $exit_status -eq 1 ]; then
+ echo "Mock assertion preparation failed with exit status $exit_status."
+ echo "::set-output name=script_output::failure"
+ exit 1
+fi
+
+echo "Mock assertion prepared successfully 🎉"
+
+## Now run the tests both for pre-recorded test cases and test-bench recorded test cases to compare the mocks (mock assertion)
+delete_if_exists "$pre_rec/keploy/reports"
+
+## Run tests for pre-recorded test cases
+sudo -E env PATH=$PATH keployR test -c "${COMMAND}" --delay ${DELAY} --path "$pre_rec"
+
+sleep 5
+
+overallStatus=$(check_test_status "$pre_rec" 1)
+echo "Overall TestRun status for pre-recorded testscase (after mock assertion): $overallStatus"
+if [ "$overallStatus" -eq 0 ]; then
+ echo "Newly recorded mocks are not consistent with the pre-recorded mocks."
+ echo "::set-output name=script_output::failure"
+ exit 1
+fi
+echo "New mocks are consistent with the pre-recorded mocks 🎉"
+
+
+## Run tests for test-bench-recorded test cases
+sudo -E env PATH=$PATH keployR test -c "${COMMAND}" --delay ${DELAY} --path "$test_bench_rec"
+
+sleep 5
+
+overallStatus=$(check_test_status "$test_bench_rec" 1)
+echo "Overall TestRun status for test-bench-recorded testscase (after mock assertion): $overallStatus"
+if [ "$overallStatus" -eq 0 ]; then
+ echo "Old recorded mocks are not consistent with the test-bench-recorded mocks."
+ delete_if_exists "$test_bench_rec"
+ echo "::set-output name=script_output::failure"
+ exit 1
+fi
+echo "Old mocks are consistent with the test-bench-recorded mocks 🎉"
+
+# Delete the tests and mocks generated via test-bench.
+delete_if_exists "$test_bench_rec"
+
+echo "Tests and mocks are consistent for this application 🎉"
+echo "::set-output name=script_output::success"
+
+exit 0
\ No newline at end of file
diff --git a/keploy/.github/docs.svg b/keploy/.github/docs.svg
new file mode 100755
index 0000000..79e1deb
--- /dev/null
+++ b/keploy/.github/docs.svg
@@ -0,0 +1,21 @@
+
+ 📖: docs
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 📖
+ 📖
+ docs
+ docs
+
+
\ No newline at end of file
diff --git a/keploy/.github/slack.svg b/keploy/.github/slack.svg
new file mode 100755
index 0000000..6cb641a
--- /dev/null
+++ b/keploy/.github/slack.svg
@@ -0,0 +1,20 @@
+
+ slack
+
+
+
+
+
+
+
+
+
+
+
+
+
+ slack
+ slack
+
+
+
\ No newline at end of file
diff --git a/keploy/.github/workflows/bug.yml b/keploy/.github/workflows/bug.yml
new file mode 100755
index 0000000..d187639
--- /dev/null
+++ b/keploy/.github/workflows/bug.yml
@@ -0,0 +1,38 @@
+name: Issue labeler
+on:
+ issues:
+ types: [ opened ]
+
+permissions:
+ contents: read
+
+jobs:
+ label-component:
+ runs-on: ubuntu-latest
+
+ permissions:
+ # required for all workflows
+ issues: write
+
+ # only required for workflows in private repositories
+ actions: read
+ contents: read
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Parse issue form
+ uses: stefanbuck/github-issue-parser@v3
+ id: issue-parser
+ with:
+ template-path: .github/ISSUE_TEMPLATE/--bug-report.yaml
+
+ - name: Set labels based on repository field
+ uses: redhat-plumbers-in-action/advanced-issue-labeler@v3
+ with:
+ issue-form: ${{ steps.issue-parser.outputs.jsonString }}
+ section: repository
+ block-list: |
+ None
+ Other
+ token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/keploy/.github/workflows/cla.yml b/keploy/.github/workflows/cla.yml
new file mode 100755
index 0000000..f734d9b
--- /dev/null
+++ b/keploy/.github/workflows/cla.yml
@@ -0,0 +1,42 @@
+name: "CLA Assistant"
+
+on:
+ issue_comment:
+ types:
+ - "created"
+ pull_request_target:
+ types:
+ - "opened"
+ - "closed"
+ - "synchronize"
+
+jobs:
+ cla-assistant:
+ runs-on: "ubuntu-latest"
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: "CLA Assistant"
+ if: "(github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'"
+ uses: "cla-assistant/github-action@v2.1.3-beta"
+ env:
+ GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+ PERSONAL_ACCESS_TOKEN: "${{ secrets.PRO_ACCESS_TOKEN }}"
+ with:
+ remote-organization-name: "keploy"
+ remote-repository-name: "keploy"
+ path-to-signatures: "etc/cla-signatures/signatures.json"
+ path-to-document: "https://github.com/keploy/keploy/tree/main/.github/CLA.md"
+ branch: "cla-signatures"
+ allowlist: "keploy-bot,renovate"
+
+ - name: "Post Failure Instructions"
+ if: failure() && github.event_name == 'pull_request_target'
+ run: |
+ gh pr comment ${{ github.event.pull_request.number }} --body "**The CLA check failed.** Please ensure you have:
+ - Signed the CLA by commenting **'I have read the CLA Document and I hereby sign the CLA.'**
+ - Used the correct email address in your commits (matches the one you used to sign the CLA).
+
+ After fixing these issues, comment **'recheck'** to trigger the workflow again."
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/keploy/.github/workflows/codeql.yml b/keploy/.github/workflows/codeql.yml
new file mode 100755
index 0000000..6ae28be
--- /dev/null
+++ b/keploy/.github/workflows/codeql.yml
@@ -0,0 +1,84 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ main ]
+ schedule:
+ - cron: '34 8 * * 2'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'go' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+ # Learn more about CodeQL language support at https://git.io/codeql-language-support
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v2
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 https://git.io/JvXDl
+
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ #- run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
+
+ - name: Create a folder for security reports
+ run: mkdir -p reports
+
+ # - name: Generate Security Report
+ # uses: peter-murray/github-security-report-action@v2
+ # with:
+ # token: ${{ secrets.CODEQL_EXPORT_PAT_TOKEN }}
+ # outputDir: ./reports
+ # - name: Upload artifact
+ # uses: actions/upload-artifact@v3
+ # with:
+ # name: security-report
+ # path: /home/runner/work/keploy/results
diff --git a/keploy/.github/workflows/coverage_stage.yml b/keploy/.github/workflows/coverage_stage.yml
new file mode 100644
index 0000000..53c7495
--- /dev/null
+++ b/keploy/.github/workflows/coverage_stage.yml
@@ -0,0 +1,57 @@
+name: Calculate Coverage
+on:
+ workflow_call:
+
+jobs:
+ coverage:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Create directories to store coverage data
+ run: |
+ mkdir -p unzipped-cov
+ mkdir -p final-cov
+
+ - name: Download first coverage data of gin-mongo application
+ uses: actions/download-artifact@v4
+ with:
+ name: coverage-reports-samples-go-gin-mongo-0
+ path: ./temp-coverage-dir
+
+ - name: Download second coverage data of gin-mongo application
+ uses: actions/download-artifact@v4
+ with:
+ name: coverage-reports-samples-go-gin-mongo-1
+ path: ./temp-coverage-dir
+
+ - name: Unzip all coverage data zip files
+ run: |
+ unzip temp-coverage-dir/coverage-reports-samples-go-gin-mongo-0.zip -d unzipped-cov
+ unzip temp-coverage-dir/coverage-reports-samples-go-gin-mongo-1.zip -d unzipped-cov
+
+ - name: Combine all coverage data
+ run: |
+ find unzipped-cov -mindepth 1 -maxdepth 1 -type d -exec echo {} \; >> cov-dirs.txt
+ go tool covdata merge -o final-cov -i=`cat cov-dirs.txt | paste -sd ","`
+
+ - name: Calculate total coverage data and remove generated files
+ run: |
+ go tool covdata textfmt -i="./final-cov" -o="./unfiltered-cov.txt"
+ grep -v "go.keploy.io/server/v2/pkg/graph/generated.go" unfiltered-cov.txt > total-coverage.txt
+
+ - name: Display the coverage data
+ run: |
+ go tool cover -func total-coverage.txt
+
+ # - name: Generate the HTML coverage report
+ # run: |
+ # go tool cover -html=total-coverage.txt -o=coverage.html
+
+ # - name: Upload the HTML coverage report
+ # id: artifact-upload-step
+ # uses: actions/upload-artifact@v4
+ # with:
+ # name: coverage-report-html
+ # path: coverage.html
diff --git a/keploy/.github/workflows/docker-publish.yml b/keploy/.github/workflows/docker-publish.yml
new file mode 100755
index 0000000..1049ebb
--- /dev/null
+++ b/keploy/.github/workflows/docker-publish.yml
@@ -0,0 +1,96 @@
+name: Docker
+
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+
+on:
+ push:
+ tags: [ 'v*.*.*' ]
+
+env:
+ # Use docker.io for Docker Hub if empty
+ REGISTRY: ghcr.io
+ # github.repository as /
+ IMAGE_NAME: ${{ github.repository }}
+
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+ # This is used to complete the identity challenge
+ # with sigstore/fulcio when running outside of PRs.
+ id-token: write
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ # Install the cosign tool except on PR
+ # https://github.com/sigstore/cosign-installer
+ - name: Install cosign
+ if: github.event_name != 'pull_request'
+ uses: sigstore/cosign-installer@v3.4.0
+
+
+ # Workaround: https://github.com/docker/build-push-action/issues/461
+ - name: Setup Docker buildx
+ uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf
+
+ # Login against a Docker registry except on PR
+ # https://github.com/docker/login-action
+ - name: Log into registry ${{ env.REGISTRY }}
+ if: github.event_name != 'pull_request'
+ uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ # Extract metadata (tags, labels) for Docker
+ # https://github.com/docker/metadata-action
+ - name: Extract Docker metadata
+ id: meta
+ uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
+ with:
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+
+ - name: Set version
+ shell: bash
+ run: |
+ echo "VERSION=$(echo ${GITHUB_REF#refs/tags/v})" >> $GITHUB_ENV
+
+ # Build and push Docker image with Buildx (don't push on PR)
+ # https://github.com/docker/build-push-action
+ - name: Build and push Docker image
+ id: build-and-push
+ uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
+ with:
+ context: .
+ platforms: linux/amd64, linux/arm64
+ push: ${{ github.event_name != 'pull_request' }}
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+ build-args: |
+ SENTRY_DSN_DOCKER=${{secrets.SENTRY_DSN_DOCKER}}
+ VERSION=${{ env.VERSION }}
+ SERVER_URL=https://api.keploy.io
+ GITTHUB_APP_CLIENT_ID=${{ secrets.CLIENT_ID_GITHUB_APP }}
+
+ # Sign the resulting Docker image digest except on PRs.
+ # This will only write to the public Rekor transparency log when the Docker
+ # repository is public to avoid leaking data. If you would like to publish
+ # transparency data even for private images, pass --force to cosign below.
+ # https://github.com/sigstore/cosign
+ - name: Sign the published Docker image
+ if: ${{ github.event_name != 'pull_request' }}
+ env:
+ COSIGN_EXPERIMENTAL: "true"
+ # This step uses the identity token to provision an ephemeral certificate
+ # against the sigstore community Fulcio instance.
+ run: cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }}
diff --git a/keploy/.github/workflows/docs.yml b/keploy/.github/workflows/docs.yml
new file mode 100755
index 0000000..08304de
--- /dev/null
+++ b/keploy/.github/workflows/docs.yml
@@ -0,0 +1,38 @@
+name: Issue labeler
+on:
+ issues:
+ types: [ opened ]
+
+permissions:
+ contents: read
+
+jobs:
+ label-component:
+ runs-on: ubuntu-latest
+
+ permissions:
+ # required for all workflows
+ issues: write
+
+ # only required for workflows in private repositories
+ actions: read
+ contents: read
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Parse issue form
+ uses: stefanbuck/github-issue-parser@v3
+ id: issue-parser
+ with:
+ template-path: .github/ISSUE_TEMPLATE/--documentation-tools.yaml
+
+ - name: Set labels based on repository field
+ uses: redhat-plumbers-in-action/advanced-issue-labeler@v3
+ with:
+ issue-form: ${{ steps.issue-parser.outputs.jsonString }}
+ section: repository
+ block-list: |
+ None
+ Other
+ token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/keploy/.github/workflows/feat.yml b/keploy/.github/workflows/feat.yml
new file mode 100755
index 0000000..e61053b
--- /dev/null
+++ b/keploy/.github/workflows/feat.yml
@@ -0,0 +1,38 @@
+name: Issue labeler
+on:
+ issues:
+ types: [ opened ]
+
+permissions:
+ contents: read
+
+jobs:
+ label-component:
+ runs-on: ubuntu-latest
+
+ permissions:
+ # required for all workflows
+ issues: write
+
+ # only required for workflows in private repositories
+ actions: read
+ contents: read
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Parse issue form
+ uses: stefanbuck/github-issue-parser@v3
+ id: issue-parser
+ with:
+ template-path: .github/ISSUE_TEMPLATE/--feature-request.yaml
+
+ - name: Set labels based on repository field
+ uses: redhat-plumbers-in-action/advanced-issue-labeler@v3
+ with:
+ issue-form: ${{ steps.issue-parser.outputs.jsonString }}
+ section: repository
+ block-list: |
+ None
+ Other
+ token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/keploy/.github/workflows/go.yml b/keploy/.github/workflows/go.yml
new file mode 100755
index 0000000..c0f0983
--- /dev/null
+++ b/keploy/.github/workflows/go.yml
@@ -0,0 +1,31 @@
+name: Go on Linux
+
+on:
+ pull_request:
+ branches: [ main ]
+
+jobs:
+
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: ">=1.23"
+
+ - name: Build
+ run: go build -v ./...
+
+ - name: Build arm
+ run: GOOS=linux GOARCH=arm64 go build -v ./...
+
+ - uses: codfish/semantic-release-action@v3
+ with:
+ dry-run: true
+ additional-packages: '["@semantic-release/exec@7.0.3"]'
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/keploy/.github/workflows/go_macos.yaml b/keploy/.github/workflows/go_macos.yaml
new file mode 100644
index 0000000..77d6885
--- /dev/null
+++ b/keploy/.github/workflows/go_macos.yaml
@@ -0,0 +1,25 @@
+name: Go on macOS
+
+on:
+ pull_request:
+ branches: [ main ]
+
+jobs:
+
+ build:
+ runs-on: macos-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: ">=1.23"
+
+ - name: Build amd64
+ run: GOARCH=amd64 go build -v ./...
+
+ - name: Build arm64
+ run: GOARCH=arm64 go build -v ./...
+
diff --git a/keploy/.github/workflows/go_windows.yml b/keploy/.github/workflows/go_windows.yml
new file mode 100644
index 0000000..48a03c9
--- /dev/null
+++ b/keploy/.github/workflows/go_windows.yml
@@ -0,0 +1,31 @@
+name: Go on Windows
+
+on:
+ pull_request:
+ branches: [ main ]
+
+jobs:
+
+ build:
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: ">=1.23"
+
+ - name: Build amd64
+ run: |
+ set GOARCH=amd64
+ go build -v ./...
+ shell: cmd
+
+ - name: Build arm64
+ run: |
+ set GOARCH=arm64
+ go build -v ./...
+ shell: cmd
+
diff --git a/keploy/.github/workflows/golang_docker-compose.yml b/keploy/.github/workflows/golang_docker-compose.yml
new file mode 100644
index 0000000..ffe386d
--- /dev/null
+++ b/keploy/.github/workflows/golang_docker-compose.yml
@@ -0,0 +1,49 @@
+name: Golang On Docker Compose
+on:
+ workflow_call:
+jobs:
+ golang_docker_compose:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - job: record_latest_replay_build
+ record_src: latest
+ replay_src: build
+ - job: record_build_replay_latest
+ record_src: build
+ replay_src: latest
+ - job: record_build_replay_build
+ record_src: build
+ replay_src: build
+ name: ${{ matrix.job }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - id: image
+ uses: ./.github/actions/download-image
+
+ - id: record
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.record_src }}
+
+ - id: replay
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.replay_src }}
+
+ - name: Checkout the samples-go repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-go
+ path: samples-go
+ - name: Run echo-sql application
+ env:
+ RECORD_BIN: ${{ steps.record.outputs.path }}
+ REPLAY_BIN: ${{ steps.replay.outputs.path }}
+ run: |
+ cd samples-go/echo-sql
+ source ./../../.github/workflows/test_workflow_scripts/golang-docker-compose.sh
diff --git a/keploy/.github/workflows/golang_docker.yml b/keploy/.github/workflows/golang_docker.yml
new file mode 100755
index 0000000..b8e31b7
--- /dev/null
+++ b/keploy/.github/workflows/golang_docker.yml
@@ -0,0 +1,49 @@
+name: Golang On Docker
+on:
+ workflow_call:
+jobs:
+ golang_docker:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - job: record_latest_replay_build
+ record_src: latest
+ replay_src: build
+ - job: record_build_replay_latest
+ record_src: build
+ replay_src: latest
+ - job: record_build_replay_build
+ record_src: build
+ replay_src: build
+ name: ${{ matrix.job }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - id: image
+ uses: ./.github/actions/download-image
+
+ - id: record
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.record_src }}
+
+ - id: replay
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.replay_src }}
+
+ - name: Checkout the samples-go repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-go
+ path: samples-go
+ - name: Run gin-mongo application
+ env:
+ RECORD_BIN: ${{ steps.record.outputs.path }}
+ REPLAY_BIN: ${{ steps.replay.outputs.path }}
+ run: |
+ cd samples-go/gin-mongo
+ source ./../../.github/workflows/test_workflow_scripts/golang-docker.sh
diff --git a/keploy/.github/workflows/golang_http_linux.yml b/keploy/.github/workflows/golang_http_linux.yml
new file mode 100644
index 0000000..322d40a
--- /dev/null
+++ b/keploy/.github/workflows/golang_http_linux.yml
@@ -0,0 +1,53 @@
+name: Golang(http) On Linux
+on:
+ workflow_call:
+jobs:
+ http_golang_linux:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - job: record_latest_replay_build
+ record_src: latest
+ replay_src: build
+ - job: record_build_replay_latest
+ record_src: build
+ replay_src: latest
+ - job: record_build_replay_build
+ record_src: build
+ replay_src: build
+ name: ${{ matrix.job }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - id: record
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.record_src }}
+
+ - id: replay
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.replay_src }}
+
+ - name: Checkout the samples-go repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-go
+ path: samples-go
+
+ - name: echo record and replay binary
+ run: |
+ echo "Record binary path: ${{ steps.record.outputs.path }}"
+ echo "Replay binary path: ${{ steps.replay.outputs.path }}"
+
+ - name: Run go-http application
+ env:
+ RECORD_BIN: ${{ steps.record.outputs.path }}
+ REPLAY_BIN: ${{ steps.replay.outputs.path }}
+ run: |
+ cd samples-go/http-pokeapi
+ chmod +x ./../../.github/workflows/test_workflow_scripts/golang-http-linux.sh
+ source ./../../.github/workflows/test_workflow_scripts/golang-http-linux.sh
diff --git a/keploy/.github/workflows/golang_linux.yml b/keploy/.github/workflows/golang_linux.yml
new file mode 100644
index 0000000..c34dc28
--- /dev/null
+++ b/keploy/.github/workflows/golang_linux.yml
@@ -0,0 +1,46 @@
+name: Golang On Linux
+on:
+ workflow_call:
+jobs:
+ golang_linux:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - job: record_latest_replay_build
+ record_src: latest
+ replay_src: build
+ - job: record_build_replay_latest
+ record_src: build
+ replay_src: latest
+ - job: record_build_replay_build
+ record_src: build
+ replay_src: build
+ name: ${{ matrix.job }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - id: record
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.record_src }}
+
+ - id: replay
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.replay_src }}
+
+ - name: Checkout the samples-go repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-go
+ path: samples-go
+ - name: Run samples-go application
+ env:
+ RECORD_BIN: ${{ steps.record.outputs.path }}
+ REPLAY_BIN: ${{ steps.replay.outputs.path }}
+ run: |
+ cd samples-go/gin-mongo
+ source ./../../.github/workflows/test_workflow_scripts/golang-linux.sh
diff --git a/keploy/.github/workflows/golang_mysql_linux.yml b/keploy/.github/workflows/golang_mysql_linux.yml
new file mode 100644
index 0000000..2568400
--- /dev/null
+++ b/keploy/.github/workflows/golang_mysql_linux.yml
@@ -0,0 +1,46 @@
+name: MySQL-Golang On Linux
+on:
+ workflow_call:
+jobs:
+ mysql_golang_linux:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - job: record_latest_replay_build
+ record_src: latest
+ replay_src: build
+ - job: record_build_replay_latest
+ record_src: build
+ replay_src: latest
+ - job: record_build_replay_build
+ record_src: build
+ replay_src: build
+ name: ${{ matrix.job }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - id: record
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.record_src }}
+
+ - id: replay
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.replay_src }}
+
+ - name: Checkout the samples-go repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-go
+ path: samples-go
+ - name: Run echo-mysql application
+ env:
+ RECORD_BIN: ${{ steps.record.outputs.path }}
+ REPLAY_BIN: ${{ steps.replay.outputs.path }}
+ run: |
+ cd samples-go/echo-mysql
+ source ./../../.github/workflows/test_workflow_scripts/golang-mysql-linux.sh
\ No newline at end of file
diff --git a/keploy/.github/workflows/golangci-lint.yml b/keploy/.github/workflows/golangci-lint.yml
new file mode 100644
index 0000000..9d94af0
--- /dev/null
+++ b/keploy/.github/workflows/golangci-lint.yml
@@ -0,0 +1,48 @@
+name: golangci-lint
+on:
+ push:
+ branches:
+ - master
+ - main
+ pull_request:
+
+permissions:
+ contents: read
+ # Optional: allow read access to pull request. Use with `only-new-issues` option.
+ pull-requests: read
+
+# cancel the in-progress workflow when PR is refreshed.
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
+ cancel-in-progress: true
+
+jobs:
+ golangci:
+ name: lint
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-go@v5
+ with:
+ go-version: ">=1.23"
+ cache: false
+
+ - name: golangci-lint
+ uses: golangci/golangci-lint-action@v7
+ with:
+ only-new-issues: true
+ args: --timeout=5m
+
+ - name: Check gofmt (diff with line numbers)
+ run: |
+ output=$(gofmt -l -d .)
+ if [ -n "$output" ]; then
+ echo "🚨 gofmt found issues:"
+ echo "$output"
+ exit 1
+ fi
+
+ - name: Check for Deprecated Dependencies
+ run: |
+ source ./.github/workflows/test_workflow_scripts/check-deprecated-deps.sh
\ No newline at end of file
diff --git a/keploy/.github/workflows/greetings.yml b/keploy/.github/workflows/greetings.yml
new file mode 100755
index 0000000..d1bcbc2
--- /dev/null
+++ b/keploy/.github/workflows/greetings.yml
@@ -0,0 +1,17 @@
+name: Greetings
+
+on: [pull_request_target, issues]
+
+jobs:
+ greeting:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ steps:
+ - uses: actions/first-interaction@v1
+ continue-on-error: true
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ issue-message: 'Thank you and congratulations 🎉 for opening your very first issue in keploy'
+ pr-message: 'Thank you and congratulations 🎉 for opening your very first pull request in keploy'
diff --git a/keploy/.github/workflows/java_linux.yml b/keploy/.github/workflows/java_linux.yml
new file mode 100644
index 0000000..604b3c3
--- /dev/null
+++ b/keploy/.github/workflows/java_linux.yml
@@ -0,0 +1,58 @@
+name: Java on Linux
+on:
+ workflow_call:
+jobs:
+ java_linux:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - job: record_latest_replay_build
+ record_src: latest
+ replay_src: build
+ - job: record_build_replay_latest
+ record_src: build
+ replay_src: latest
+ - job: record_build_replay_build
+ record_src: build
+ replay_src: build
+ name: ${{ matrix.job }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - id: record
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.record_src }}
+
+ - id: replay
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.replay_src }}
+
+ - name: Checkout samples-java repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-java
+ path: samples-java
+
+ - name: Installing the necessary dependencies
+ run: |
+ cd samples-java/spring-petclinic/spring-petclinic-rest
+ ./mvnw dependency:resolve
+
+ - name: Compile the project
+ run: |
+ cd samples-java/spring-petclinic/spring-petclinic-rest
+ source ./../../../.github/workflows/test_workflow_scripts/update-java.sh
+ ./mvnw compile
+
+ - name: Run the spring-petclinic-rest app
+ env:
+ RECORD_BIN: ${{ steps.record.outputs.path }}
+ REPLAY_BIN: ${{ steps.replay.outputs.path }}
+ run: |
+ cd samples-java/spring-petclinic/spring-petclinic-rest
+ source ./../../../.github/workflows/test_workflow_scripts/java-linux.sh
diff --git a/keploy/.github/workflows/main.yml b/keploy/.github/workflows/main.yml
new file mode 100755
index 0000000..50cc354
--- /dev/null
+++ b/keploy/.github/workflows/main.yml
@@ -0,0 +1,26 @@
+name: Go
+
+on:
+ push:
+ branches: [ main ]
+
+jobs:
+
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: ">=1.23"
+
+ - name: Build
+ run: |
+ go build -v ./...
+
+ - name: Build arm
+ run: GOOS=linux GOARCH=arm64 go build -v ./...
diff --git a/keploy/.github/workflows/node_docker.yml b/keploy/.github/workflows/node_docker.yml
new file mode 100755
index 0000000..e523661
--- /dev/null
+++ b/keploy/.github/workflows/node_docker.yml
@@ -0,0 +1,50 @@
+name: Node on Docker
+on:
+ workflow_call:
+jobs:
+ node_docker:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - job: record_latest_replay_build
+ record_src: latest
+ replay_src: build
+ - job: record_build_replay_latest
+ record_src: build
+ replay_src: latest
+ - job: record_build_replay_build
+ record_src: build
+ replay_src: build
+ name: ${{ matrix.job }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - id: image
+ uses: ./.github/actions/download-image
+
+ - id: record
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.record_src }}
+
+ - id: replay
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.replay_src }}
+
+ - name: Checkout samples-typescript repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-typescript
+ path: samples-typescript
+
+ - name: Run the express-mongoose app
+ env:
+ RECORD_BIN: ${{ steps.record.outputs.path }}
+ REPLAY_BIN: ${{ steps.replay.outputs.path }}
+ run: |
+ cd samples-typescript/express-mongoose
+ source ./../../.github/workflows/test_workflow_scripts/node-docker.sh
diff --git a/keploy/.github/workflows/node_encoding.yaml b/keploy/.github/workflows/node_encoding.yaml
new file mode 100644
index 0000000..960dbb6
--- /dev/null
+++ b/keploy/.github/workflows/node_encoding.yaml
@@ -0,0 +1,48 @@
+name: Node Encoding Workflow
+description: "Run the Node Encoding Sample application with Keploy"
+on:
+ workflow_call:
+jobs:
+ node_encoding:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - job: record_latest_replay_build
+ record_src: latest
+ replay_src: build
+ - job: record_build_replay_latest
+ record_src: build
+ replay_src: latest
+ - job: record_build_replay_build
+ record_src: build
+ replay_src: build
+ name: ${{ matrix.job }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - id: record
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.record_src }}
+
+ - id: replay
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.replay_src }}
+
+ - name: Checkout Encoding sample repository
+ uses: actions/checkout@v4
+ with:
+ repository: ayush3160/http-br
+ path: http-br
+
+ - name: Run the Encoding Sample application
+ env:
+ RECORD_BIN: ${{ steps.record.outputs.path }}
+ REPLAY_BIN: ${{ steps.replay.outputs.path }}
+ run: |
+ cd http-br
+ source ./../.github/workflows/test_workflow_scripts/node-encoding.sh
diff --git a/keploy/.github/workflows/node_linux.yml b/keploy/.github/workflows/node_linux.yml
new file mode 100644
index 0000000..5a9194a
--- /dev/null
+++ b/keploy/.github/workflows/node_linux.yml
@@ -0,0 +1,47 @@
+name: Node on Linux
+on:
+ workflow_call:
+jobs:
+ node_linux:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - job: record_latest_replay_build
+ record_src: latest
+ replay_src: build
+ - job: record_build_replay_latest
+ record_src: build
+ replay_src: latest
+ - job: record_build_replay_build
+ record_src: build
+ replay_src: build
+ name: ${{ matrix.job }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - id: record
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.record_src }}
+
+ - id: replay
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.replay_src }}
+
+ - name: Checkout samples-typescript repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-typescript
+ path: samples-typescript
+
+ - name: Run the express-mongoose app
+ env:
+ RECORD_BIN: ${{ steps.record.outputs.path }}
+ REPLAY_BIN: ${{ steps.replay.outputs.path }}
+ run: |
+ cd samples-typescript/express-mongoose
+ source ./../../.github/workflows/test_workflow_scripts/node-linux.sh
diff --git a/keploy/.github/workflows/prepare_and_run.yml b/keploy/.github/workflows/prepare_and_run.yml
new file mode 100644
index 0000000..31b9d51
--- /dev/null
+++ b/keploy/.github/workflows/prepare_and_run.yml
@@ -0,0 +1,74 @@
+name: Prepare Binary and Run Workflows
+on:
+ pull_request:
+ branches: [main]
+jobs:
+ build-and-upload:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Build Keploy (PR)
+ run: go build -race -tags=viper_bind_struct -o keploy
+ - uses: actions/upload-artifact@v4
+ with: { name: build, path: keploy }
+
+ upload-latest:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Upload latest release
+ run: |
+ LATEST=$(curl -s https://api.github.com/repos/keploy/keploy/releases/latest | jq -r .tag_name)
+ URL="https://github.com/keploy/keploy/releases/download/${LATEST}/keploy_linux_amd64.tar.gz"
+ curl -L "$URL" -o keploy.tar.gz
+ tar -xzf keploy.tar.gz
+ chmod +x keploy
+ - uses: actions/upload-artifact@v4
+ with: { name: latest, path: keploy }
+
+ build-docker-image:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Build image
+ run: |
+ source ./.github/workflows/test_workflow_scripts/update-docker.sh
+
+ - name: Push image
+ run: |
+ docker push ttl.sh/keploy/keploy:1h
+
+ run_golang_linux:
+ needs: [build-and-upload, upload-latest]
+ uses: ./.github/workflows/golang_linux.yml
+ run_golang_http_linux:
+ needs: [build-and-upload, upload-latest]
+ uses: ./.github/workflows/golang_http_linux.yml
+ run_golang_mysql_linux:
+ needs: [build-and-upload, upload-latest]
+ uses: ./.github/workflows/golang_mysql_linux.yml
+ run_golang_docker:
+ needs: [build-and-upload, upload-latest,build-docker-image]
+ uses: ./.github/workflows/golang_docker.yml
+ run_golang_docker-compose:
+ needs: [build-and-upload, upload-latest, build-docker-image]
+ uses: ./.github/workflows/golang_docker-compose.yml
+ run_java_linux:
+ needs: [build-and-upload, upload-latest]
+ uses: ./.github/workflows/java_linux.yml
+ run_node_linux:
+ needs: [build-and-upload, upload-latest]
+ uses: ./.github/workflows/node_linux.yml
+ run_node_docker:
+ needs: [build-and-upload, upload-latest, build-docker-image]
+ uses: ./.github/workflows/node_docker.yml
+ run_python_docker:
+ needs: [build-and-upload, upload-latest, build-docker-image]
+ uses: ./.github/workflows/python_docker.yml
+ run_python_linux:
+ needs: [build-and-upload, upload-latest]
+ uses: ./.github/workflows/python_linux.yml
+ run_node_encoding:
+ needs: [build-and-upload, upload-latest]
+ uses: ./.github/workflows/node_encoding.yaml
\ No newline at end of file
diff --git a/keploy/.github/workflows/python_docker.yml b/keploy/.github/workflows/python_docker.yml
new file mode 100644
index 0000000..8cbb85a
--- /dev/null
+++ b/keploy/.github/workflows/python_docker.yml
@@ -0,0 +1,49 @@
+name: Python On Docker
+on:
+ workflow_call:
+jobs:
+ python_docker:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - job: record_latest_replay_build
+ record_src: latest
+ replay_src: build
+ - job: record_build_replay_latest
+ record_src: build
+ replay_src: latest
+ - job: record_build_replay_build
+ record_src: build
+ replay_src: build
+ name: ${{ matrix.job }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - id: image
+ uses: ./.github/actions/download-image
+
+ - id: record
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.record_src }}
+
+ - id: replay
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.replay_src }}
+
+ - name: Checkout the samples-python repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-python
+ path: samples-python
+ - name: Run the flask-mongo application
+ env:
+ RECORD_BIN: ${{ steps.record.outputs.path }}
+ REPLAY_BIN: ${{ steps.replay.outputs.path }}
+ run: |
+ cd samples-python/flask-mongo
+ source ./../../.github/workflows/test_workflow_scripts/python-docker.sh
diff --git a/keploy/.github/workflows/python_linux.yml b/keploy/.github/workflows/python_linux.yml
new file mode 100644
index 0000000..b9ce0a7
--- /dev/null
+++ b/keploy/.github/workflows/python_linux.yml
@@ -0,0 +1,45 @@
+name: Python On Linux
+on:
+ workflow_call:
+jobs:
+ python_linux:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - job: record_latest_replay_build
+ record_src: latest
+ replay_src: build
+ - job: record_build_replay_latest
+ record_src: build
+ replay_src: latest
+ - job: record_build_replay_build
+ record_src: build
+ replay_src: build
+ name: ${{ matrix.job }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - id: record
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.record_src }}
+
+ - id: replay
+ uses: ./.github/actions/download-binary
+ with:
+ src: ${{ matrix.replay_src }}
+ - name: Checkout the samples-python repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-python
+ path: samples-python
+ - name: Run the sample python application
+ env:
+ RECORD_BIN: ${{ steps.record.outputs.path }}
+ REPLAY_BIN: ${{ steps.replay.outputs.path }}
+ run: |
+ cd samples-python/django-postgres/django_postgres
+ source ../../../.github/workflows/test_workflow_scripts/python-linux.sh
diff --git a/keploy/.github/workflows/release.yml b/keploy/.github/workflows/release.yml
new file mode 100755
index 0000000..374acf9
--- /dev/null
+++ b/keploy/.github/workflows/release.yml
@@ -0,0 +1,101 @@
+name: Release
+on:
+ push:
+ tags:
+ - 'v*.*.*'
+
+permissions:
+ contents: write
+
+jobs:
+# build-ui:
+# runs-on: ubuntu-latest
+# steps:
+# - uses: actions/checkout@v4
+# with:
+# fetch-depth: 0
+#
+# - name: Set up Go
+# uses: actions/setup-go@v2
+# with:
+# go-version: "1.20"
+
+# - name: Checkout UI
+# uses: actions/checkout@v4
+# with:
+# repository: keploy/ui
+# path: ui
+#
+# - name: Cache UI
+# id: cache-ui
+# uses: actions/cache@v3
+# with:
+# path: web
+# key: ${{ hashFiles('ui/src') }}
+#
+# - name: Set up Node
+# if: steps.cache-ui.outputs.cache-hit != 'true'
+# uses: actions/setup-node@v3
+# with:
+# node-version: '14'
+#
+# - name: Build web app
+# if: steps.cache-ui.outputs.cache-hit != 'true'
+# run: |
+# cd $GITHUB_WORKSPACE/ui
+# npm install
+# npm run build
+# cp -r public $GITHUB_WORKSPACE/web
+# rm -rf $GITHUB_WORKSPACE/ui
+
+ build-go:
+ runs-on: macos-latest
+ # runs-on: ubuntu-latest
+# needs: build-ui
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: ">=1.23"
+
+# - name: Checkout UI
+# uses: actions/checkout@v4
+# with:
+# repository: keploy/ui
+# path: ui
+#
+# - name: Cache UI
+# id: cache-ui
+# uses: actions/cache@v3
+# with:
+# path: web
+# key: ${{ hashFiles('ui/src') }}
+ - name: Import Code-Signing Certificates
+ uses: Apple-Actions/import-codesign-certs@v5
+ with:
+ # The certificates in a PKCS12 file encoded as a base64 string created with "openssl base64 -in cert.p12 -out cert-base64.txt"
+ p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }}
+ # The password used to import the PKCS12 file.
+ p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }}
+ - name: Install gon for code signing and app notarization
+ run: |
+ wget -q https://github.com/Bearer/gon/releases/download/v0.0.37/gon_macos.zip
+ unzip gon_macos.zip -d /usr/local/bin
+ rm -rf gon_macos.zip
+
+ - name: Run GoReleaser
+ uses: goreleaser/goreleaser-action@v6
+ with:
+ distribution: goreleaser
+ version: v1.26.2
+ args: release --rm-dist
+ env:
+ AC_PROVIDER: ${{ secrets.APPLE_ACCOUNT_TEAM_ID }}
+ AC_PASSWORD: ${{ secrets.APPLE_ACCOUNT_PASSWORD }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SENTRY_DSN_BINARY: ${{ secrets.SENTRY_DSN_BINARY }}
+ GITTHUB_APP_CLIENT_ID: ${{ secrets.CLIENT_ID_GITHUB_APP }}
+ SERVER_URL: https://api.keploy.io
diff --git a/keploy/.github/workflows/sample-run.yml b/keploy/.github/workflows/sample-run.yml
new file mode 100755
index 0000000..870b80e
--- /dev/null
+++ b/keploy/.github/workflows/sample-run.yml
@@ -0,0 +1,25 @@
+name: sample-run
+
+on:
+ push:
+ branches: [main]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: ">=1.23"
+
+ - name: Build Keploy
+ run: |
+ go build -v ./...
+
+ - name: Build arm
+ run: GOOS=linux GOARCH=arm64 go build -v ./...
\ No newline at end of file
diff --git a/keploy/.github/workflows/test-go-mongo-1.yml b/keploy/.github/workflows/test-go-mongo-1.yml
new file mode 100644
index 0000000..7164ea9
--- /dev/null
+++ b/keploy/.github/workflows/test-go-mongo-1.yml
@@ -0,0 +1,60 @@
+name: Test GinApp 1
+on:
+ workflow_call:
+
+jobs:
+ test-go-mongo:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Download Keploy Build Binary
+ uses: actions/download-artifact@v4
+ with:
+ name: keploy-binary
+ path: /usr/local/bin
+
+ - name: Giving permission to binary
+ run: |
+ sudo chmod +x /usr/local/bin/keployB
+
+ - name: Download the latest released binary of keploy
+ run: |
+ curl --silent --location "https://github.com/keploy/keploy/releases/latest/download/keploy_linux_amd64.tar.gz" | tar xz -C /tmp
+ sudo mkdir -p /usr/local/bin && sudo mv /tmp/keploy /usr/local/bin/keployR
+
+ # Get the pilot for tests and mocks assertion
+ - name: Get the pilot for tests and mocks assertion
+ run: |
+ curl --silent -o pilot --location "https://github.com/keploy/pilot/releases/latest/download/pilot_linux_amd64" &&
+ sudo chmod a+x pilot && sudo mkdir -p /usr/local/bin && sudo mv pilot /usr/local/bin
+
+ - name: Checkout to the samples-go repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-go
+ ref: native-linux
+ path: samples-go
+
+ - name: Build the gin-mongo application
+ run: |
+ cd samples-go/gin-mongo
+ go build -o ginApp .
+
+ # add the noisy fields in the config file if any.
+ - name: Add noisy fields in the config file
+ run: |
+ echo "You can add the noisy fields in the config file if any."
+
+ - name: Run testing script for gin-mongo application
+ uses: ./.github/actions/tester
+ with:
+ working-directory: samples-go/gin-mongo
+ command: ./ginApp
+ delay: 7
+ keploy-record-bin: /usr/local/bin/keployR
+ keploy-test-bin: /usr/local/bin/keployB
+ mode: 0
+
+
diff --git a/keploy/.github/workflows/test-go-mongo-2.yml b/keploy/.github/workflows/test-go-mongo-2.yml
new file mode 100644
index 0000000..43daa46
--- /dev/null
+++ b/keploy/.github/workflows/test-go-mongo-2.yml
@@ -0,0 +1,58 @@
+name: Test GinApp 2
+on:
+ workflow_call:
+
+jobs:
+ test-go-mongo:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Download Keploy Build Binary
+ uses: actions/download-artifact@v4
+ with:
+ name: keploy-binary
+ path: /usr/local/bin
+
+ - name: Giving permission to binary
+ run: |
+ sudo chmod +x /usr/local/bin/keployB
+
+ - name: Download the latest released binary of keploy
+ run: |
+ curl --silent --location "https://github.com/keploy/keploy/releases/latest/download/keploy_linux_amd64.tar.gz" | tar xz -C /tmp
+ sudo mkdir -p /usr/local/bin && sudo mv /tmp/keploy /usr/local/bin/keployR
+
+ # Get the pilot for tests and mocks assertion
+ - name: Get the pilot for tests and mocks assertion
+ run: |
+ curl --silent -o pilot --location "https://github.com/keploy/pilot/releases/latest/download/pilot_linux_amd64" &&
+ sudo chmod a+x pilot && sudo mkdir -p /usr/local/bin && sudo mv pilot /usr/local/bin
+
+ - name: Checkout to the samples-go repository
+ uses: actions/checkout@v4
+ with:
+ repository: keploy/samples-go
+ ref: native-linux
+ path: samples-go
+
+ - name: Build the gin-mongo application
+ run: |
+ cd samples-go/gin-mongo
+ go build -o ginApp .
+
+ # add the noisy fields in the config file if any.
+ - name: Add noisy fields in the config file
+ run: |
+ echo "You can add the noisy fields in the config file if any."
+
+ - name: Run testing script for gin-mongo application
+ uses: ./.github/actions/tester
+ with:
+ working-directory: samples-go/gin-mongo
+ command: ./ginApp
+ delay: 7
+ keploy-record-bin: /usr/local/bin/keployB
+ keploy-test-bin: /usr/local/bin/keployR
+ mode: 1
\ No newline at end of file
diff --git a/keploy/.github/workflows/test_workflow_scripts/check-deprecated-deps.sh b/keploy/.github/workflows/test_workflow_scripts/check-deprecated-deps.sh
new file mode 100644
index 0000000..fbcc414
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/check-deprecated-deps.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Extract direct dependencies from go.mod using go mod edit -json
+direct_deps=$(go mod edit -json | jq -r '.Require[] | select(.Indirect == null) | .Path')
+
+# List all modules with their update status
+output=$(go list -m -u all)
+
+found_deprecated=false
+
+while IFS= read -r line; do
+ mod_path=$(echo "$line" | awk '{print $1}')
+
+ if echo "$direct_deps" | grep -q "$mod_path"; then
+ if [[ "$line" == *"deprecated"* || "$line" == *"retracted"* ]]; then
+ echo "Deprecated/retracted direct dependency found: $line"
+ found_deprecated=true
+ fi
+ fi
+done <<< "$output"
+
+if [ "$found_deprecated" = true ]; then
+ echo "Exiting with failure due to deprecated direct dependencies."
+ exit 1
+fi
\ No newline at end of file
diff --git a/keploy/.github/workflows/test_workflow_scripts/golang-docker-compose.sh b/keploy/.github/workflows/test_workflow_scripts/golang-docker-compose.sh
new file mode 100644
index 0000000..a196679
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/golang-docker-compose.sh
@@ -0,0 +1,127 @@
+#!/bin/bash
+
+source ./../../.github/workflows/test_workflow_scripts/test-iid.sh
+
+# Build Docker Image
+docker compose build
+
+# Remove any preexisting keploy tests and mocks.
+sudo rm -rf keploy/
+
+# Generate the keploy-config file.
+sudo -E env PATH=$PATH $RECORD_BIN config --generate
+
+# Update the global noise to ts in the config file.
+config_file="./keploy.yml"
+sed -i 's/global: {}/global: {"body": {"ts":[]}}/' "$config_file"
+
+container_kill() {
+ pid=$(pgrep -n keploy)
+ echo "$pid Keploy PID"
+ echo "Killing keploy"
+ sudo kill $pid
+}
+
+send_request(){
+ sleep 10
+ app_started=false
+ while [ "$app_started" = false ]; do
+ if curl -X GET http://localhost:8082/health; then
+ app_started=true
+ fi
+ sleep 3
+ done
+ echo "App started"
+ # Make curl calls to record the test cases and mocks.
+ curl --request POST \
+ --url http://localhost:8082/url \
+ --header 'content-type: application/json' \
+ --data '{
+ "url": "https://google.com"
+ }'
+
+ curl --request POST \
+ --url http://localhost:8082/url \
+ --header 'content-type: application/json' \
+ --data '{
+ "url": "https://facebook.com"
+ }'
+
+ curl -X GET http://localhost:8082/health
+
+ # Wait for 5 seconds for keploy to record the test cases and mocks.
+ sleep 5
+ container_kill
+ wait
+}
+
+for i in {1..2}; do
+ container_name="echoApp"
+ send_request &
+ sudo -E env PATH=$PATH $RECORD_BIN record -c "docker compose up" --container-name "$container_name" --generateGithubActions=false &> "${container_name}.txt"
+
+ if grep "WARNING: DATA RACE" "${container_name}.txt"; then
+ echo "Race condition detected in recording, stopping pipeline..."
+ cat "${container_name}.txt"
+ exit 1
+ fi
+ if grep "ERROR" "${container_name}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${container_name}.txt"
+ exit 1
+ fi
+ sleep 5
+
+ echo "Recorded test case and mocks for iteration ${i}"
+done
+
+# Shutdown services before test mode - Keploy should use mocks for dependencies
+echo "Shutting down docker compose services before test mode..."
+docker compose down
+echo "Services stopped - Keploy should now use mocks for dependency interactions"
+
+# Start keploy in test mode.
+test_container="echoApp"
+sudo -E env PATH=$PATH $REPLAY_BIN test -c 'docker compose up' --containerName "$test_container" --apiTimeout 60 --delay 20 --generate-github-actions=false &> "${test_container}.txt"
+
+if grep "ERROR" "${test_container}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${test_container}.txt"
+ exit 1
+fi
+
+if grep "WARNING: DATA RACE" "${test_container}.txt"; then
+ echo "Race condition detected in test, stopping pipeline..."
+ cat "${test_container}.txt"
+ exit 1
+fi
+
+all_passed=true
+
+for i in {0..1}
+do
+ # Define the report file for each test set
+ report_file="./keploy/reports/test-run-0/test-set-$i-report.yaml"
+
+ # Extract the test status
+ test_status=$(grep 'status:' "$report_file" | head -n 1 | awk '{print $2}')
+
+ # Print the status for debugging
+ echo "Test status for test-set-$i: $test_status"
+
+ # Check if any test set did not pass
+ if [ "$test_status" != "PASSED" ]; then
+ all_passed=false
+ echo "Test-set-$i did not pass."
+ break # Exit the loop early as all tests need to pass
+ fi
+done
+
+# Check the overall test status and exit accordingly
+if [ "$all_passed" = true ]; then
+ echo "All tests passed"
+ exit 0
+else
+ cat "${test_container}.txt"
+ exit 1
+fi
diff --git a/keploy/.github/workflows/test_workflow_scripts/golang-docker.sh b/keploy/.github/workflows/test_workflow_scripts/golang-docker.sh
new file mode 100755
index 0000000..5f66285
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/golang-docker.sh
@@ -0,0 +1,134 @@
+#!/bin/bash
+
+source ./../../.github/workflows/test_workflow_scripts/test-iid.sh
+
+# Start mongo before starting keploy.
+docker network create keploy-network
+docker run --name mongoDb --rm --net keploy-network -p 27017:27017 -d mongo
+
+# Generate the keploy-config file.
+sudo -E env PATH=$PATH $RECORD_BIN config --generate
+
+# Update the global noise to ts.
+config_file="./keploy.yml"
+sed -i 's/global: {}/global: {"body": {"ts":[]}}/' "$config_file"
+
+# Remove any preexisting keploy tests and mocks.
+sudo rm -rf keploy/
+docker logs mongoDb &
+
+# Start keploy in record mode.
+docker build -t gin-mongo .
+docker rm -f ginApp 2>/dev/null || true
+
+container_kill() {
+ pid=$(pgrep -n keploy)
+ echo "$pid Keploy PID"
+ echo "Killing keploy"
+ sudo kill $pid
+}
+
+send_request(){
+ sleep 10
+ app_started=false
+ while [ "$app_started" = false ]; do
+ if curl -X GET http://localhost:8080/CJBKJd92; then
+ app_started=true
+ fi
+ sleep 3
+ done
+ echo "App started"
+ # Start making curl calls to record the testcases and mocks.
+ curl --request POST \
+ --url http://localhost:8080/url \
+ --header 'content-type: application/json' \
+ --data '{
+ "url": "https://google.com"
+ }'
+
+ curl --request POST \
+ --url http://localhost:8080/url \
+ --header 'content-type: application/json' \
+ --data '{
+ "url": "https://facebook.com"
+ }'
+
+ curl -X GET http://localhost:8080/CJBKJd92
+
+ # Wait for 5 seconds for keploy to record the tcs and mocks.
+ sleep 5
+ container_kill
+ wait
+}
+
+for i in {1..2}; do
+ container_name="ginApp_${i}"
+ send_request &
+ sudo -E env PATH=$PATH $RECORD_BIN record -c "docker run -p8080:8080 --net keploy-network --rm --name $container_name gin-mongo" --container-name "$container_name" &> "${container_name}.txt"
+
+ if grep "WARNING: DATA RACE" "${container_name}.txt"; then
+ echo "Race condition detected in recording, stopping pipeline..."
+ cat "${container_name}.txt"
+ exit 1
+ fi
+ if grep "ERROR" "${container_name}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${container_name}.txt"
+ exit 1
+ fi
+ sleep 5
+
+ echo "Recorded test case and mocks for iteration ${i}"
+done
+
+# Shutdown mongo before test mode - Keploy should use mocks for database interactions
+echo "Shutting down mongo before test mode..."
+docker stop mongoDb || true
+docker rm mongoDb || true
+echo "MongoDB stopped - Keploy should now use mocks for database interactions"
+
+# Start the keploy in test mode.
+test_container="ginApp_test"
+sudo -E env PATH=$PATH $REPLAY_BIN test -c 'docker run -p8080:8080 --net keploy-network --name ginApp_test gin-mongo' --containerName "$test_container" --apiTimeout 60 --delay 20 --generate-github-actions=false &> "${test_container}.txt"
+
+if grep "ERROR" "${test_container}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${test_container}.txt"
+ exit 1
+fi
+
+if grep "WARNING: DATA RACE" "${test_container}.txt"; then
+ echo "Race condition detected in test, stopping pipeline..."
+ cat "${test_container}.txt"
+ exit 1
+fi
+
+all_passed=true
+
+for i in {0..1}
+do
+ # Define the report file for each test set
+ report_file="./keploy/reports/test-run-0/test-set-$i-report.yaml"
+
+ # Extract the test status
+ test_status=$(grep 'status:' "$report_file" | head -n 1 | awk '{print $2}')
+
+ # Print the status for debugging
+ echo "Test status for test-set-$i: $test_status"
+
+ # Check if any test set did not pass
+ if [ "$test_status" != "PASSED" ]; then
+ all_passed=false
+ echo "Test-set-$i did not pass."
+ break # Exit the loop early as all tests need to pass
+ fi
+done
+
+# Check the overall test status and exit accordingly
+if [ "$all_passed" = true ]; then
+ echo "All tests passed"
+ exit 0
+else
+ cat "${test_container}.txt"
+ exit 1
+fi
\ No newline at end of file
diff --git a/keploy/.github/workflows/test_workflow_scripts/golang-http-linux.sh b/keploy/.github/workflows/test_workflow_scripts/golang-http-linux.sh
new file mode 100644
index 0000000..668fc02
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/golang-http-linux.sh
@@ -0,0 +1,134 @@
+#!/bin/bash
+
+echo "$RECORD_BIN"
+echo "$REPLAY_BIN"
+
+source ./../../.github/workflows/test_workflow_scripts/test-iid.sh
+echo "iid.sh executed"
+
+# Checkout a different branch
+git fetch origin
+#git checkout native-linux
+
+# Check if there is a keploy-config file, if there is, delete it.
+if [ -f "./keploy.yml" ]; then
+ rm ./keploy.yml
+fi
+
+rm -rf keploy/
+
+# Build go binary
+go build -o http-pokeapi
+echo "go binary built"
+
+# Generate the keploy-config file.
+sudo "$RECORD_BIN" config --generate
+
+# Update the global noise to updated_at.
+config_file="./keploy.yml"
+sed -i 's/global: {}/global: {"body": {"updated_at":[]}}/' "$config_file"
+
+send_request() {
+ local index=$1
+
+ sleep 10
+ app_started=false
+ while [ "$app_started" = false ]; do
+ if curl -X GET http://localhost:8080/api/locations; then
+ app_started=true
+ fi
+ sleep 3
+ done
+
+ echo "App started"
+
+ response=$(curl -s -X GET http://localhost:8080/api/locations)
+
+ # Extract any location from the reponse
+ location=$(echo "$response" | jq -r ".location[$index]")
+
+ response=$(curl -s -X GET http://localhost:8080/api/locations/$location)
+
+ # Extract any pokemon from the response
+ pokemon=$(echo "$response" | jq -r ".[$index]")
+
+ curl -s -X GET http://localhost:8080/api/pokemon/$pokemon
+
+ curl -s -X GET http://localhost:8080/api/greet
+
+ curl -s -X GET http://localhost:8080/api/greet?format=html
+
+ curl -s -X GET http://localhost:8080/api/greet?format=xml
+
+ # Wait for 10 seconds for Keploy to record the tcs and mocks.
+ sleep 10
+ pid=$(pgrep keploy)
+ echo "$pid Keploy PID"
+ echo "Killing Keploy"
+ sudo kill $pid
+}
+
+for i in {1..2}; do
+ app_name="http-pokeapi_${i}"
+ send_request $i &
+ sudo -E env PATH="$PATH" "$RECORD_BIN" record -c "./http-pokeapi" --generateGithubActions=false &> "${app_name}.txt"
+ if grep "ERROR" "${app_name}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${app_name}.txt"
+ exit 1
+ fi
+ if grep "WARNING: DATA RACE" "${app_name}.txt"; then
+ echo "Race condition detected in recording, stopping pipeline..."
+ cat "${app_name}.txt"
+ exit 1
+ fi
+ sleep 5
+ wait
+ echo "Recorded test case and mocks for iteration ${i}"
+done
+
+# Start the go-http app in test mode.
+sudo -E env PATH="$PATH" "$REPLAY_BIN" test -c "./http-pokeapi" --delay 7 --generateGithubActions=false &> test_logs.txt
+
+if grep "ERROR" "test_logs.txt"; then
+ echo "Error found in pipeline..."
+ cat "test_logs.txt"
+ exit 1
+fi
+
+if grep "WARNING: DATA RACE" "test_logs.txt"; then
+ echo "Race condition detected in test, stopping pipeline..."
+ cat "test_logs.txt"
+ exit 1
+fi
+
+all_passed=true
+
+# Get the test results from the testReport file.
+for i in {0..1}
+do
+ # Define the report file for each test set
+ report_file="./keploy/reports/test-run-0/test-set-$i-report.yaml"
+
+ # Extract the test status
+ test_status=$(grep 'status:' "$report_file" | head -n 1 | awk '{print $2}')
+
+ # Print the status for debugging
+ echo "Test status for test-set-$i: $test_status"
+
+ # Check if any test set did not pass
+ if [ "$test_status" != "PASSED" ]; then
+ all_passed=false
+ echo "Test-set-$i did not pass."
+ break # Exit the loop early as all tests need to pass
+ fi
+done
+
+# Check the overall test status and exit accordingly
+if [ "$all_passed" = true ]; then
+ echo "All tests passed"
+ exit 0
+else
+ cat "test_logs.txt"
+ exit 1
+fi
diff --git a/keploy/.github/workflows/test_workflow_scripts/golang-linux.sh b/keploy/.github/workflows/test_workflow_scripts/golang-linux.sh
new file mode 100644
index 0000000..693d5ab
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/golang-linux.sh
@@ -0,0 +1,144 @@
+#!/bin/bash
+
+source ./../../.github/workflows/test_workflow_scripts/test-iid.sh
+
+# Checkout a different branch
+git fetch origin
+git checkout native-linux
+
+# Start mongo before starting keploy.
+docker run --rm -d -p27017:27017 --name mongoDb mongo
+
+# Check if there is a keploy-config file, if there is, delete it.
+if [ -f "./keploy.yml" ]; then
+ rm ./keploy.yml
+fi
+
+# Generate the keploy-config file.
+sudo $RECORD_BIN config --generate
+
+# Update the global noise to ts.
+config_file="./keploy.yml"
+sed -i 's/global: {}/global: {"body": {"ts":[]}}/' "$config_file"
+
+sed -i 's/ports: 0/ports: 27017/' "$config_file"
+
+# Remove any preexisting keploy tests and mocks.
+rm -rf keploy/
+
+# Build the binary.
+go build -o ginApp
+
+
+send_request(){
+ sleep 10
+ app_started=false
+ while [ "$app_started" = false ]; do
+ if curl --request POST \
+ --url http://localhost:8080/url \
+ --header 'content-type: application/json' \
+ --data '{
+ "url": "https://facebook.com"
+ }'; then
+ app_started=true
+ fi
+ sleep 3 # wait for 3 seconds before checking again.
+ done
+ echo "App started"
+ # Start making curl calls to record the testcases and mocks.
+ curl --request POST \
+ --url http://localhost:8080/url \
+ --header 'content-type: application/json' \
+ --data '{
+ "url": "https://google.com"
+ }'
+
+ curl --request POST \
+ --url http://localhost:8080/url \
+ --header 'content-type: application/json' \
+ --data '{
+ "url": "https://facebook.com"
+ }'
+
+ curl -X GET http://localhost:8080/CJBKJd92
+
+ # Wait for 10 seconds for keploy to record the tcs and mocks.
+ sleep 10
+ pid=$(pgrep keploy)
+ echo "$pid Keploy PID"
+ echo "Killing keploy"
+ sudo kill $pid
+}
+
+
+for i in {1..2}; do
+ app_name="javaApp_${i}"
+ send_request &
+ sudo -E env PATH="$PATH" $RECORD_BIN record -c "./ginApp" &> "${app_name}.txt"
+ if grep "ERROR" "${app_name}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${app_name}.txt"
+ exit 1
+ fi
+ if grep "WARNING: DATA RACE" "${app_name}.txt"; then
+ echo "Race condition detected in recording, stopping pipeline..."
+ cat "${app_name}.txt"
+ exit 1
+ fi
+ sleep 5
+ wait
+ echo "Recorded test case and mocks for iteration ${i}"
+done
+
+# Shutdown mongo before test mode - Keploy should use mocks for database interactions
+echo "Shutting down mongo before test mode..."
+docker stop mongoDb || true
+docker rm mongoDb || true
+echo "MongoDB stopped - Keploy should now use mocks for database interactions"
+
+# Start the gin-mongo app in test mode.
+sudo -E env PATH="$PATH" $REPLAY_BIN test -c "./ginApp" --delay 7 &> test_logs.txt
+
+if grep "ERROR" "test_logs.txt"; then
+ echo "Error found in pipeline..."
+ cat "test_logs.txt"
+ exit 1
+fi
+
+if grep "WARNING: DATA RACE" "test_logs.txt"; then
+ echo "Race condition detected in test, stopping pipeline..."
+ cat "test_logs.txt"
+ exit 1
+fi
+
+all_passed=true
+
+
+# Get the test results from the testReport file.
+for i in {0..1}
+do
+ # Define the report file for each test set
+ report_file="./keploy/reports/test-run-0/test-set-$i-report.yaml"
+
+ # Extract the test status
+ test_status=$(grep 'status:' "$report_file" | head -n 1 | awk '{print $2}')
+
+ # Print the status for debugging
+ echo "Test status for test-set-$i: $test_status"
+
+ # Check if any test set did not pass
+ if [ "$test_status" != "PASSED" ]; then
+ all_passed=false
+ echo "Test-set-$i did not pass."
+ break # Exit the loop early as all tests need to pass
+ fi
+done
+
+# Check the overall test status and exit accordingly
+if [ "$all_passed" = true ]; then
+ echo "All tests passed"
+ exit 0
+else
+ cat "test_logs.txt"
+ exit 1
+fi
\ No newline at end of file
diff --git a/keploy/.github/workflows/test_workflow_scripts/golang-mysql-linux.sh b/keploy/.github/workflows/test_workflow_scripts/golang-mysql-linux.sh
new file mode 100644
index 0000000..f4f85d4
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/golang-mysql-linux.sh
@@ -0,0 +1,178 @@
+#!/usr/bin/env bash
+
+# safer bash, but we’ll locally disable -e around commands we want to inspect
+set -Eeuo pipefail
+
+# ----- helpers -----
+section() { echo "::group::$*"; }
+endsec() { echo "::endgroup::"; }
+die() {
+ rc=$?
+ echo "::error::Pipeline failed (exit=$rc). Dumping context…"
+ echo "== docker ps =="; docker ps || true
+ echo "== mysql logs (complete) =="; docker logs mysql-container || true
+ echo "== workspace tree (depth 3) =="; find . -maxdepth 3 -type d -print | sort || true
+ echo "== keploy tree (depth 4) =="; find ./keploy -maxdepth 4 -type f -print 2>/dev/null | sort || true
+ echo "== *.txt logs (complete) =="; for f in ./*.txt; do [[ -f "$f" ]] && { echo "--- $f ---"; cat "$f"; }; done
+ [[ -f test_logs.txt ]] && { echo "== test_logs.txt (complete) =="; cat test_logs.txt; }
+ exit "$rc"
+}
+trap die ERR
+
+wait_for_mysql() {
+ section "Wait for MySQL readiness"
+ # ping until mysqld accepts connections
+ for i in {1..60}; do
+ if docker exec mysql-container mysql -uroot -ppassword -e "SELECT 1" >/dev/null 2>&1; then
+ echo "MySQL is ready."
+ endsec; return 0
+ fi
+ sleep 1
+ done
+ echo "::error::MySQL did not become ready in time"
+ endsec; return 1
+}
+
+send_request() {
+ local kp_pid="$1"
+
+ # Wait for the app to report healthy
+ for i in {1..60}; do
+ if curl -fsS http://localhost:9090/healthcheck >/dev/null; then
+ echo "good!App started"
+ break
+ fi
+ sleep 1
+ done
+
+ curl -sS -X POST http://localhost:9090/shorten -H "Content-Type: application/json" \
+ -d '{"url": "https://github.com"}' || true
+
+ curl -sS http://localhost:9090/resolve/4KepjkTT || true
+
+ # Give Keploy a moment to persist artifacts, then stop it cleanly
+ sleep 10
+ echo "$kp_pid Keploy PID"
+ echo "Killing Keploy"
+ sudo kill "$kp_pid" 2>/dev/null || true
+}
+
+run_record_iteration() {
+ local idx="$1"
+ local app_name="urlShort_${idx}"
+
+ section "Record iteration $idx"
+
+ # Clean slate per run
+ rm -rf keploy/ keploy.yml || true
+
+ # Start mysql (once) only for first iteration
+ if ! docker ps --format '{{.Names}}' | grep -q '^mysql-container$'; then
+ docker run --name mysql-container -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=uss \
+ -p 3306:3306 --rm -d mysql:latest
+ wait_for_mysql
+ fi
+
+ # Generate config
+ sudo "$RECORD_BIN" config --generate
+ sed -i 's/global: {}/global: {"body": {"updated_at":[]}}/' ./keploy.yml
+
+ # Build app
+ go build -o urlShort
+
+ # Start recording in background so we capture its PID explicitly
+ sudo -E env PATH="$PATH" "$RECORD_BIN" record -c "./urlShort" --generateGithubActions=false \
+ > "${app_name}.txt" 2>&1 &
+ local KEPLOY_PID=$!
+
+ # Drive traffic + stop keploy
+ send_request "$KEPLOY_PID"
+
+ # Wait for keploy exit and capture code
+ set +e
+ wait "$KEPLOY_PID"
+ local rc=$?
+ set -e
+ echo "Record exit code: $rc"
+ if [[ $rc -ne 0 ]]; then
+ echo "::error::Keploy record exited with $rc (iteration $idx)"
+ fi
+
+ # Quick sanity: ensure something was written
+ echo "== keploy artifacts after record =="
+ find ./keploy -maxdepth 3 -type f | sort || true
+
+ # Fail on obvious errors/races in log
+ if grep -q "WARNING: DATA RACE" "${app_name}.txt"; then
+ echo "::error::Data race detected in ${app_name}.txt"
+ cat "${app_name}.txt"
+ return 1
+ fi
+ if grep -q "ERROR" "${app_name}.txt"; then
+ echo "::warning::Errors found in ${app_name}.txt (not fatal unless record failed)"
+ cat "${app_name}.txt"
+ fi
+
+ endsec
+}
+
+# ----- main flow -----
+
+section "Environment"
+echo "RECORD_BIN: $RECORD_BIN"
+echo "REPLAY_BIN : $REPLAY_BIN"
+"$RECORD_BIN" version 2>/dev/null || true
+"$REPLAY_BIN" version 2>/dev/null || true
+endsec
+
+for i in 1 2; do
+ run_record_iteration "$i"
+ echo "Recorded test case and mocks for iteration $i"
+done
+
+section "Shutdown MySQL before test mode"
+# Stop MySQL container - Keploy should use mocks for database interactions
+docker stop mysql-container || true
+docker rm mysql-container || true
+echo "MySQL stopped - Keploy should now use mocks for database interactions"
+endsec
+
+section "Replay"
+# Run replay but DON'T crash the step; capture rc and print logs
+set +e
+sudo -E env PATH="$PATH" "$REPLAY_BIN" test -c "./urlShort" --delay 7 --generateGithubActions=false \
+ > test_logs.txt 2>&1
+REPLAY_RC=$?
+set -e
+echo "Replay exit code: $REPLAY_RC"
+cat test_logs.txt || true
+endsec
+
+# If replay failed, still try to read reports to say which set failed
+section "Check reports"
+# Find the most recent test-run dir (don’t assume test-run-0)
+RUN_DIR=$(ls -1dt ./keploy/reports/test-run-* 2>/dev/null | head -n1 || true)
+if [[ -z "${RUN_DIR:-}" ]]; then
+ echo "::error::No test-run directory found under ./keploy/reports"
+ [[ $REPLAY_RC -ne 0 ]] && exit "$REPLAY_RC" || exit 1
+fi
+
+echo "Using reports from: $RUN_DIR"
+all_passed=true
+for rpt in "$RUN_DIR"/test-set-*-report.yaml; do
+ [[ -f "$rpt" ]] || continue
+ status=$(awk '/^status:/{print $2; exit}' "$rpt")
+ echo "Test status for $(basename "$rpt"): ${status:-}"
+ if [[ "$status" != "PASSED" ]]; then
+ all_passed=false
+ fi
+done
+endsec
+
+if [[ "$all_passed" == "true" && $REPLAY_RC -eq 0 ]]; then
+ echo "All tests passed"
+ exit 0
+fi
+
+echo "::error::Some tests failed or replay exited non-zero"
+exit 1
\ No newline at end of file
diff --git a/keploy/.github/workflows/test_workflow_scripts/java-linux.sh b/keploy/.github/workflows/test_workflow_scripts/java-linux.sh
new file mode 100644
index 0000000..f3a36ba
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/java-linux.sh
@@ -0,0 +1,255 @@
+#!/usr/bin/env bash
+# Safe, chatty CI script for Java + Postgres + Keploy with auto API-prefix detection
+
+set -Eeuo pipefail
+
+section() { echo "::group::$*"; }
+endsec() { echo "::endgroup::"; }
+
+die() {
+ rc=$?
+ echo "::error::Pipeline failed (exit=$rc). Dumping context…"
+ echo "== docker ps =="; docker ps || true
+ echo "== postgres logs (complete) =="; docker logs mypostgres || true
+ echo "== workspace tree (depth 3) =="; find . -maxdepth 3 -type d -print | sort || true
+ echo "== keploy tree (depth 4) =="; find ./keploy -maxdepth 4 -type f -print 2>/dev/null | sort || true
+ echo "== *.txt logs (complete) =="; for f in ./*.txt; do [[ -f "$f" ]] && { echo "--- $f ---"; cat "$f"; }; done
+ [[ -f test_logs.txt ]] && { echo "== test_logs.txt (complete) =="; cat test_logs.txt; }
+ exit "$rc"
+}
+trap die ERR
+
+http_code() {
+ # prints HTTP status code or 000 on error
+ curl -s -o /dev/null -w "%{http_code}" "$1" 2>/dev/null || echo 000
+}
+
+wait_for_postgres() {
+ section "Wait for Postgres readiness"
+ for i in {1..120}; do
+ if docker exec mypostgres pg_isready -U petclinic -d petclinic >/dev/null 2>&1; then
+ echo "Postgres is ready."
+ endsec; return 0
+ fi
+ # Fallback probe
+ docker exec mypostgres psql -U petclinic -d petclinic -c "SELECT 1" >/dev/null 2>&1 && { echo "Postgres responded."; endsec; return 0; }
+ sleep 1
+ done
+ echo "::error::Postgres did not become ready in time"
+ endsec; return 1
+}
+
+wait_for_http_port() {
+ # waits for *any* HTTP response from root or actuator (not strict 200)
+ local base="http://localhost:9966"
+ section "Wait for app HTTP port"
+ for i in {1..180}; do
+ if curl -sS "${base}/" -o /dev/null || curl -sS "${base}/actuator/health" -o /dev/null; then
+ echo "HTTP port responded."
+ endsec; return 0
+ fi
+ sleep 1
+ done
+ echo "::error::App did not open HTTP port on 9966"
+ endsec; return 1
+}
+
+detect_api_prefix() {
+ # returns either /petclinic/api or /api (echo to stdout), otherwise empty
+ local base="http://localhost:9966"
+ local candidates=( "/petclinic/api" "/api" )
+ for p in "${candidates[@]}"; do
+ local code
+ code=$(http_code "${base}${p}/pettypes")
+ if [[ "$code" =~ ^(200|201|202|204)$ ]]; then
+ echo "$p"; return 0
+ fi
+ done
+ # If no 2xx, still check which gives *any* non-404 (e.g., 401/403 if security toggled)
+ for p in "${candidates[@]}"; do
+ local code
+ code=$(http_code "${base}${p}/pettypes")
+ if [[ "$code" != "404" && "$code" != "000" ]]; then
+ echo "$p"; return 0
+ fi
+ done
+ # Fallback to actuator presence: assume /api if actuator exists
+ if [[ "$(http_code "${base}/actuator/health")" == "200" ]]; then
+ echo "/api"; return 0
+ fi
+ echo ""
+ return 1
+}
+
+send_request() {
+ local kp_pid="$1"
+ local base="http://localhost:9966"
+
+ wait_for_http_port
+
+ # Try to detect API prefix dynamically
+ local API_PREFIX
+ API_PREFIX=$(detect_api_prefix || true)
+
+ if [[ -z "${API_PREFIX}" ]]; then
+ echo "::warning::Could not auto-detect API prefix. Trying both /petclinic/api and /api."
+ # Try both paths to maximize coverage
+ local paths=( "/petclinic/api" "/api" )
+ for pref in "${paths[@]}"; do
+ curl -sS "${base}${pref}/pettypes" || true
+ curl -sS --request POST \
+ --url "${base}${pref}/pettypes" \
+ --header 'content-type: application/json' \
+ --data '{"name":"John Doe"}' || true
+ curl -sS "${base}${pref}/pettypes" || true
+ curl -sS --request POST \
+ --url "${base}${pref}/pettypes" \
+ --header 'content-type: application/json' \
+ --data '{"name":"Alice Green"}' || true
+ curl -sS "${base}${pref}/pettypes" || true
+ curl -sS --request DELETE "${base}${pref}/pettypes/1" || true
+ curl -sS "${base}${pref}/pettypes" || true
+ done
+ else
+ echo "Detected API prefix: ${API_PREFIX}"
+ echo "good!App started"
+ curl -sS "${base}${API_PREFIX}/pettypes" || true
+
+ curl -sS --request POST \
+ --url "${base}${API_PREFIX}/pettypes" \
+ --header 'content-type: application/json' \
+ --data '{"name":"John Doe"}' || true
+
+ curl -sS "${base}${API_PREFIX}/pettypes" || true
+
+ curl -sS --request POST \
+ --url "${base}${API_PREFIX}/pettypes" \
+ --header 'content-type: application/json' \
+ --data '{"name":"Alice Green"}' || true
+
+ curl -sS "${base}${API_PREFIX}/pettypes" || true
+
+ curl -sS --request DELETE "${base}${API_PREFIX}/pettypes/1" || true
+
+ curl -sS "${base}${API_PREFIX}/pettypes" || true
+ fi
+
+ # Let keploy persist, then stop it
+ sleep 10
+ echo "$kp_pid Keploy PID"
+ echo "Killing keploy"
+ sudo kill "$kp_pid" 2>/dev/null || true
+}
+
+# ----- main -----
+
+source ./../../../.github/workflows/test_workflow_scripts/test-iid.sh
+
+section "Git branch"
+git fetch origin
+git checkout native-linux
+endsec
+
+section "Start Postgres"
+docker run -d --name mypostgres -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic \
+ -e POSTGRES_DB=petclinic -p 5432:5432 postgres:15.2
+wait_for_postgres
+# seed DB
+docker cp ./src/main/resources/db/postgresql/initDB.sql mypostgres:/initDB.sql
+docker exec mypostgres psql -U petclinic -d petclinic -f /initDB.sql
+endsec
+
+section "Java setup"
+source ./../../../.github/workflows/test_workflow_scripts/update-java.sh
+endsec
+
+# Clean once (keep artifacts across iterations)
+sudo rm -rf keploy/
+
+for i in 1 2; do
+ section "Record iteration $i"
+
+ # Build app (captured to log)
+ mvn clean install -Dmaven.test.skip=true | tee -a mvn_build.log
+
+ app_name="javaApp_${i}"
+
+ # Start keploy in background, capture PID
+ sudo -E env PATH="$PATH" "$RECORD_BIN" record \
+ -c 'java -jar target/spring-petclinic-rest-3.0.2.jar' \
+ > "${app_name}.txt" 2>&1 &
+ KEPLOY_PID=$!
+
+ # Drive traffic and stop keploy
+ send_request "$KEPLOY_PID"
+
+ # Wait for keploy exit and capture code
+ set +e
+ wait "$KEPLOY_PID"
+ rc=$?
+ set -e
+ echo "Record exit code: $rc"
+ [[ $rc -ne 0 ]] && echo "::warning::Keploy record exited non-zero (iteration $i)"
+
+ # Quick sanity: ensure something was written
+ echo "== keploy artifacts after record =="
+ find ./keploy -maxdepth 3 -type f | sort || true
+
+ # Surface issues from record logs
+ if grep -q "WARNING: DATA RACE" "${app_name}.txt"; then
+ echo "::error::Data race detected in ${app_name}.txt"
+ cat "${app_name}.txt"
+ exit 1
+ fi
+ if grep -q "ERROR" "${app_name}.txt"; then
+ echo "::warning::Errors found in ${app_name}.txt"
+ cat "${app_name}.txt"
+ fi
+
+ endsec
+ echo "Recorded test case and mocks for iteration ${i}"
+done
+
+section "Shutdown Postgres before test mode"
+# Stop Postgres container - Keploy should use mocks for database interactions
+docker stop mypostgres || true
+docker rm mypostgres || true
+echo "Postgres stopped - Keploy should now use mocks for database interactions"
+endsec
+
+section "Replay"
+set +e
+sudo -E env PATH="$PATH" "$REPLAY_BIN" test \
+ -c 'java -jar target/spring-petclinic-rest-3.0.2.jar' \
+ --delay 20 \
+ > test_logs.txt 2>&1
+REPLAY_RC=$?
+set -e
+echo "Replay exit code: $REPLAY_RC"
+cat test_logs.txt || true
+endsec
+
+section "Check reports"
+RUN_DIR=$(ls -1dt ./keploy/reports/test-run-* 2>/dev/null | head -n1 || true)
+if [[ -z "${RUN_DIR:-}" ]]; then
+ echo "::error::No test-run directory found under ./keploy/reports"
+ [[ $REPLAY_RC -ne 0 ]] && exit "$REPLAY_RC" || exit 1
+fi
+echo "Using reports from: $RUN_DIR"
+
+all_passed=true
+for rpt in "$RUN_DIR"/test-set-*-report.yaml; do
+ [[ -f "$rpt" ]] || continue
+ status=$(awk '/^status:/{print $2; exit}' "$rpt")
+ echo "Test status for $(basename "$rpt"): ${status:-}"
+ [[ "$status" == "PASSED" ]] || all_passed=false
+done
+endsec
+
+if [[ "$all_passed" == "true" && $REPLAY_RC -eq 0 ]]; then
+ echo "All tests passed"
+ exit 0
+fi
+
+echo "::error::Some tests failed or replay exited non-zero"
+exit 1
\ No newline at end of file
diff --git a/keploy/.github/workflows/test_workflow_scripts/node-docker.sh b/keploy/.github/workflows/test_workflow_scripts/node-docker.sh
new file mode 100755
index 0000000..10da181
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/node-docker.sh
@@ -0,0 +1,127 @@
+#!/bin/bash
+
+source ./../../.github/workflows/test_workflow_scripts/test-iid.sh
+
+# Start the docker container.
+docker network create keploy-network
+docker run --name mongoDb --rm --net keploy-network -p 27017:27017 -d mongo
+
+# Remove any preexisting keploy tests.
+sudo rm -rf keploy/
+
+# Build the image of the application.
+docker build -t node-app:1.0 .
+
+container_kill() {
+ pid=$(pgrep -n keploy)
+ echo "$pid Keploy PID"
+ echo "Killing keploy"
+ sudo kill $pid
+}
+
+send_request(){
+ sleep 10
+ # Wait for the application to start.
+ app_started=false
+ while [ "$app_started" = false ]; do
+ if curl -X GET http://localhost:8000/students; then
+ app_started=true
+ fi
+ sleep 3 # wait for 3 seconds before checking again.
+ done
+
+ # Start making curl calls to record the testcases and mocks.
+ curl --request POST \
+ --url http://localhost:8000/students \
+ --header 'content-type: application/json' \
+ --data '{
+ "name":"John Doe",
+ "email":"john@xyz.com",
+ "phone":"0123456799"
+ }'
+
+ curl --request POST \
+ --url http://localhost:8000/students \
+ --header 'content-type: application/json' \
+ --data '{
+ "name":"Alice Green",
+ "email":"green@alice.com",
+ "phone":"3939201584"
+ }'
+
+ curl -X GET http://localhost:8000/students
+ # Wait for 5 seconds for keploy to record the tcs and mocks.
+ sleep 5
+ container_kill
+ wait
+}
+
+for i in {1..2}; do
+ # Start keploy in record mode.
+ container_name="nodeApp_${i}"
+ send_request &
+ sudo -E env PATH=$PATH $RECORD_BIN record -c "docker run -p 8000:8000 --name "${container_name}" --network keploy-network node-app:1.0" --container-name "${container_name}" &> "${container_name}.txt"
+ if grep "ERROR" "${container_name}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${container_name}.txt"
+ exit 1
+ fi
+ if grep "WARNING: DATA RACE" "${container_name}.txt"; then
+ echo "Race condition detected in recording, stopping pipeline..."
+ cat "${container_name}.txt"
+ exit 1
+ fi
+ sleep 5
+
+ echo "Recorded test case and mocks for iteration ${i}"
+done
+
+# Shutdown mongo before test mode - Keploy should use mocks for database interactions
+echo "Shutting down mongo before test mode..."
+docker stop mongoDb || true
+docker rm mongoDb || true
+echo "MongoDB stopped - Keploy should now use mocks for database interactions"
+
+# Start keploy in test mode.
+test_container="nodeApp_test"
+sudo -E env PATH=$PATH $REPLAY_BIN test -c "docker run -p8000:8000 --rm --name $test_container --network keploy-network node-app:1.0" --containerName "$test_container" --apiTimeout 30 --delay 30 --generate-github-actions=false &> "${test_container}.txt"
+if grep "ERROR" "${test_container}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${test_container}.txt"
+ exit 1
+fi
+# Monitor Docker logs for race conditions during testing.
+if grep "WARNING: DATA RACE" "${test_container}.txt"; then
+ echo "Race condition detected in test, stopping pipeline..."
+ cat "${test_container}.txt"
+ exit 1
+fi
+all_passed=true
+
+for i in {0..1}
+do
+ # Define the report file for each test set
+ report_file="./keploy/reports/test-run-0/test-set-$i-report.yaml"
+
+ # Extract the test status
+ test_status=$(grep 'status:' "$report_file" | head -n 1 | awk '{print $2}')
+
+ # Print the status for debugging
+ echo "Test status for test-set-$i: $test_status"
+
+ # Check if any test set did not pass
+ if [ "$test_status" != "PASSED" ]; then
+ all_passed=false
+ echo "Test-set-$i did not pass."
+ break # Exit the loop early as all tests need to pass
+ fi
+done
+
+# Check the overall test status and exit accordingly
+if [ "$all_passed" = true ]; then
+ echo "All tests passed"
+ exit 0
+else
+ cat "${test_container}.txt"
+ exit 1
+fi
\ No newline at end of file
diff --git a/keploy/.github/workflows/test_workflow_scripts/node-encoding.sh b/keploy/.github/workflows/test_workflow_scripts/node-encoding.sh
new file mode 100644
index 0000000..bdaf008
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/node-encoding.sh
@@ -0,0 +1,165 @@
+#!/bin/bash
+
+# Load test scripts and start MongoDB container
+source ./../.github/workflows/test_workflow_scripts/test-iid.sh
+
+# Prepare environment
+npm install
+rm -rf keploy/
+
+# Check if there is a keploy-config file, if there is, delete it.
+if [ -f "./keploy.yml" ]; then
+ rm ./keploy.yml
+fi
+
+echo "Record Bin: $RECORD_BIN"
+echo "Record Version:"
+sudo $RECORD_BIN --version
+echo "Replay Bin: $REPLAY_BIN"
+echo "Replay Version:"
+sudo $REPLAY_BIN --version
+
+# Generate the keploy-config file.
+sudo $RECORD_BIN config --generate
+
+# Update the global noise to ts.
+config_file="./keploy.yml"
+sed -i 's/global: {}/global: {"header": {"Etag":""}}/' "$config_file"
+
+send_request(){
+ node server.js &
+ node_pid=$!
+ sleep 10
+ app_started=false
+ while [ "$app_started" = false ]; do
+ if curl http://localhost:3000/; then
+ app_started=true
+ fi
+ sleep 3 # wait for 3 seconds before checking again.
+ done
+ echo "App started"
+ # Start making curl calls to record the testcases and mocks.
+ curl -v -H "Accept-Encoding: br" -i http://localhost:3000/ --output -
+ curl -v -H "Accept-Encoding: gzip" -i http://localhost:3000/ --output -
+ curl -v -H "Accept-Encoding: br" -i http://localhost:3000/proxy --output -
+ curl -v -H "Accept-Encoding: gzip" -i http://localhost:3000/proxy --output -
+ node request.js &
+ request_pid=$!
+ # Wait for 10 seconds for keploy to record the tcs and mocks.
+ sleep 10
+ echo "Killing request.js"
+ if kill -0 $request_pid 2>/dev/null; then
+ kill $request_pid
+ wait $request_pid 2>/dev/null
+ fi
+ echo "Killing node server.js"
+ kill $node_pid
+ wait $node_pid 2>/dev/null
+ pid=$(pgrep keploy)
+ echo "$pid Keploy PID"
+ echo "Killing keploy"
+ sudo kill $pid
+}
+
+# Record and test sessions in a loop
+for i in {1..2}; do
+ app_name="nodeApp_${i}"
+ send_request &
+ sudo -E env PATH=$PATH $RECORD_BIN record -c 'node app.js' &> "${app_name}.txt"
+ cat "${app_name}.txt"
+ if grep "ERROR" "${app_name}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${app_name}.txt"
+ exit 1
+ fi
+ if grep "WARNING: DATA RACE" "${app_name}.txt"; then
+ echo "Race condition detected in recording, stopping pipeline..."
+ cat "${app_name}.txt"
+ exit 1
+ fi
+ sleep 5
+ wait
+ echo "Recorded test case and mocks for iteration ${i}"
+done
+
+# Test modes and result checking
+sudo -E env PATH=$PATH $REPLAY_BIN test -c 'node app.js' --delay 10 &> test_logs1.txt
+cat test_logs1.txt
+if grep "ERROR" "test_logs1.txt"; then
+ echo "Error found in pipeline..."
+ cat "test_logs1.txt"
+ exit 1
+fi
+if grep "WARNING: DATA RACE" "test_logs1.txt"; then
+ echo "Race condition detected in test, stopping pipeline..."
+ cat "test_logs1.txt"
+ exit 1
+fi
+
+sudo -E env PATH=$PATH $REPLAY_BIN test -c 'node app.js' --delay 10 --testsets test-set-0 &> test_logs2.txt
+cat test_logs2.txt
+if grep "ERROR" "test_logs2.txt"; then
+ echo "Error found in pipeline..."
+ cat "test_logs2.txt"
+ exit 1
+fi
+if grep "WARNING: DATA RACE" "test_logs2.txt"; then
+ echo "Race condition detected in test, stopping pipeline..."
+ cat "test_logs2.txt"
+ exit 1
+fi
+
+sudo -E env PATH=$PATH $REPLAY_BIN test -c 'node app.js' --apiTimeout 30 --delay 10 &> test_logs3.txt
+cat test_logs3.txt
+if grep "ERROR" "test_logs3.txt"; then
+ echo "Error found in pipeline..."
+ cat "test_logs3.txt"
+ exit 1
+fi
+if grep "WARNING: DATA RACE" "test_logs3.txt"; then
+ echo "Race condition detected in test, stopping pipeline..."
+ cat "test_logs3.txt"
+ exit 1
+fi
+
+all_passed=true
+
+for i in {0..2}
+do
+ report_file="./keploy/reports/test-run-$i/test-set-0-report.yaml"
+ # Extract the test status
+ test_status=$(grep 'status:' "$report_file" | head -n 1 | awk '{print $2}')
+
+ # Print the status for debugging
+ echo "Test status for test-set-$i: $test_status"
+
+ # Check if any test set did not pass
+ if [ "$test_status" != "PASSED" ]; then
+ all_passed=false
+ echo "Test-set-$i did not pass."
+ cat "test_logs${i+1}.txt"
+ break # Exit the loop early as all tests need to pass
+ fi
+done
+
+
+# Check the overall test status and exit accordingly
+if [ "$all_passed" = true ]; then
+ report_file="./keploy/reports/test-run-0/test-set-1-report.yaml"
+ test_status=$(grep 'status:' "$report_file" | head -n 1 | awk '{print $2}')
+
+ # Print the status for debugging
+ echo "Test status for test-set-0: $test_status"
+
+ # Check if any test set did not pass
+ if [ "$test_status" != "PASSED" ]; then
+ all_passed=false
+ echo "Test-set-1 did not pass."
+ cat "test_logs1.txt"
+ exit 1
+ fi
+ echo "All tests passed"
+ exit 0
+else
+ exit 1
+fi
diff --git a/keploy/.github/workflows/test_workflow_scripts/node-linux.sh b/keploy/.github/workflows/test_workflow_scripts/node-linux.sh
new file mode 100755
index 0000000..474bcbf
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/node-linux.sh
@@ -0,0 +1,203 @@
+#!/usr/bin/env bash
+# Safe, chatty CI script for Node + Mongo + Keploy
+
+set -Eeuo pipefail
+
+section() { echo "::group::$*"; }
+endsec() { echo "::endgroup::"; }
+
+die() {
+ rc=$?
+ echo "::error::Pipeline failed (exit=$rc). Dumping context…"
+ echo "== docker ps =="; docker ps || true
+ echo "== mongo logs (complete) =="; docker logs mongoDb || true
+ echo "== workspace tree (depth 3) =="; find . -maxdepth 3 -type d -print | sort || true
+ echo "== keploy tree (depth 4) =="; find ./keploy -maxdepth 4 -type f -print 2>/dev/null | sort || true
+ echo "== *.txt logs (complete) =="; for f in ./*.txt; do [[ -f "$f" ]] && { echo "--- $f ---"; cat "$f"; }; done
+ for f in test_logs*.txt; do [[ -f "$f" ]] && { echo "== $f (complete) =="; cat "$f"; }; done
+ exit "$rc"
+}
+trap die ERR
+
+wait_for_mongo() {
+ section "Wait for Mongo readiness"
+ for i in {1..90}; do
+ if docker exec mongoDb mongosh --quiet --eval "db.adminCommand('ping').ok" >/dev/null 2>&1; then
+ echo "Mongo responds to ping."
+ endsec; return 0
+ fi
+ if (echo > /dev/tcp/127.0.0.1/27017) >/dev/null 2>&1; then
+ echo "Mongo TCP port open."
+ endsec; return 0
+ fi
+ sleep 1
+ done
+ echo "::error::Mongo did not become ready in time"
+ endsec; return 1
+}
+
+wait_for_http() {
+ local url="$1" tries="${2:-60}"
+ for _ in $(seq 1 "$tries"); do
+ if curl -fsS "$url" >/dev/null; then return 0; fi
+ sleep 1
+ done
+ return 1
+}
+
+send_request() {
+ local kp_pid="$1"
+
+ if ! wait_for_http "http://localhost:8000/students" 120; then
+ echo "::error::App did not become healthy at /students"
+ else
+ echo "good!App started"
+ fi
+
+ curl -sS --request POST --url http://localhost:8000/students \
+ --header 'content-type: application/json' \
+ --data '{"name":"John Doe","email":"john@xyiz.com","phone":"0123456799"}' || true
+
+ curl -sS --request POST --url http://localhost:8000/students \
+ --header 'content-type: application/json' \
+ --data '{"name":"Alice Green","email":"green@alice.com","phone":"3939201584"}' || true
+
+ curl -sS http://localhost:8000/students || true
+ curl -sS http://localhost:8000/get || true
+
+ sleep 10
+ echo "$kp_pid Keploy PID"
+ echo "Killing keploy"
+ sudo kill "$kp_pid" 2>/dev/null || true
+}
+
+# ----- main -----
+
+# Load test scripts and start MongoDB container
+source ./../../.github/workflows/test_workflow_scripts/test-iid.sh
+
+section "Start Mongo"
+docker run --name mongoDb --rm -p 27017:27017 -d mongo
+wait_for_mongo
+endsec
+
+section "Prepare app"
+npm ci || npm install
+sed -i "s/mongoDb:27017/localhost:27017/" "src/db/connection.js"
+rm -rf keploy/
+[[ -f "./keploy.yml" ]] && rm ./keploy.yml
+
+# Generate the keploy-config file.
+sudo "$RECORD_BIN" config --generate
+
+# Update the global noise to page (ignore changes to this field)
+config_file="./keploy.yml"
+sed -i 's/global: {}/global: {"body": {"page":[]}}/' "$config_file"
+endsec
+
+for i in 1 2; do
+ section "Record iteration $i"
+ app_name="nodeApp_${i}"
+
+ # Start keploy recording in background, capture PID
+ sudo -E env PATH="$PATH" "$RECORD_BIN" record -c 'npm start' \
+ > "${app_name}.txt" 2>&1 &
+ KEPLOY_PID=$!
+
+ # Drive traffic and stop keploy
+ send_request "$KEPLOY_PID"
+
+ # Wait + capture rc
+ set +e
+ wait "$KEPLOY_PID"
+ rc=$?
+ set -e
+ echo "Record exit code: $rc"
+ [[ $rc -ne 0 ]] && echo "::warning::Keploy record exited non-zero (iteration $i)"
+
+ echo "== keploy artifacts (depth 3) =="
+ find ./keploy -maxdepth 3 -type f | sort || true
+
+ if grep -q "WARNING: DATA RACE" "${app_name}.txt"; then
+ echo "::error::Data race detected in ${app_name}.txt"
+ cat "${app_name}.txt"
+ exit 1
+ fi
+ if grep -q "ERROR" "${app_name}.txt"; then
+ echo "::warning::Errors found in ${app_name}.txt"
+ cat "${app_name}.txt"
+ fi
+
+ endsec
+ echo "Recorded test case and mocks for iteration ${i}"
+done
+
+# Optional tweak to a mock; guard if file exists
+mocks_file="keploy/test-set-0/tests/test-5.yaml"
+if [[ -f "$mocks_file" ]]; then
+ sed -i 's/"page":1/"page":4/' "$mocks_file"
+else
+ echo "::warning::$mocks_file not found; skipping page change"
+fi
+
+# ---- Replays ----
+# Shutdown MongoDB before test mode to verify Keploy mocking works correctly
+section "Shutdown MongoDB before test mode"
+docker stop mongoDb || true
+docker rm mongoDb || true
+echo "MongoDB stopped - Keploy should now use mocks for database interactions"
+endsec
+
+run_replay() {
+ local idx="$1"
+ local extra_args="${2:-}"
+ local logfile="test_logs${idx}.txt"
+
+ section "Replay #$idx (args: ${extra_args:-})"
+ set +e
+ sudo -E env PATH="$PATH" "$REPLAY_BIN" test -c 'npm start' --delay 10 $extra_args \
+ > "$logfile" 2>&1
+ local rc=$?
+ set -e
+ echo "Replay #$idx exit code: $rc"
+ cat "$logfile" || true
+
+ # Find newest run dir and print set statuses
+ local RUN_DIR
+ RUN_DIR=$(ls -1dt ./keploy/reports/test-run-* 2>/dev/null | head -n1 || true)
+ if [[ -z "${RUN_DIR:-}" ]]; then
+ echo "::error::No test-run directory found after replay #$idx"
+ return "$rc"
+ fi
+ echo "Using reports from: $RUN_DIR"
+ local any_fail=false
+ for rpt in "$RUN_DIR"/test-set-*-report.yaml; do
+ [[ -f "$rpt" ]] || continue
+ local status
+ status=$(awk '/^status:/{print $2; exit}' "$rpt")
+ echo "Test status for $(basename "$rpt"): ${status:-}"
+ if [[ "$status" != "PASSED" ]]; then any_fail=true; fi
+ done
+ endsec
+
+ if $any_fail; then
+ return 1
+ else
+ return "$rc"
+ fi
+}
+
+run_replay 1
+run_replay 2 "--testsets test-set-0"
+
+# enable selected tests in keploy.yml (guarded)
+if [[ -f "./keploy.yml" ]]; then
+ sed -i 's/selectedTests: {}/selectedTests: {"test-set-0": ["test-1", "test-2"]}/' "./keploy.yml" || true
+else
+ echo "::warning::keploy.yml missing; cannot set selectedTests"
+fi
+
+run_replay 3 "--apiTimeout 30"
+
+echo "All replays completed. If no errors above, CI can PASS."
+exit 0
\ No newline at end of file
diff --git a/keploy/.github/workflows/test_workflow_scripts/python-docker.sh b/keploy/.github/workflows/test_workflow_scripts/python-docker.sh
new file mode 100755
index 0000000..3bc3eea
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/python-docker.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+source ./../../.github/workflows/test_workflow_scripts/test-iid.sh
+
+# Start mongo before starting keploy.
+docker network create keploy-network
+docker run --name mongo --rm --net keploy-network -p 27017:27017 -d mongo
+
+# Set up environment
+rm -rf keploy/ # Clean up old test data
+docker build -t flask-app:1.0 . # Build the Docker image
+
+# Configure keploy
+sed -i 's/global: {}/global: {"header": {"Allow":[]}}/' "./keploy.yml"
+sleep 5 # Allow time for configuration to apply
+
+
+container_kill() {
+ pid=$(pgrep -n keploy)
+ echo "$pid Keploy PID"
+ echo "Killing keploy"
+ sudo kill $pid
+}
+
+send_request(){
+ local container_name=$1
+ sleep 10
+ app_started=false
+ while [ "$app_started" = false ]; do
+ if curl --silent http://localhost:6000/students; then
+ app_started=true
+ else
+ sleep 3 # Check every 3 seconds
+ fi
+ done
+ # Start making curl calls to record the testcases and mocks.
+ curl -X POST -H "Content-Type: application/json" -d '{"student_id": "12345", "name": "John Doe", "age": 20}' http://localhost:6000/students
+ curl -X POST -H "Content-Type: application/json" -d '{"student_id": "12346", "name": "Alice Green", "age": 22}' http://localhost:6000/students
+ curl http://localhost:6000/students
+ curl -X PUT -H "Content-Type: application/json" -d '{"name": "Jane Smith", "age": 21}' http://localhost:6000/students/12345
+ curl http://localhost:6000/students
+ curl -X DELETE http://localhost:6000/students/12345
+
+ # Wait for 5 seconds for keploy to record the tcs and mocks.
+ sleep 5
+ container_kill
+ wait
+}
+
+# Record sessions
+for i in {1..2}; do
+ container_name="flaskApp_${i}"
+ send_request &
+ sudo -E env PATH=$PATH $RECORD_BIN record -c "docker run -p6000:6000 --net keploy-network --rm --name $container_name flask-app:1.0" --container-name "$container_name" &> "${container_name}.txt"
+ if grep "ERROR" "${container_name}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${container_name}.txt"
+ exit 1
+ fi
+ if grep "WARNING: DATA RACE" "${container_name}.txt"; then
+ echo "Race condition detected in recording, stopping pipeline..."
+ cat "${container_name}.txt"
+ exit 1
+ fi
+ sleep 5
+
+ echo "Recorded test case and mocks for iteration ${i}"
+done
+
+# Shutdown mongo before test mode - Keploy should use mocks for database interactions
+echo "Shutting down mongo before test mode..."
+docker stop mongo || true
+docker rm mongo || true
+echo "MongoDB stopped - Keploy should now use mocks for database interactions"
+
+# Testing phase
+test_container="flashApp_test"
+sudo -E env PATH=$PATH $REPLAY_BIN test -c "docker run -p8080:8080 --net keploy-network --name $test_container flask-app:1.0" --containerName "$test_container" --apiTimeout 60 --delay 20 --generate-github-actions=false &> "${test_container}.txt"
+if grep "ERROR" "${test_container}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${test_container}.txt"
+ exit 1
+fi
+if grep "WARNING: DATA RACE" "${test_container}.txt"; then
+ echo "Race condition detected in test, stopping pipeline..."
+ cat "${test_container}.txt"
+ exit 1
+fi
+
+all_passed=true
+
+for i in {0..1}
+do
+ # Define the report file for each test set
+ report_file="./keploy/reports/test-run-0/test-set-$i-report.yaml"
+
+ # Extract the test status
+ test_status=$(grep 'status:' "$report_file" | head -n 1 | awk '{print $2}')
+
+ # Print the status for debugging
+ echo "Test status for test-set-$i: $test_status"
+
+ # Check if any test set did not pass
+ if [ "$test_status" != "PASSED" ]; then
+ all_passed=false
+ echo "Test-set-$i did not pass."
+ break # Exit the loop early as all tests need to pass
+ fi
+done
+
+# Check the overall test status and exit accordingly
+if [ "$all_passed" = true ]; then
+ echo "All tests passed"
+ exit 0
+else
+ cat "${test_container}.txt"
+ exit 1
+fi
\ No newline at end of file
diff --git a/keploy/.github/workflows/test_workflow_scripts/python-linux.sh b/keploy/.github/workflows/test_workflow_scripts/python-linux.sh
new file mode 100644
index 0000000..c064607
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/python-linux.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+
+source ./../../../.github/workflows/test_workflow_scripts/test-iid.sh
+
+# Checkout to the specified branch
+git fetch origin
+git checkout native-linux
+
+# Start the postgres database
+docker compose up -d
+
+# Install dependencies
+pip3 install -r requirements.txt
+
+# Setup environment
+export PYTHON_PATH=./venv/lib/python3.10/site-packages/django
+
+# Database migrations
+python3 manage.py makemigrations
+python3 manage.py migrate
+
+# Configuration and cleanup
+sudo $RECORD_BIN config --generate
+sudo rm -rf keploy/ # Clean old test data
+config_file="./keploy.yml"
+sed -i 's/global: {}/global: {"header": {"Allow":[],}}/' "$config_file"
+sleep 5 # Allow time for configuration changes
+
+send_request(){
+ sleep 10
+ app_started=false
+ while [ "$app_started" = false ]; do
+ if curl -X GET http://127.0.0.1:8000/; then
+ app_started=true
+ fi
+ sleep 3 # wait for 3 seconds before checking again.
+ done
+ echo "App started"
+ # Start making curl calls to record the testcases and mocks.
+ curl --location 'http://127.0.0.1:8000/user/' --header 'Content-Type: application/json' --data-raw '{
+ "name": "Jane Smith",
+ "email": "jane.smith@example.com",
+ "password": "smith567",
+ "website": "www.janesmith.com"
+ }'
+ curl --location 'http://127.0.0.1:8000/user/' --header 'Content-Type: application/json' --data-raw '{
+ "name": "John Doe",
+ "email": "john.doe@example.com",
+ "password": "john567",
+ "website": "www.johndoe.com"
+ }'
+ curl --location 'http://127.0.0.1:8000/user/'
+ # Wait for 10 seconds for keploy to record the tcs and mocks.
+ sleep 10
+ pid=$(pgrep keploy)
+ echo "$pid Keploy PID"
+ echo "Killing keploy"
+ sudo kill $pid
+}
+
+# Record and Test cycles
+for i in {1..2}; do
+ app_name="flaskApp_${i}"
+ send_request &
+ sudo -E env PATH="$PATH" $RECORD_BIN record -c "python3 manage.py runserver" &> "${app_name}.txt"
+ if grep "ERROR" "${app_name}.txt"; then
+ echo "Error found in pipeline..."
+ cat "${app_name}.txt"
+ exit 1
+ fi
+ if grep "WARNING: DATA RACE" "${app_name}.txt"; then
+ echo "Race condition detected in recording, stopping pipeline..."
+ cat "${app_name}.txt"
+ exit 1
+ fi
+ sleep 5
+ wait
+ echo "Recorded test case and mocks for iteration ${i}"
+done
+
+# Shutdown postgres before test mode - Keploy should use mocks for database interactions
+echo "Shutting down postgres before test mode..."
+docker compose down
+echo "Postgres stopped - Keploy should now use mocks for database interactions"
+
+# Testing phase
+sudo -E env PATH="$PATH" $REPLAY_BIN test -c "python3 manage.py runserver" --delay 10 &> test_logs.txt
+
+if grep "ERROR" "test_logs.txt"; then
+ echo "Error found in pipeline..."
+ cat "test_logs.txt"
+ exit 1
+fi
+if grep "WARNING: DATA RACE" "test_logs.txt"; then
+ echo "Race condition detected in test, stopping pipeline..."
+ cat "test_logs.txt"
+ exit 1
+fi
+
+all_passed=true
+
+for i in {0..1}
+do
+ # Define the report file for each test set
+ report_file="./keploy/reports/test-run-0/test-set-$i-report.yaml"
+
+ # Extract the test status
+ test_status=$(grep 'status:' "$report_file" | head -n 1 | awk '{print $2}')
+
+ # Print the status for debugging
+ echo "Test status for test-set-$i: $test_status"
+
+ # Check if any test set did not pass
+ if [ "$test_status" != "PASSED" ]; then
+ all_passed=false
+ echo "Test-set-$i did not pass."
+ break # Exit the loop early as all tests need to pass
+ fi
+done
+
+# Check the overall test status and exit accordingly
+if [ "$all_passed" = true ]; then
+ echo "All tests passed"
+ exit 0
+else
+ cat "test_logs.txt"
+ exit 1
+fi
\ No newline at end of file
diff --git a/keploy/.github/workflows/test_workflow_scripts/test-iid.sh b/keploy/.github/workflows/test_workflow_scripts/test-iid.sh
new file mode 100644
index 0000000..45faacf
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/test-iid.sh
@@ -0,0 +1,4 @@
+# Add fake installation-id for the workflow.
+sudo mkdir ~/.keploy
+sudo touch ~/.keploy/installation-id.yaml
+echo "ObjectID('123456789')" | sudo tee ~/.keploy/installation-id.yaml > /dev/null
\ No newline at end of file
diff --git a/keploy/.github/workflows/test_workflow_scripts/update-docker.sh b/keploy/.github/workflows/test_workflow_scripts/update-docker.sh
new file mode 100644
index 0000000..ca5907d
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/update-docker.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# Define the Dockerfile path
+DOCKERFILE_PATH="./Dockerfile"
+
+# Function to add the -race flag to the go build command in the Dockerfile
+update_dockerfile() {
+ echo "Updating Dockerfile to include the -race flag in the go build command..."
+
+ # Use sed to update the Dockerfile
+ sed -i 's/RUN go build -tags=viper_bind_struct -ldflags="-X main.dsn=$SENTRY_DSN_DOCKER -X main.version=$VERSION" -o keploy ./RUN go build -race -tags=viper_bind_struct -ldflags="-X main.dsn=$SENTRY_DSN_DOCKER -X main.version=$VERSION" -o keploy ./' "$DOCKERFILE_PATH"
+
+ echo "Dockerfile updated successfully."
+}
+
+# Function to build the Docker image
+build_docker_image() {
+ echo "Building Docker image..."
+
+ # Build the Docker image
+ docker image build -t ttl.sh/keploy/keploy:1h .
+
+ if [ $? -eq 0 ]; then
+ echo "Docker image built successfully."
+ else
+ echo "Failed to build Docker image."
+ exit 1
+ fi
+}
+
+# Main function to update the Dockerfile and build the Docker image
+main() {
+ update_dockerfile
+ build_docker_image
+}
+
+# Run the main function
+main
diff --git a/keploy/.github/workflows/test_workflow_scripts/update-java.sh b/keploy/.github/workflows/test_workflow_scripts/update-java.sh
new file mode 100644
index 0000000..f744fbb
--- /dev/null
+++ b/keploy/.github/workflows/test_workflow_scripts/update-java.sh
@@ -0,0 +1,8 @@
+#! /bin/bash
+
+# Update the java version
+sudo apt update
+sudo apt install openjdk-17-jre -y
+export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
+export PATH=$JAVA_HOME/bin:$PATH
+source ~/.bashrc
\ No newline at end of file
diff --git a/keploy/.github/write-good.yml b/keploy/.github/write-good.yml
new file mode 100755
index 0000000..78accbc
--- /dev/null
+++ b/keploy/.github/write-good.yml
@@ -0,0 +1,3 @@
+writeGood: true
+alex: true
+spellchecker: true
\ No newline at end of file
diff --git a/keploy/.gitignore b/keploy/.gitignore
new file mode 100755
index 0000000..c0795f6
--- /dev/null
+++ b/keploy/.gitignore
@@ -0,0 +1,61 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Ignore the server binary output by `go build` but not any folders with the name server
+/server
+!/server/
+
+# Ignore the config and log files
+keploy-config.yaml
+keploy.yml
+keploy-logs.txt
+
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+.deploy.sh
+
+### Go Patch ###
+/vendor/
+/Godeps/
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
+.idea/
+web/public/*
+!web/public/README.md
+!web/public/index.html
+
+
+.vscode
+.env
+ui
+
+coverage.tmp.txt
+coverage.txt
+test-reports
+.DS_Store
+._.DS_Store
+**/.DS_Store
+**/._.DS_Store
+
+#Ignore the zip files
+*.zip
+
+#Ignore the test reports
+test-reports
+
+#Ignore the c and header files
+*.c
+*.h
+
+# Ignore the debug_bin
+__debug_bin*
+keploy
\ No newline at end of file
diff --git a/keploy/.golangci.yml b/keploy/.golangci.yml
new file mode 100644
index 0000000..88b3898
--- /dev/null
+++ b/keploy/.golangci.yml
@@ -0,0 +1,24 @@
+# This is the configuration for golangci-lint for the restic project.
+#
+# A sample config with all settings is here:
+# https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml
+version: "2"
+
+linters:
+ enable:
+ - govet
+ - staticcheck
+ - errcheck
+ - ineffassign
+ - unused
+ exclusions:
+ rules: []
+ paths-except:
+ - pkg/core/hooks/bpf_arm64_bpfel.go
+ - pkg/core/hooks/bpf_x86_bpfel.go
+ - pkg/service/utgen
+
+formatters:
+ enable:
+ - gofmt
+ - goimports
\ No newline at end of file
diff --git a/keploy/.pre-commit-config.yaml b/keploy/.pre-commit-config.yaml
new file mode 100755
index 0000000..03cb10b
--- /dev/null
+++ b/keploy/.pre-commit-config.yaml
@@ -0,0 +1,7 @@
+repos:
+- hooks:
+ - id: commitizen
+ stages:
+ - commit-msg
+ repo: https://github.com/commitizen-tools/commitizen
+ rev: v2.21.2
diff --git a/keploy/.releaserc.json b/keploy/.releaserc.json
new file mode 100755
index 0000000..eac6d3c
--- /dev/null
+++ b/keploy/.releaserc.json
@@ -0,0 +1,13 @@
+{
+ "branches": ["main"],
+ "repositoryUrl": "git@github.com:keploy/keploy.git",
+ "plugins": [
+ "@semantic-release/commit-analyzer",
+ "@semantic-release/release-notes-generator",
+ "@semantic-release/github",
+ ["@semantic-release/exec", {
+ "prepareCmd" : "set-version ${nextRelease.version}",
+ "publishCmd" : "publish-package"
+ }]
+ ]
+}
\ No newline at end of file
diff --git a/keploy/CITATION.cff b/keploy/CITATION.cff
new file mode 100755
index 0000000..0d5b322
--- /dev/null
+++ b/keploy/CITATION.cff
@@ -0,0 +1,7 @@
+cff-version: 1.2.0
+title: 'Keploy: No code API testing platform. Create unit tests and data mocks from API calls.'
+message: 'To cite Keploy in publications use:'
+type: software
+author: Keploy Inc
+repository-code: 'https://github.com/keploy/keploy'
+license: Apache License 2.0
\ No newline at end of file
diff --git a/keploy/CODE_OF_CONDUCT.md b/keploy/CODE_OF_CONDUCT.md
new file mode 100755
index 0000000..8b5b1bc
--- /dev/null
+++ b/keploy/CODE_OF_CONDUCT.md
@@ -0,0 +1,126 @@
+# Contributor Code of Conduct for Keploy
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Expected Behavior
+
+The following behaviors are expected and requested of all community members:
+
+ * Act authentically and participate actively in the community to help maintain a positive and productive environment.
+ * Show consideration and respect in all your actions and speech. Avoid any behavior that is demeaning, discriminatory, or harassing.
+ * Seek collaboration as an initial step instead of conflict.
+ * Refrain from demeaning, discriminatory, or harassing behavior and speech.
+ * Report any unsafe situations, distress or violations of the code of conduct to the maintainers through [Slack](https://join.slack.com/t/keploy/shared_invite/zt-357qqm9b5-PbZRVu3Yt2rJIa6ofrwWNg).
+* Practice empathy and kindness towards other community members.
+* Respect diverse opinions, perspectives, and experiences.
+* Give and receive constructive feedback in a gracious manner.
+* Take responsibility for your actions and apologize for mistakes. Use them as learning opportunities.
+* Prioritize the well-being and success of the community as a whole over individual gain.
+
+Examples of unacceptable behavior include:
+
+* Violence, threats of violence or any language that incites violence towards any individual or group is prohibited.
+ * Discriminatory jokes and language, such as those based on gender, race, sexual orientation, religion, ability, or any other characteristic, is strictly forbidden.
+ * Displaying or sharing sexually explicit or violent content is prohibited.
+ * Any form of harassment, including but not limited to "doxing" (posting or threatening to post other people's personally identifying information) is prohibited.
+ * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
+* Publishing any personal information of others without their explicit consent is strictly forbidden.
+* Using sexualized language or imagery, or making any sexual advances towards another person is prohibited.
+* Harassment, whether it be public or private, will not be tolerated.
+
+## Enforcement Responsibilities
+
+Organizations' maintainers are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+They have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces related to Keploy, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+support@keploy.io.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Contact info
+
+* [Slack](https://join.slack.com/t/keploy/shared_invite/zt-357qqm9b5-PbZRVu3Yt2rJIa6ofrwWNg)
+* [Mail](hello@keploy.io)
+
+## Support 🙏
+
+This project needs a ⭐️ from you. Don't forget to leave a star ⭐️
+
+## Happy Contributions !!
diff --git a/keploy/CONTRIBUTING.md b/keploy/CONTRIBUTING.md
new file mode 100755
index 0000000..e30112d
--- /dev/null
+++ b/keploy/CONTRIBUTING.md
@@ -0,0 +1,122 @@
+# Contributing to Keploy
+
+Thank you for your interest in Keploy and for taking the time to contribute to this project. 🙌 Keploy is a project by developers for developers and there are a lot of ways you can contribute.
+
+If you don't know where to start contributing, ask us on our [Slack channel](https://join.slack.com/t/keploy/shared_invite/zt-357qqm9b5-PbZRVu3Yt2rJIa6ofrwWNg).
+
+## Code of conduct
+
+Contributors are expected to adhere to the [Code of Conduct](CODE_OF_CONDUCT.md).
+
+## Prerequisites for the contributors
+
+Contributors should have knowledge of git, go, and markdown for most projects since the project work heavily depends on them.
+We encourage Contributors to set up Keploy for local development and play around with the code and tests to get more comfortable with the project.
+
+Sections
+
+- General Contribution Flow
+ - Developer Certificate of Origin
+- Keploy Contribution Flow
+ - Keploy Server
+ - Keploy Documentation
+
+# General Contribution Flow
+
+## Signing-off on Commits (Developer Certificate of Origin)
+
+To contribute to this project, you must agree to the Developer Certificate of
+Origin (DCO) for each commit you make. The DCO is a simple statement that you,
+as a contributor, have the legal right to make the contribution.
+
+See the [DCO](https://developercertificate.org) file for the full text of what you must agree to
+and how it works [here](https://github.com/probot/dco#how-it-works).
+To signify that you agree to the DCO for contributions, you simply add a line to each of your
+git commit messages:
+
+```
+Signed-off-by: Jane Smith
+```
+
+In most cases, you can add this signoff to your commit automatically with the
+`-s` or `--signoff` flag to `git commit`. You must use your real name and a reachable email
+address (sorry, no pseudonyms or anonymous contributions). An example of signing off on a commit:
+
+```
+$ commit -s -m “my commit message w/signoff”
+```
+
+To ensure all your commits are signed, you may choose to add this alias to your global `.gitconfig`:
+
+_~/.gitconfig_
+
+```
+[alias]
+ amend = commit -s --amend
+ cm = commit -s -m
+ commit = commit -s
+```
+
+# How to contribute ?
+
+We encourage contributions from the community.
+
+**Create a [GitHub issue](https://github.com/keploy/keploy/issues) for any changes beyond typos and small fixes.**
+
+We review GitHub issues and PRs on a regular schedule.
+
+To ensure that each change is relevant and properly peer reviewed, please adhere to best practices for open-source contributions.
+This means that if you are outside the Keploy organization, you must fork the repository and create PRs from branches on your own fork.
+The README in GitHub's [first-contributions repo](https://github.com/firstcontributions/first-contributions) provides an example.
+
+## ## How to set up the docs website locally?
+
+1. Fork the repository
+
+
+
+2. Clone the repository with the following command. Replace the with your username
+
+```sh
+git clone https://github.com//keploy.git
+```
+
+
+
+3. Go into the directory containing the project and edit the changes.
+
+
+When we merge your PR, a new build automatically occurs and your changes publish to [https://keploy.io](https://github.com/keploy/keploy).
+
+## Keploy Contribution Flow
+
+Keploy is written in `Go` (Golang) and leverages Go Modules. Relevant coding style guidelines are the [Go Code Review Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) and the _Formatting and style_ section of Peter Bourgon's [Go: Best
+Practices for Production Environments](https://peter.bourgon.org/go-in-production/#formatting-and-style).
+
+There are many ways in which you can contribute to Keploy.
+
+### Keploy Server
+
+#### Report a Bug
+Report all issues through GitHub Issues using the [Report a Bug](https://github.com/keploy/keploy/issues/new?assignees=&labels=&template=bug_report.md&title=) template.
+To help resolve your issue as quickly as possible, read the template and provide all the requested information.
+
+#### Feature request
+We welcome all feature requests, whether it's to add new functionality to an existing extension or to offer an idea for a brand new extension.
+File your feature request through GitHub Issues using the [Feature Request](https://github.com/keploy/keploy/issues/new?assignees=&labels=&template=feature_request.md&title=) template.
+
+#### Close a Bug
+We welcome contributions that help make keploy bug free & improve the experience of our users. You can also find issues tagged [Good First Issues](https://github.com/keploy/keploy/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
+
+### Keploy Documentation
+
+The Keploy documentation site uses Docusaurus 2, which is a static website generator, you can make changes locally without previewing them in the browser.
+
+In the process of shipping features quickly, we may forget to keep our docs up to date. You can help by suggesting improvements to our documentation using the [Documentation Improvement](https://github.com/keploy/docs/issues) template.
+
+Please refer to [Keploy Docs Contributing Guide](https://github.com/keploy/docs/blob/main/CONTRIBUTING.md#-how-to-set-up-the-docs-website-locally) for setting up your development environment and the follow [Keploy Style Guide](https://github.com/keploy/docs/blob/main/STYLE.md).
+
+
+# Contact
+
+Feel free to join [slack](https://join.slack.com/t/keploy/shared_invite/zt-357qqm9b5-PbZRVu3Yt2rJIa6ofrwWNg) to start a conversation with us.
diff --git a/keploy/DEBUG.md b/keploy/DEBUG.md
new file mode 100644
index 0000000..210ef44
--- /dev/null
+++ b/keploy/DEBUG.md
@@ -0,0 +1,79 @@
+# 🐰 Keploy Debugging Guide:
+
+## 1) Capturing Stack Traces with `SIGQUIT`
+
+If Keploy appears stuck or unresponsive, and you want a quick insight into what it's doing (e.g., which goroutines are running), you can send a `SIGQUIT` signal. This is a fast alternative to setting up `pprof` and will make the Go runtime dump a full stack trace to `stderr`.
+
+---
+
+## 🔧 **When Keploy is Running Natively (on the Host)**
+
+### 1. **Find the PID of the Keploy process**
+
+You can use the proxy port Keploy listens on (`16789` by default) to locate the process:
+
+```bash
+sudo lsof -i:16789
+```
+Example output:
+
+```bash
+keploy 12345 root ... TCP *:16789 ...
+```
+
+In this case, 12345 is the PID of the keploy process.
+
+### 2. Send a SIGQUIT signal to the Keploy process
+```bash
+sudo kill -SIGQUIT 12345
+```
+
+The Go runtime will then print a full goroutine stack trace to the terminal or to wherever your logs are configured to go.
+
+
+
+## 🐳 When Keploy is Running in Docker (with --pid=host)
+
+If Keploy is running in a Docker container with the --pid=host flag, docker kill --signal=SIGQUIT won't work because Keploy is not PID 1 inside the container. You'll need to send the signal to the actual host PID.
+
+### 1. Find the host PID of the /app/keploy process
+
+Use docker top to inspect the container's process tree:
+
+```bash
+docker top keploy-v2
+```
+
+Look for a line where the command starts with /app/keploy:
+```bash
+UID PID PPID C STIME TTY TIME CMD
+root 341003 ... ... ... ... ... /app/keploy record -c ...
+```
+In this case, 341003 is the host PID of the running Go binary inside the container.
+
+### 2. Send SIGQUIT to that PID from the host
+
+```bash
+sudo kill -SIGQUIT 341003
+```
+
+This will cause the Go runtime to emit a full stack trace from the running keploy process.
+
+You should see a stack trace similar to:
+```bash
+SIGQUIT: quit
+goroutine 1 [running]:
+main.main()
+ /app/main.go:42 +0x123
+...
+```
+
+⸻
+
+🧠 Notes
+ • This is a quick way to inspect a hung or slow keploy run without setting up full profiling.
+ • Make sure the Go binary is not built with -ldflags="-s -w" — that strips debug symbols, making stack traces useless.
+ • Do not intercept SIGQUIT in your code using signal.Notify(..., syscall.SIGQUIT) — it prevents the Go runtime from printing the trace.
+ • When using --pid=host, ensure you signal the actual host PID, not via docker kill.
+
+⸻
\ No newline at end of file
diff --git a/keploy/Dockerfile b/keploy/Dockerfile
new file mode 100755
index 0000000..eff8195
--- /dev/null
+++ b/keploy/Dockerfile
@@ -0,0 +1,57 @@
+# === Build Stage ===
+FROM golang:1.24 AS build
+
+# Set the working directory
+WORKDIR /app
+
+# Define build arguments for ldflags
+ARG SENTRY_DSN_DOCKER
+ARG VERSION
+ARG SERVER_URL
+
+# Copy the Go module files and download dependencies
+COPY go.mod go.sum /app/
+RUN go mod download
+
+# Copy the contents of the current directory into the build container
+COPY . /app
+
+# Build the keploy binary
+# setting GOMAXPROCS to avoid crashing qemu while building different arch with docker buildx
+# ref - https://github.com/golang/go/issues/70329#issuecomment-2559049444
+RUN GOMAXPROCS=2 go build -tags=viper_bind_struct -ldflags="-X main.dsn=$SENTRY_DSN_DOCKER -X main.version=$VERSION -X main.apiServerURI=$SERVER_URL -X main.gitHubClientID=$GITTHUB_APP_CLIENT_ID" -o keploy .
+
+# === Runtime Stage ===
+FROM debian:bookworm-slim
+
+ENV KEPLOY_INDOCKER=true
+
+# Update the package lists and install required packages
+RUN apt-get update
+RUN apt-get install -y ca-certificates curl sudo && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# Install Docker engine
+RUN curl -fsSL https://get.docker.com -o get-docker.sh && \
+ sh get-docker.sh && \
+ rm get-docker.sh
+
+# Install docker-compose to PATH
+# Install specific version of Docker Compose plugin (v2.29.1)
+RUN mkdir -p /usr/lib/docker/cli-plugins && \
+ curl -SL "https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-linux-$(uname -m)" -o /usr/lib/docker/cli-plugins/docker-compose && \
+ chmod +x /usr/lib/docker/cli-plugins/docker-compose
+
+# Copy the keploy binary and the entrypoint script from the build container
+COPY --from=build /app/keploy /app/keploy
+COPY --from=build /app/entrypoint.sh /app/entrypoint.sh
+
+# windows comapatibility
+RUN sed -i 's/\r$//' /app/entrypoint.sh
+
+# Make the entrypoint.sh file executable
+RUN chmod +x /app/entrypoint.sh
+
+# Set the entrypoint
+ENTRYPOINT ["/app/entrypoint.sh", "/app/keploy"]
diff --git a/keploy/HACKTOBERFEST_GUIDE.md b/keploy/HACKTOBERFEST_GUIDE.md
new file mode 100644
index 0000000..6311476
--- /dev/null
+++ b/keploy/HACKTOBERFEST_GUIDE.md
@@ -0,0 +1,82 @@
+
+
+
Celebrate
+Open Source with Hacktoberfest 2023
+
+
+
+ 𝑶𝒏𝒆 𝒄𝒐𝒏𝒕𝒓𝒊𝒃𝒖𝒕𝒊𝒐𝒏 𝒂𝒕 𝒂 𝒕𝒊𝒎𝒆
+
+
+
+---
+
+
+## Our Journey with Hacktoberfest ❤️
+
+[Hacktoberfest](https://hacktoberfest.com/) is an initiative that matters very deeply to us. We launched the first iteration of Keploy as a mere open-source project in December 2021. Hacktoberfest 2022 was truly a game-changer for us, as we saw over 200 contributions from some lovely members of the open-source community that October.
+
+There are many different ways you can contribute to [Keploy](https://keploy.io). If you’ve ever wanted to contribute to open-source now is your chance!
+
+All backgrounds and skill levels are encouraged to participate. [Learn How to Contribute?](https://opensource.guide/how-to-contribute)
+
+# About Keploy
+
+Keploy is a next-gen E2E testing tool that provides an easy way to capture and generate tests(KTests) and data-mocks(KMocks) from real API calls. It automatically generates mocks and stubs, making the testing process simpler and more efficient.
+
+**- Automatically Mocks Dependencies**
+**- Zero Code Change Integration**
+**- Language-Agnostic Support**
+**- Native Docker/Kubernetes Support**
+**- Asynchronous Processes Support**
+
+
+
+[ ⭐ Star and try Out Keploy ➜ ](https://keploy.io)
+
+
+
+___
+
+### Learn more about projects and contributing
+
+👨🏻💻 **Code Contribubtion**
+
+Code contributions are a great way to get involved in supporting open source, and learn new skills. Here are some examples of ways you can contribute to open-source projects:
+
+- 👉 Bug fixes :- If you'd like to break and build software, fix a current issue reported by the community and be a hero!
+
+- 👉 Implement features :- You can choose from a carefully curated selection of Hacktoberfest requests that have been made by community members.
+
+- 👉 Build a demo app :- Build a demo app with Keploy and share it with the community.
+
+
+📝 **Low Code and No-Code Contribution**
+
+You can choose from a carefully curated selection of Hacktoberfest requests that have been made by community members.
+
+- Technical documentation
+- User experience testing
+- Case studies
+- Technical blog post or tutorial
+- Translating to Other Languages
+- Give Talks or presentations
+- Organize event with community
+
+# Community Support
+
+The open source community needs you. Do you have what it takes to join the community and build a better future? We’re here to help you.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/keploy/LICENSE b/keploy/LICENSE
new file mode 100755
index 0000000..c20e7bd
--- /dev/null
+++ b/keploy/LICENSE
@@ -0,0 +1,201 @@
+ 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 2022 Keploy Inc
+
+ 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.
diff --git a/keploy/README-UnitGen.md b/keploy/README-UnitGen.md
new file mode 100644
index 0000000..ff86a43
--- /dev/null
+++ b/keploy/README-UnitGen.md
@@ -0,0 +1,278 @@
+
+
+
+
+
+⚡️ Generate unit tests with LLMs, that actually works ⚡️
+
+
+
+🌟 The must-have tool for developers in the AI-Gen era 🌟
+
+
+---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[](https://join.slack.com/t/keploy/shared_invite/zt-357qqm9b5-PbZRVu3Yt2rJIa6ofrwWNg)
+[](https://www.linkedin.com/company/keploy/)
+[](https://x.com/keployio)
+
+
+
+---
+
+Keploy-gen uses LLMs to understand code semantics and generates meaningful **unit tests**. It's inspired by the [Automated Unit Test Improvement using LLM at Meta](https://arxiv.org/pdf/2402.09171).
+
+### Objectives
+
+- **Automate unit test generation (UTG)**: Quickly generate comprehensive unit tests and reduce the redundant manual effort.
+
+- **Improve edge cases**: Extend and improve the scope of tests to cover more complex scenarios that are often missed manually.
+
+- **Boost test coverage**: As codebase grows, ensuring exhaustive coverage should become feasible.
+
+## Core Components
+
+| **Phase** | **Activities** | **Tools/Technologies** |
+| ----------------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------- |
+| **Code Analysis** | Analyze the code structure and dependencies. | Static analysis tools, LLMs |
+| **Prompt Engineering** | Generation of targeted prompts to guide the LLM in producing relevant tests. | LLMs, Custom scripts |
+| **Iterative Test Refinement** | Cyclic process of refining tests by running them, analyzing coverage, and incorporating feedback. | Testing frameworks (e.g., JUnit, pytest) |
+
+### Process Overview
+
+Referred from [Meta's research](https://arxiv.org/pdf/2402.09171), TestGen-LLM top level architecture.
+
+
+
+## Prerequisites
+
+**AI model Setup** - Set the environment variable **API_KEY**.
+```
+export API_KEY=xxxx
+```
+
+**API_KEY** can be from either of one these:
+- **OpenAI's GPT-4o** directly **[preferred]**.
+
+- Alternative LLMs via [litellm](https://github.com/BerriAI/litellm?tab=readme-ov-file#quick-start-proxy---cli).
+
+- Azure OpenAI
+
+## Installation
+
+Install Keploy locally by running the following command:
+
+#### ➡ Linux/Mac
+
+```shell
+ curl --silent -O -L https://keploy.io/install.sh && source install.sh
+```
+
+#### ➡ Windows
+
+- [Download](https://github.com/keploy/keploy/releases/latest/download/keploy_windows_amd64.tar.gz) and **move the keploy.exe file** to `C:\Windows\System32`
+
+###  ➡ Running with Node.js/TypeScript applications
+
+- Ensure you've set the API key, as mentioned in pre-requisites above:
+
+ ```shell
+ export API_KEY=xxxx
+ ```
+
+- Ensure **Cobertura** formatted coverage reports, edit `jest.config.js` or `package.json`:
+
+
+ ```json
+ // package.json
+ "jest": {
+ "coverageReporters": ["text", "cobertura"],
+ }
+ ```
+ or
+
+ ```javascript
+ // jest.config.js
+ module.exports = {
+ coverageReporters: ["text", "cobertura"],
+ };
+ ```
+
+#### Generating Unit Tests
+
+- Run the following command in the root of your application.
+
+
+ - **For Single Test File:** If you prefer to test a smaller section of your application or to control costs, consider generating tests for a single source and its corresponding test file:
+
+ ```shell
+ keploy gen --sourceFilePath="
" --testFilePath="" --testCommand="npm test" --coverageReportPath=""
+ ```
+
+
+
+ - **For Entire Application** use the following command to generate tests across:
+
+ ⚠️ **Warning:** Executing this command will generate unit tests for all files in the application. Depending on the size of the codebase, this process may take between 20 minutes to an hour and will incur costs related to LLM usage.
+
+ ```bash
+ keploy gen --testCommand="npm test" --testDir="test" --coverageReportPath=""
+ ```
+
+ 🎉 You should see improved test cases and code-coverage. ✅ Enjoy coding with enhanced unit test coverage! 🫰
+
+###  → Running with Golang applications
+
+- Ensure you've set the API key, as mentioned in pre-requisites above:
+
+ ```shell
+ export API_KEY=xxxx
+ ```
+
+- To ensure **Cobertura** formatted coverage reports, add:
+ ```bash
+ go install github.com/axw/gocov/gocov@v1.1.0
+ go install github.com/AlekSi/gocov-xml@v1.1.0
+ ```
+#### Generating Unit Tests
+- Run the following command in the root of your application.
+
+
+ - **For Single Test File:** If you prefer to test a smaller section of your application or to control costs, consider generating tests for a single source and its corresponding test file:
+
+ ```shell
+ keploy gen --sourceFilePath="" --testFilePath="" --testCommand="go test -v ./... -coverprofile=coverage.out && gocov convert coverage.out | gocov-xml > coverage.xml" --coverageReportPath=""
+ ```
+
+
+
+ - **For Entire Application** use the following command to generate tests across:
+
+ ⚠️ **Warning:** Executing this command will generate unit tests for all files in the application. Depending on the size of the codebase, this process may take between 20 minutes to an hour and will incur costs related to LLM usage.
+
+ ```bash
+ keploy gen --testDir="." --testCommand="go test -v ./... -coverprofile=coverage.out && gocov convert coverage.out | gocov-xml > coverage.xml" --coverageReportPath=""
+ ```
+
+ 🎉 You should see improved test cases and code-coverage. ✅ Enjoy coding with enhanced unit test coverage! 🫰
+
+### → Setup for Other Languages
+
+- Ensure you've set the API key, as mentioned in pre-requisites above:
+
+ ```shell
+ export API_KEY=xxxx
+ ```
+
+- Ensure that your unit test report format is **Cobertura**(it's very common).
+- Generate tests using keploy-gen:
+ ```bash
+ keploy gen --sourceFilePath="" --testFilePath="" --testCommand="" --coverageReportPath=""
+ ```
+
+## Configuration
+
+Configure Keploy using command-line flags:
+
+```bash
+
+ --sourceFilePath ""
+ --testFilePath ""
+ --coverageReportPath "coverage.xml"
+ --testCommand ""
+ --coverageFormat "cobertura"
+ --expectedCoverage 100
+ --maxIterations 5
+ --testDir ""
+ --llmBaseUrl "https://api.openai.com/v1"
+ --model "gpt-4o"
+ --llmApiVersion "
+```
+
+- `sourceFilePath`: Path to the source file for which tests are to be generated.
+- `testFilePath`: Path where the generated tests will be saved.
+- `coverageReportPath`: Path to generate the coverage report.
+- `testCommand` (required): Command to execute tests and generate the coverage report.
+- `coverageFormat`: Type of the coverage report (default "cobertura").
+- `expectedCoverage`: Desired coverage percentage (default 100%).
+- `maxIterations`: Maximum number of iterations for refining tests (default 5).
+- `testDir`: Directory where tests will be written.
+- `llmBaseUrl`: Base url of the llm.
+- `model`: Specifies the AI model to use (default "gpt-4o").
+- `llmApiVersion`: API version of the llm if any (default "")
+
+# Frequently Asked Questions
+
+1. What is Keploy's Unit Test Generator (UTG)?
+ - Keploy's UTG automates the creation of unit tests based on code semantics, enhancing test coverage and reliability.
+
+2. Does Keploy send your private data to any cloud server for test generation?
+ - No, Keploy does not send any user code to remote systems, except when using the unit test generation feature. When using the UT gen feature, only the source code and the unit test code will be sent to the Large Language Model (LLM) you are using. By default, Keploy uses - litellm to support vast number of LLM backends. Yes, if your organization has its own LLM(a private one), you can use it with Keploy. This ensures that data is not sent to any external systems.
+
+3. How does Keploy contribute to improving unit test coverage?
+ - By providing a zero code platform for automated testing, Keploy empowers developers to scale up their unit test coverage without extensive coding knowledge. This integration enhances testing reports, ultimately boosting confidence in the product's quality.
+
+4. Is Keploy cost-effective for automated unit testing?
+ - Yes, Keploy optimizes costs by automating repetitive testing tasks and improving overall test efficiency.
+
+5. How does Keploy generate coverage reports?
+ - Keploy generates detailed Cobertura format reports, offering insights into test effectiveness and code quality.
+
+6. Can Keploy handle large codebases efficiently?
+ - Yes, Keploy is designed to handle large codebases efficiently, though processing time may vary based on project size and complexity.
+
+# 🙋🏻♀️ Questions? 🙋🏻♂️
+
+Reach out to us. We're here to answer!
+
+[](https://join.slack.com/t/keploy/shared_invite/zt-357qqm9b5-PbZRVu3Yt2rJIa6ofrwWNg)
+[](https://www.linkedin.com/company/keploy/)
+[](https://www.youtube.com/channel/UC6OTg7F4o0WkmNtSoob34lg)
+[](https://x.com/Keployio)
+
+
+# 📝 Sample QuickStarts
+-  : Try a unit-gen on [Mux-SQL](https://github.com/keploy/samples-go/tree/main/mux-sql#create-unit-testcase-with-keploy) app
+
+-  : Try a unit-gen on [Express-Mongoose](https://github.com/keploy/samples-typescript/tree/main/express-mongoose#create-unit-testcase-with-keploy) app
+
+## 🌐 Language Support
+
+
+
+
+Other language may be supported, we've not tested them yet. If your **coverage reports** are of **Cobertura format** then you should be able to use keploy-gen in any language.
+
+## Dev Support
+
+Keploy-gen is not just a project but an attempt to make developers life easier testing applications.
+It aims to simplify the creation and maintenance of tests, ensuring high coverage, and adapts to the complexity of modern software development.
+
+#### Prompt Generation
+
+Referred from [Meta's research](https://arxiv.org/pdf/2402.09171), the four primary prompts used in the deployment for the December 2023 Instagram and Facebook app test-a-thons
+
+| Prompt Name | Template |
+| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| extend_test | Here is a Kotlin unit test class: {`existing_test_class`}. Write an extended version of the test class that includes additional tests to cover some extra corner cases. |
+| extend_coverage | Here is a Kotlin unit test class and the class that it tests: {`existing_test_class`} {`class_under_test`}. Write an extended version of the test class that includes additional unit tests that will increase the test coverage of the class under test. |
+| corner_cases | Here is a Kotlin unit test class and the class that it tests: {`existing_test_class`} {`class_under_test`}. Write an extended version of the test class that includes additional unit tests that will cover corner cases missed by the original and will increase the test coverage of the class under test. |
+| statement_to_complete | Here is a Kotlin class under test {`class_under_test`} This class under test can be tested with this Kotlin unit test class {`existing_test_class`}. Here is an extended version of the unit test class that includes additional unit test cases that will cover methods, edge cases, corner cases, and other features of the class under test that were missed by the original unit test class: |
+
+Limitation: This project currently doesn't generate quality fresh tests if there are no existing tests to learn from.
+
+Enjoy coding with enhanced unit test coverage! 🫰
diff --git a/keploy/README.md b/keploy/README.md
new file mode 100755
index 0000000..ea0d5b6
--- /dev/null
+++ b/keploy/README.md
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+⚡️ API tests faster than unit tests, from user traffic ⚡️
+
+
+
+🌟 The must-have tool for developers in the AI-Gen era 🌟
+
+
+---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[](https://join.slack.com/t/keploy/shared_invite/zt-357qqm9b5-PbZRVu3Yt2rJIa6ofrwWNg)
+[](https://www.linkedin.com/company/keploy/)
+[](https://www.youtube.com/channel/UC6OTg7F4o0WkmNtSoob34lg)
+[](https://x.com/Keployio)
+
+
+
+
+
+[Keploy](https://keploy.io) is **developer-centric** API testing tool that creates **tests along with built-in-mocks**, faster than unit tests.
+
+Keploy not only records API calls, but also records database calls and replays them during testing, making it **easy to use, powerful, and extensible**.
+
+
+
+> 🐰 **Fun fact:** Keploy uses itself for testing! Check out our swanky coverage badge: [](https://coveralls.io/github/keploy/keploy?branch=main&kill_cache=1)
+
+## 🚨 Here for [Unit Test Generator](README-UnitGen.md) (ut-gen)?
+Keploy has newly launched the world's first unit test generator(ut-gen) implementation of [Meta LLM research paper](https://arxiv.org/pdf/2402.09171), it understands code semantics and generates meaningful unit tests, aiming to:
+
+- **Automate unit test generation (UTG)**: Quickly generate comprehensive unit tests and reduce redundant manual effort.
+
+- **Improve edge cases**: Extend and improve the scope of automated tests to cover more complex scenarios, often missed manually.
+
+- **Boost test coverage**: As codebases grow, ensuring exhaustive coverage should become feasible, aligning with our mission.
+
+### 📜 Follow [Unit Test Generator README](README-UnitGen.md)! ✅
+
+## 📘 Documentation!
+Become a Keploy pro with **[Keploy Documentation](https://keploy.io/docs/)**.
+
+
+
+# 🚀 Quick Installation (API test generator)
+
+Integrate Keploy by installing the agent locally. No code-changes required.
+
+```shell
+curl --silent -O -L https://keploy.io/install.sh && source install.sh
+```
+
+## 🎬 Recording Testcases
+
+Start your app with Keploy to convert API calls as Tests and Mocks/Stubs.
+
+```zsh
+keploy record -c "CMD_TO_RUN_APP"
+```
+For example, if you're using a simple Python app the `CMD_TO_RUN_APP` would resemble to `python main.py`, for Golang `go run main.go`, for java `java -jar xyz.jar`, for node `npm start`..
+
+```zsh
+keploy record -c "python main.py"
+```
+
+## 🧪 Running Tests
+Shut down the databases, redis, kafka or any other services your application uses. Keploy doesn't need those during test.
+```zsh
+keploy test -c "CMD_TO_RUN_APP" --delay 10
+```
+
+## ✅ Test Coverage Integration
+To integrate with your unit-testing library and see combine test coverage, follow this [test-coverage guide](https://keploy.io/docs/server/sdk-installation/go/).
+
+> #### **If You Had Fun:** Please leave a 🌟 star on this repo! It's free and will bring a smile. 😄 👏
+
+## One-Click Setup 🚀
+
+Setup and run keploy quickly, with no local machine installation required:
+
+[]([https://github.dev/Sonichigo/mux-sql](https://github.dev/Sonichigo/mux-sql))
+
+## 🤔 Questions?
+Reach out to us. We're here to help!
+
+[](https://join.slack.com/t/keploy/shared_invite/zt-357qqm9b5-PbZRVu3Yt2rJIa6ofrwWNg)
+[](https://www.linkedin.com/company/keploy/)
+[](https://www.youtube.com/channel/UC6OTg7F4o0WkmNtSoob34lg)
+[](https://x.com/Keployio)
+
+
+## 🌐 Language Support
+From Go's gopher 🐹 to Python's snake 🐍, we support:
+
+
+
+
+
+
+
+
+## 🫰 Keploy Adopters 🧡
+
+So you and your organisation are using Keploy? That’s great. Please add yourselves to [**this list,**](https://github.com/orgs/keploy/discussions/1765) and we'll send you goodies! 💖
+
+
+We are happy and proud to have you all as part of our community! 💖
+
+## 🎩 How's the Magic Happen?
+Keploy proxy captures and replays **ALL** (CRUD operations, including non-idempotent APIs) of your app's network interactions.
+
+
+Take a journey to **[How Keploy Works?](https://keploy.io/docs/keploy-explained/how-keploy-works/)** to discover the tricks behind the curtain!
+
+ ## 🔧 Core Features
+
+- ♻️ **Combined Test Coverage:** Merge your Keploy Tests with your fave testing libraries(JUnit, go-test, py-test, jest) to see a combined test coverage.
+
+
+- 🤖 **EBPF Instrumentation:** Keploy uses EBPF like a secret sauce to make integration code-less, language-agnostic, and oh-so-lightweight.
+
+
+- 🌐 **CI/CD Integration:** Run tests with mocks anywhere you like—locally on the CLI, in your CI pipeline (Jenkins, Github Actions..) , or even across a Kubernetes cluster.
+
+
+- 📽️ **Record-Replay Complex Flows:** Keploy can record and replay complex, distributed API flows as mocks and stubs. It's like having a time machine for your tests—saving you tons of time!
+
+
+- 🎭 **Multi-Purpose Mocks:** You can also use Keploy-generated Mocks, as server Tests!
+
+
+👉 **Explore the code on GitHub**: [github.com/keploy/keploy](https://github.com/keploy/keploy)
+
+
+## 👨🏻💻 Let's Build Together! 👩🏻💻
+Whether you're a newbie coder or a wizard 🧙♀️, your perspective is golden. Take a peek at our:
+
+📜 [Contribution Guidelines](https://github.com/keploy/keploy/blob/main/CONTRIBUTING.md)
+
+❤️ [Code of Conduct](https://github.com/keploy/keploy/blob/main/CODE_OF_CONDUCT.md)
+
+
+## 🐲 Current Limitations!
+- **Unit Testing:** While Keploy is designed to run alongside unit testing frameworks (Go test, JUnit..) and can add to the overall code coverage, it still generates integration tests.
+- **Production Lands**: Keploy is currently focused on generating tests for developers. These tests can be captured from any environment, but we have not tested it on high volume production environments. This would need robust deduplication to avoid too many redundant tests being captured. We do have ideas on building a robust deduplication system [#27](https://github.com/keploy/keploy/issues/27)
+
+## ✨ Resources!
+🤔 [FAQs](https://keploy.io/docs/keploy-explained/faq/)
+
+🕵️️ [Why Keploy](https://keploy.io/docs/keploy-explained/why-keploy/)
+
+⚙️ [Installation Guide](https://keploy.io/docs/application-development/)
+
+📖 [Contribution Guide](https://keploy.io/docs/keploy-explained/contribution-guide/)
diff --git a/keploy/READMEes-Es.md b/keploy/READMEes-Es.md
new file mode 100644
index 0000000..8503289
--- /dev/null
+++ b/keploy/READMEes-Es.md
@@ -0,0 +1,243 @@
+
+
+
+
+
+⚡️ Backend tests faster than unit-tests, from user traffic ⚡️
+
+
+
+🌟 The must-have tool for developers in the AI-Gen era 🌟
+
+
+---
+
+
+
+## 🎤 Presentando Keploy 🐰
+Keploy es una herramienta de prueba de backend centrada en el **desarrollador**. Realiza pruebas de backend con **mocks incorporados**, más rápido que las pruebas unitarias, a partir del tráfico del usuario, lo que lo hace **fácil de usar, potente y extensible**. 🛠
+
+¿Listo para la magia? Aquí están las características principales de Keploy:
+
+- ♻️ **Cobertura de prueba combinada:** Fusiona tus pruebas de Keploy con tus bibliotecas de pruebas favoritas (junit, go-test, py-test, jest) para ver una cobertura de prueba combinada.
+
+- 🤖 **Instrumentación EBPF:** Keploy utiliza EBPF como un ingrediente secreto para hacer que la integración sea sin código, independiente del lenguaje y muy ligera.
+
+- 🌐 **Integración CI/CD:** Ejecuta pruebas con mocks donde quieras, ya sea localmente en la CLI, en tu canal de integración continua o incluso en un clúster de Kubernetes. ¡Es prueba donde la necesitas!
+
+- 🎭 **Mocks multipropósito:** Úsalos en pruebas existentes, como pruebas de servidor o simplemente para impresionar a tus amigos.
+
+- 📽️ **Grabación y reproducción de flujos complejos:** Keploy puede grabar y reproducir flujos de API complejos y distribuidos como mocks y stubs. Es como tener una máquina del tiempo para tus pruebas, ¡ahorrándote mucho tiempo!
+
+
+
+> 🐰 **Dato curioso:** ¡Keploy se utiliza a sí mismo para realizar pruebas! Echa un vistazo a nuestra elegante insignia de cobertura: [](https://coveralls.io/github/keploy/keploy?branch=main&kill_cache=1)
+
+## 🌐 Soporte de idiomas
+Desde el gopher de Go 🐹 hasta la serpiente de Python 🐍, ofrecemos soporte para:
+
+
+
+
+
+
+## 🎩 ¿Cómo funciona la magia?
+Nuestro mágico 🧙♂️ proxy de Keploy captura y reproduce **TODAS** las interacciones de red de tu aplicación (operaciones CRUD, incluyendo APIs no idempotentes).
+
+Realiza un viaje a **[¿Cómo funciona Keploy?](https://docs.keploy.io/docs/keploy-explained/how-keploy-works)** para descubrir los trucos detrás del telón.
+
+
+
+## 📘 ¡Aprende más!
+Conviértete en un profesional de Keploy con nuestra **[Documentación](https://docs.keploy.io/)**.
+
+# Instalación rápida
+
+Usando **Binario** ( Linux / WSL)
+-
+
+Keploy se puede utilizar en Linux nativamente y a través de WSL en Windows.
+
+### Descarga el binario de Keploy.
+
+```zsh
+curl --silent --location "https://github.com/keploy/keploy/releases/latest/download/keploy_linux_amd64.tar.gz" | tar xz -C /tmp
+
+sudo mkdir -p /usr/local/bin && sudo mv /tmp/keploy /usr/local/bin && keploy
+
+
+ Arquitectura ARM
+curl --silent --location "https://github.com/keploy/keploy/releases/latest/download/keploy_linux_arm64.tar.gz" | tar xz -C /tmp
+
+sudo mkdir-p /usr/local/bin && sudo mv /tmp/keploy /usr/local/bin && keploy
+
+
+### Captura de casos de prueba
+Para iniciar la grabación de llamadas a la API, ejecuta este comando en tu terminal donde normalmente ejecutas tu aplicación. Si necesitas configurar variables de entorno, hazlo de la manera habitual:
+
+```zsh
+sudo -E env PATH=$PATH keploy record -c "CMD_PARA_EJECUTAR_LA_APP"
+```
+
+Por ejemplo, si estás utilizando un programa sencillo de Golang, el comando se vería así:
+
+```zsh
+sudo -E env PATH=$PATH keploy record -c "CMD_PARA_EJECUTAR_LA_APP"
+```
+
+### Ejecución de casos de prueba
+
+Para ejecutar los casos de prueba y generar un informe de cobertura de pruebas, utiliza este comando en la terminal donde normalmente ejecutas tu aplicación. Si necesitas configurar variables de entorno, hazlo de la manera habitual:
+
+```zsh
+sudo -E env PATH=$PATH keploy test -c "CMD_PARA_EJECUTAR_LA_APP" --delay 10
+ ```
+
+ Por ejemplo, si estás utilizando un framework de Golang, el comando sería:
+
+ ```zsh
+ sudo -E env PATH=$PATH keploy test -c "go run main.go" --delay 10
+ ```
+
+ Instalación de Docker
+
+Keploy se puede utilizar en Linux y Windows a través de Docker.
+
+> **️ Nota:** MacOS necesitan instalar [Colima](https://github.com/abiosoft/colima#installation). Usuarios de Windows necesitan installar [WSL](https://learn.microsoft.com/en-us/windows/wsl/install#install-wsl-command).
+
+### Creación de alias
+
+Creemos un alias para Keploy:
+
+```shell
+alias keploy='sudo docker run --pull always --name keploy-v2 -p 16789:16789 --privileged --pid=host -it -v $(pwd):$(pwd) -w $(pwd) -v /sys/fs/cgroup:/sys/fs/cgroup -v /sys/kernel/debug:/sys/kernel/debug -v /sys/fs/bpf:/sys/fs/bpf -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/keploy/keploy'
+```
+
+### Grabación de Casos de Prueba y Datos Simulados
+
+Aquí tienes algunos puntos a considerar antes de la grabación:
+- Si estás ejecutando mediante **docker-compose**, asegúrate de incluir el `` en el servicio de tu aplicación en el archivo docker-compose.yaml [como se muestra aquí](https://github.com/keploy/samples-python/blob/9d6cf40da2eb75f6e035bedfb30e54564785d5c9/flask-mongo/docker-compose.yml#L14).
+- Debes ejecutar los contenedores en una red, si no es así, asegúrate de que todos tus contenedores estén en la misma red con la propiedad externa activada - [como se muestra aquí](https://github.com/keploy/samples-python/blob/9d6cf40da2eb75f6e035bedfb30e54564785d5c9/flask-mongo/docker-compose.yml#L24). Reemplaza el **nombre de la red** (bandera `--network`) por tu red personalizada si la cambiaste anteriormente, como la red en el ejemplo dado.
+- `Docker_CMD_to_run_user_container` se refiere al **comando de Docker para iniciar** la aplicación.
+
+Utiliza el alias de keploy que creamos para capturar casos de prueba. **Ejecuta** el siguiente comando dentro del **directorio raíz** de tu aplicación.
+
+```shell
+keploy record -c "Docker_CMD_to_run_user_container --network " --containerName ""
+```
+
+Realiza llamadas API utilizando herramientas como [Hoppscotch](https://hoppscotch.io/), [Postman](https://www.postman.com/) o comandos cURL.
+
+Keploy capturará las llamadas API que hayas realizado, generando suites de pruebas que comprenden **casos de prueba (KTests) y simulaciones de datos (KMocks)** en formato `YAML`.
+
+### Ejecución de Casos de Prueba
+
+Ahora, utiliza el alias keployV2 que creamos para ejecutar los casos de prueba. Sigue estos pasos en el **directorio raíz** de tu aplicación.
+
+Cuando utilices **docker-compose** para iniciar la aplicación, es importante asegurarse de que el parámetro `--containerName` coincida con el nombre del contenedor en tu archivo `docker-compose.yaml`.
+
+```shell
+keploy test -c "Docker_CMD_to_run_user_container --network " --containerName "" --delay 20
+```
+
+## 🤔 Preguntas?
+¡Contáctanos! Estamos aquí para ayudarte.
+
+[](https://join.slack.com/t/keploy/shared_invite/zt-357qqm9b5-PbZRVu3Yt2rJIa6ofrwWNg)
+[](https://www.linkedin.com/company/keploy/)
+[](https://www.youtube.com/channel/UC6OTg7F4o0WkmNtSoob34lg)
+[](https://x.com/Keployio)
+
+## 💖 ¡Construyamos Juntos!
+Ya seas un principiante o un mago 🧙♀️ en la programación, tu perspectiva es valiosa. Echa un vistazo a nuestras:
+
+📜 [Directrices de Contribución](https://github.com/keploy/keploy/blob/main/CONTRIBUTING.md)
+
+❤️ [Código de Conducta](https://github.com/keploy/keploy/blob/main/CODE_OF_CONDUCT.md)
+
+## 🌟 Características
+
+### **🚀 Exporta, mantiene y muestra pruebas y simulaciones!**
+
+
+
+### **🤝 Saluda a los populares frameworks de pruebas - Go-Test, JUnit, Py-Test, Jest y más!**
+
+
+
+### **🕵️ Detecta ruido con precisión de cirujano!**
+Filtra campos ruidosos en las respuestas de las API como (marcas de tiempo, valores aleatorios) para asegurar pruebas de alta calidad.
+
+### **📊 ¡Saluda a una mayor cobertura!**
+Keploy se asegura de que no se generen casos de prueba redundantes.
+
+## 🐲 Los Desafíos que Enfrentamos!
+- **Pruebas Unitarias:** Aunque Keploy está diseñado para funcionar junto con los marcos de pruebas unitarias (Go test, JUnit, etc.) y puede contribuir a la cobertura de código general, todavía genera pruebas de extremo a extremo (E2E).
+- **Entornos de Producción:** Keploy actualmente se centra en generar pruebas para desarrolladores. Estas pruebas se pueden capturar desde cualquier entorno, pero no las hemos probado en entornos de producción de alto volumen. Esto requeriría una sólida deduplicación para evitar la captura de pruebas redundantes en exceso. Tenemos ideas para construir un sistema de deduplicación sólido [#27](https://github.com/keploy/keploy/issues/27)
+
+## ✨ Recursos!
+🤔 [Preguntas Frecuentes](https://docs.keploy.io/docs/keploy-explained/faq)
+
+🕵️️ [¿Por Qué Keploy?](https://docs.keploy.io/docs/keploy-explained/why-keploy)
+
+⚙️ [Guía de Instalación](https://docs.keploy.io/docs/server/server-installation)
+
+📖 [Guía de Contribución](https://docs.keploy.io/docs/devtools/server-contrib-guide/)
+
+## 🌟 Salón de Contribuyentes
+
+
+
+
+### Premios Disponibles
+
+| Nombre | Icono | Descripción |
+| ---- | ---- | ----------- |
+| Creador de Documentos | | ¡Premiado por ayudar a mejorar la documentación de Keploy! |
+| Cada Bit Cuenta | | ¡Ningún commit es demasiado pequeño! |
+| Héroe de Solicitudes de Extracción | | ¡Eres un héroe de solicitudes de extracción, sigue así! |
+| Cercano| | ¡Solo los cercanos consiguen café! |
diff --git a/keploy/READMEja-JP.md b/keploy/READMEja-JP.md
new file mode 100644
index 0000000..20a5067
--- /dev/null
+++ b/keploy/READMEja-JP.md
@@ -0,0 +1,160 @@
+
+
+
+
+
+⚡️ ユーザートラフィックからのユニットテストよりも速いAPIテスト ⚡️
+
+
+
+🌟 AI-Gen時代の開発者に必須のツール 🌟
+
+
+---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[](https://join.slack.com/t/keploy/shared_invite/zt-357qqm9b5-PbZRVu3Yt2rJIa6ofrwWNg)
+[](https://www.linkedin.com/company/keploy/)
+[](https://www.youtube.com/channel/UC6OTg7F4o0WkmNtSoob34lg)
+[](https://x.com/Keployio)
+
+
+
+
+[Keploy](https://keploy.io) は、**開発者中心**のAPIテストツールで、**組み込みモック**を使用してユニットテストよりも速くテストを作成します。
+
+KeployはAPI呼び出しだけでなく、データベース呼び出しも記録し、テスト中に再生するため、**使いやすく、強力で、拡張性があります**。
+
+
+
+> 🐰 **面白い事実:** Keployは自分自身をテストに使用しています!私たちの素晴らしいカバレッジバッジをチェックしてください: [](https://coveralls.io/github/keploy/keploy?branch=main&kill_cache=1)
+
+## 🚨 [ユニットテストジェネレーター](README-UnitGen.md) (ut-gen) のためにここにいますか?
+Keployは、[Meta LLM研究論文](https://arxiv.org/pdf/2402.09171)の世界初のユニットテストジェネレーター(ut-gen)実装を新たに発表しました。これはコードのセマンティクスを理解し、意味のあるユニットテストを生成します。目指すのは:
+
+- **ユニットテスト生成の自動化 (UTG)**: 包括的なユニットテストを迅速に生成し、冗長な手動作業を削減します。
+
+- **エッジケースの改善**: 自動テストの範囲を拡張し、手動で見逃されがちな複雑なシナリオをカバーします。
+
+- **テストカバレッジの向上**: コードベースが成長するにつれて、徹底的なカバレッジを確保することが可能になります。
+
+### 📜 [ユニットテストジェネレーター README](README-UnitGen.md) をフォローしてください! ✅
+
+## 📘 ドキュメント!
+**[Keploy Documentation](https://keploy.io/docs/)** でKeployのプロフェッショナルになりましょう。
+
+
+
+# 🚀 クイックインストール (APIテストジェネレーター)
+
+エージェントをローカルにインストールしてKeployを統合します。コード変更は不要です。
+
+```shell
+curl --silent -O -L https://keploy.io/install.sh && source install.sh
+```
+
+## 🎬 テストケースの記録
+
+API呼び出しをテストとモック/スタブに変換するために、Keployを使用してアプリを開始します。
+
+```zsh
+keploy record -c "CMD_TO_RUN_APP"
+```
+例えば、シンプルなPythonアプリを使用している場合、`CMD_TO_RUN_APP`は`python main.py`、Golangの場合は`go run main.go`、Javaの場合は`java -jar xyz.jar`、Nodeの場合は`npm start`のようになります。
+
+```zsh
+keploy record -c "python main.py"
+```
+
+## 🧪 テストの実行
+データベース、Redis、Kafka、またはアプリケーションが使用する他のサービスをシャットダウンします。Keployはテスト中にそれらを必要としません。
+```zsh
+keploy test -c "CMD_TO_RUN_APP" --delay 10
+```
+
+## ✅ テストカバレッジの統合
+ユニットテストライブラリと統合して、結合テストカバレッジを表示するには、この[テストカバレッジガイド](https://keploy.io/docs/server/sdk-installation/go/)に従ってください。
+
+> #### **楽しんでいただけましたか:** このリポジトリに🌟スターを残してください!無料で笑顔をもたらします。😄 👏
+
+## ワンクリックセットアップ 🚀
+
+ローカルマシンのインストールなしでKeployを迅速にセットアップして実行します:
+
+[]([https://github.dev/Sonichigo/mux-sql](https://github.dev/Sonichigo/mux-sql))
+
+## 🤔 質問がありますか?
+私たちに連絡してください。お手伝いします!
+
+[](https://join.slack.com/t/keploy/shared_invite/zt-357qqm9b5-PbZRVu3Yt2rJIa6ofrwWNg)
+[](https://www.linkedin.com/company/keploy/)
+[](https://www.youtube.com/channel/UC6OTg7F4o0WkmNtSoob34lg)
+[](https://x.com/Keployio)
+
+
+## 🌐 言語サポート
+Goのゴーファー 🐹 からPythonのスネーク 🐍 まで、以下の言語をサポートしています:
+
+
+
+
+
+
+
+
+## 🫰 Keployの採用者 🧡
+
+あなたとあなたの組織がKeployを使用しているのですか?それは素晴らしいことです。 [**このリスト**](https://github.com/orgs/keploy/discussions/1765) に追加してください。グッズをお送りします!💖
+
+私たちは、あなたたち全員が私たちのコミュニティの一員であることを誇りに思います!💖
+
+## 🎩 魔法はどのように起こるのか?
+Keployプロキシは、アプリの**すべての**ネットワークインタラクション(CRUD操作、非冪等なAPIを含む)をキャプチャして再生します。
+
+**[Keployの仕組み](https://keploy.io/docs/keploy-explained/how-keploy-works/)** の旅に出て、カーテンの裏にあるトリックを発見してください!
+
+ここにKeployの主な機能があります: 🛠
+
+- ♻️ **結合テストカバレッジ:** Keployテストをお気に入りのテストライブラリ(JUnit、go-test、py-test、jest)と統合して、結合テストカバレッジを表示します。
+
+- 🤖 **EBPFインストルメンテーション:** KeployはEBPFを使用して、コードレス、言語非依存、非常に軽量な統合を実現します。
+
+- 🌐 **CI/CD統合:** テストをローカルCLI、CIパイプライン(Jenkins、Github Actions..)、またはKubernetesクラスター全体で実行します。
+
+- 📽️ **複雑なフローの記録と再生:** Keployは、複雑で分散したAPIフローをモックとスタブとして記録して再生できます。これは、テストのためのタイムマシンを持っているようなもので、たくさんの時間を節約できます!
+
+- 🎭 **多目的モック:** Keployモックをサーバーテストとしても使用できます!
+
+## 👨🏻💻 一緒に構築しましょう! 👩🏻💻
+初心者のコーダーでもウィザードでも 🧙♀️、あなたの視点は貴重です。以下をチェックしてください:
+
+📜 [貢献ガイドライン](https://github.com/keploy/keploy/blob/main/CONTRIBUTING.md)
+
+❤️ [行動規範](https://github.com/keploy/keploy/blob/main/CODE_OF_CONDUCT.md)
+
+
+## 🐲 現在の制限事項!
+- **ユニットテスト:** Keployはユニットテストフレームワーク(Go test、JUnit..)と一緒に実行するように設計されており、全体的なコードカバレッジに追加することができますが、それでも統合テストを生成します。
+- **プロダクション環境:** Keployは現在、開発者向けのテスト生成に焦点を当てています。これらのテストは任意の環境からキャプチャできますが、高ボリュームのプロダクション環境ではテストしていません。これは、過剰な冗長テストのキャプチャを避けるために堅牢な重複排除が必要です。堅牢な重複排除システムの構築についてのアイデアがあります [#27](https://github.com/keploy/keploy/issues/27)
+
+## ✨ リソース!
+🤔 [FAQ](https://keploy.io/docs/keploy-explained/faq/)
+
+🕵️️ [なぜKeploy](https://keploy.io/docs/keploy-explained/why-keploy/)
+
+⚙️ [インストールガイド](https://keploy.io/docs/application-development/)
+
+📖 [貢献ガイド](https://keploy.io/docs/keploy-explained/contribution-guide/)
diff --git a/keploy/SECURITY.md b/keploy/SECURITY.md
new file mode 100755
index 0000000..3eafe68
--- /dev/null
+++ b/keploy/SECURITY.md
@@ -0,0 +1,9 @@
+# Security Policy
+
+## Reporting a Vulnerability
+
+We value security for the project very highly. We encourage all users to report any vulnerabilities they discover to us.
+If you find a security vulnerability in the Keploy project, please report it responsibly by sending an email to hello@keploy.io
+
+At this juncture, we don't have a bug bounty program. We are a small team trying to solve a big problem. We urge you to report any vulnerabilities responsibly
+so that we can continue building a secure application for the entire community.
\ No newline at end of file
diff --git a/keploy/cli/README.md b/keploy/cli/README.md
new file mode 100755
index 0000000..71c21c5
--- /dev/null
+++ b/keploy/cli/README.md
@@ -0,0 +1,5 @@
+# CMD Package Documentation
+
+In this package, the `root` command and its `subcommands` are defined
+for the CLI. This package, which is called from the main package, utilizes the
+`pkg` services to execute commands.
diff --git a/keploy/cli/cli.go b/keploy/cli/cli.go
new file mode 100644
index 0000000..d7ae3aa
--- /dev/null
+++ b/keploy/cli/cli.go
@@ -0,0 +1,22 @@
+// Package cli provides functionality for the command-line interface of the application.
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ "go.uber.org/zap"
+)
+
+type HookFunc func(context.Context, *zap.Logger, *config.Config, ServiceFactory, CmdConfigurator) *cobra.Command
+
+// Registered holds the registered command hooks
+var Registered map[string]HookFunc
+
+func Register(name string, f HookFunc) {
+ if Registered == nil {
+ Registered = make(map[string]HookFunc)
+ }
+ Registered[name] = f
+}
diff --git a/keploy/cli/config.go b/keploy/cli/config.go
new file mode 100644
index 0000000..8a7b0f5
--- /dev/null
+++ b/keploy/cli/config.go
@@ -0,0 +1,75 @@
+package cli
+
+import (
+ "context"
+ "errors"
+ "path/filepath"
+
+ "go.keploy.io/server/v2/config"
+
+ toolsSvc "go.keploy.io/server/v2/pkg/service/tools"
+ "go.keploy.io/server/v2/utils"
+
+ "github.com/spf13/cobra"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("config", Config)
+}
+
+func Config(ctx context.Context, logger *zap.Logger, cfg *config.Config, servicefactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "config",
+ Short: "manage keploy configuration file",
+ Example: "keploy config --generate --path /path/to/localdir",
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.ValidateFlags(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ isGenerate, err := cmd.Flags().GetBool("generate")
+ if err != nil {
+ utils.LogError(logger, err, "failed to get generate flag")
+ return err
+ }
+
+ if isGenerate {
+ filePath := filepath.Join(cfg.Path, "keploy.yml")
+ if !cfg.InCi && utils.CheckFileExists(filePath) {
+ override, err := utils.AskForConfirmation(ctx, "Config file already exists. Do you want to override it?")
+ if err != nil {
+ utils.LogError(logger, err, "failed to ask for confirmation")
+ return err
+ }
+ if !override {
+ logger.Info("Skipping config file override")
+ return nil
+ }
+ }
+ svc, err := servicefactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return err
+ }
+ var tools toolsSvc.Service
+ var ok bool
+ if tools, ok = svc.(toolsSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy tools service interface")
+ return err
+ }
+ if err := tools.CreateConfig(ctx, filePath, ""); err != nil {
+ utils.LogError(logger, err, "failed to create config")
+ return err
+ }
+ logger.Info("Config file generated successfully")
+ return nil
+ }
+ return errors.New("only generate flag is supported in the config command")
+ },
+ }
+ if err := cmdConfigurator.AddFlags(cmd); err != nil {
+ utils.LogError(logger, err, "failed to add flags")
+ return nil
+ }
+ return cmd
+}
diff --git a/keploy/cli/contract.go b/keploy/cli/contract.go
new file mode 100644
index 0000000..e1e0eb7
--- /dev/null
+++ b/keploy/cli/contract.go
@@ -0,0 +1,134 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ contractSvc "go.keploy.io/server/v2/pkg/service/contract"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("contract", Contract)
+}
+
+func Contract(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "contract",
+ Short: "Manage keploy contracts",
+ }
+
+ cmd.AddCommand(Generate(ctx, logger, serviceFactory, cmdConfigurator))
+ cmd.AddCommand(Download(ctx, logger, serviceFactory, cmdConfigurator))
+ cmd.AddCommand(Validate(ctx, logger, serviceFactory, cmdConfigurator))
+ for _, subCmd := range cmd.Commands() {
+ err := cmdConfigurator.AddFlags(subCmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add flags to command", zap.String("command", subCmd.Name()))
+ }
+ }
+ return cmd
+}
+
+func Generate(ctx context.Context, logger *zap.Logger, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "generate",
+ Short: "Generate contract for specified services",
+ Example: `keploy contract generate --service="email,notify"`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, "contract")
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var contract contractSvc.Service
+ var ok bool
+ if contract, ok = svc.(contractSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy contract service interface")
+ return nil
+ }
+ // Extract services from the flag
+
+ err = contract.Generate(ctx, true)
+
+ if err != nil {
+ utils.LogError(logger, err, "failed to generate contract")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ return cmd
+
+}
+
+func Download(ctx context.Context, logger *zap.Logger, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "download",
+ Short: "Download contract for specified services",
+ Example: `keploy contract download --service="email,notify" --path /local/path`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, "contract")
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var contract contractSvc.Service
+ var ok bool
+ if contract, ok = svc.(contractSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy contract service interface")
+ return nil
+ }
+ err = contract.Download(ctx, true)
+
+ if err != nil {
+ utils.LogError(logger, err, "failed to download contract")
+ }
+ return nil
+ },
+ }
+
+ return cmd
+}
+
+func Validate(ctx context.Context, logger *zap.Logger, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "test",
+ Short: "Validate contract for specified services",
+ Example: `keploy contract test --service="email,notify" --path /local/path`,
+
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, "contract")
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var contract contractSvc.Service
+ var ok bool
+ if contract, ok = svc.(contractSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy contract service interface")
+ return nil
+ }
+ err = contract.Validate(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to validate contract")
+ }
+ return nil
+ },
+ }
+
+ return cmd
+}
diff --git a/keploy/cli/examples.go b/keploy/cli/examples.go
new file mode 100755
index 0000000..d8f3baf
--- /dev/null
+++ b/keploy/cli/examples.go
@@ -0,0 +1,47 @@
+package cli
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ "go.keploy.io/server/v2/cli/provider"
+ "go.keploy.io/server/v2/config"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+
+ "github.com/spf13/cobra"
+)
+
+func init() {
+ Register("example", Example)
+}
+
+func Example(_ context.Context, logger *zap.Logger, _ *config.Config, _ ServiceFactory, _ CmdConfigurator) *cobra.Command {
+ var customSetup bool
+ var cmd = &cobra.Command{
+ Use: "example",
+ Short: "Example to record and test via keploy",
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ disableAnsi, _ := (cmd.Flags().GetBool("disable-ansi"))
+ provider.PrintLogo(os.Stdout, disableAnsi)
+ customSetup, err := cmd.Flags().GetBool("customSetup")
+ if err != nil {
+ utils.LogError(logger, nil, "failed to read the customSetup flag")
+ return err
+ }
+ if customSetup {
+ fmt.Println(provider.Examples)
+ return nil
+ }
+ fmt.Println(provider.ExampleOneClickInstall)
+ fmt.Println(provider.WithoutexampleOneClickInstall)
+ return nil
+ },
+ }
+ cmd.SetHelpTemplate(provider.CustomHelpTemplate)
+
+ cmd.Flags().Bool("customSetup", customSetup, "Check if the user is using one click install")
+
+ return cmd
+}
diff --git a/keploy/cli/export.go b/keploy/cli/export.go
new file mode 100644
index 0000000..854ab91
--- /dev/null
+++ b/keploy/cli/export.go
@@ -0,0 +1,63 @@
+package cli
+
+import (
+ "context"
+ "os"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/cli/provider"
+ "go.keploy.io/server/v2/config"
+ toolsSvc "go.keploy.io/server/v2/pkg/service/tools"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("export", Export)
+}
+
+func Export(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+
+ var exportCmd = &cobra.Command{
+ Use: "export",
+ Short: "export Keploy tests as postman collection",
+ Example: "keploy export",
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ disableAnsi, _ := (cmd.Flags().GetBool("disable-ansi"))
+ provider.PrintLogo(os.Stdout, disableAnsi)
+ return cmd.Help()
+ },
+ }
+ var postmanCmd = &cobra.Command{
+ Use: "postman",
+ Short: "export Keploy tests as Postman collection",
+ Example: "keploy export postman",
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ disableAnsi, _ := (cmd.Flags().GetBool("disable-ansi"))
+ provider.PrintLogo(os.Stdout, disableAnsi)
+ svc, err := serviceFactory.GetService(ctx, "export")
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var tools toolsSvc.Service
+ var ok bool
+ if tools, ok = svc.(toolsSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy tools service interface")
+ return nil
+ }
+ err = tools.Export(ctx) // Assuming ExportPostmanCollection is a method in tools service
+ if err != nil {
+ utils.LogError(logger, err, "failed to export Postman collection")
+ }
+ return nil
+ },
+ }
+ exportCmd.AddCommand(postmanCmd)
+
+ if err := cmdConfigurator.AddFlags(exportCmd); err != nil {
+ utils.LogError(logger, err, "failed to add export cmd flags")
+ return nil
+ }
+ return exportCmd
+}
diff --git a/keploy/cli/import.go b/keploy/cli/import.go
new file mode 100644
index 0000000..e589e29
--- /dev/null
+++ b/keploy/cli/import.go
@@ -0,0 +1,72 @@
+package cli
+
+import (
+ "context"
+ "os"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/cli/provider"
+ "go.keploy.io/server/v2/config"
+ toolsSvc "go.keploy.io/server/v2/pkg/service/tools"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("import", Import)
+}
+
+func Import(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+
+ var importCmd = &cobra.Command{
+ Use: "import",
+ Short: "import postman collection to Keploy tests",
+ Example: "keploy import",
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ disableAnsi, _ := (cmd.Flags().GetBool("disable-ansi"))
+ provider.PrintLogo(os.Stdout, disableAnsi)
+ return cmd.Help()
+ },
+ }
+
+ var postmanCmd = &cobra.Command{
+ Use: "postman",
+ Short: "import postman collection to Keploy tests",
+ Example: "keploy import postman",
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ disableAnsi, _ := (cmd.Flags().GetBool("disable-ansi"))
+ provider.PrintLogo(os.Stdout, disableAnsi)
+ path, _ := cmd.Flags().GetString("path")
+ if path == "" {
+ path = "output.json"
+ }
+ basePath, _ := cmd.Flags().GetString("base-path")
+ svc, err := serviceFactory.GetService(ctx, "import")
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var tools toolsSvc.Service
+ var ok bool
+ if tools, ok = svc.(toolsSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy tools service interface")
+ return nil
+ }
+ err = tools.Import(ctx, path, basePath)
+ if err != nil {
+ utils.LogError(logger, err, "failed to import Postman collection")
+ }
+ return nil
+ },
+ }
+ importCmd.AddCommand(postmanCmd)
+
+ for _, subCmd := range importCmd.Commands() {
+ err := cmdConfigurator.AddFlags(subCmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add flags to command", zap.String("command", subCmd.Name()))
+ }
+ }
+
+ return importCmd
+}
diff --git a/keploy/cli/load.go b/keploy/cli/load.go
new file mode 100644
index 0000000..5787e34
--- /dev/null
+++ b/keploy/cli/load.go
@@ -0,0 +1,66 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ loadSvc "go.keploy.io/server/v2/pkg/service/load"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("load", Load)
+}
+
+func Load(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "load",
+ Short: "load test a given testsuite.",
+ Example: `keploy load -f test_suite.yaml --out json > report.json`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service")
+ return nil
+ }
+
+ // Get CLI parameters
+ vus, _ := cmd.Flags().GetInt("vus")
+ duration, _ := cmd.Flags().GetString("duration")
+ rps, _ := cmd.Flags().GetInt("rps")
+
+ // values comming from CLI flags to override the spec.load options.
+ ctx := context.WithValue(ctx, "vus", vus)
+ ctx = context.WithValue(ctx, "duration", duration)
+ ctx = context.WithValue(ctx, "rps", rps)
+
+ var ltSvc loadSvc.Service
+ var ok bool
+ if ltSvc, ok = svc.(loadSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy load service interface")
+ return nil
+ }
+
+ err = ltSvc.Start(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to start the load tester")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ err := cmdConfigurator.AddFlags(cmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add load flags")
+ return nil
+ }
+
+ return cmd
+}
diff --git a/keploy/cli/login.go b/keploy/cli/login.go
new file mode 100644
index 0000000..6d41466
--- /dev/null
+++ b/keploy/cli/login.go
@@ -0,0 +1,41 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ toolsSvc "go.keploy.io/server/v2/pkg/service/tools"
+
+ "go.keploy.io/server/v2/config"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("login", Login)
+}
+
+func Login(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, _ CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "login",
+ Short: "login to keploy via github",
+ Example: `keploy login`,
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var tools toolsSvc.Service
+ var ok bool
+ if tools, ok = svc.(toolsSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy record service interface")
+ return nil
+ }
+ tools.Login(ctx)
+ return nil
+ },
+ }
+
+ return cmd
+}
diff --git a/keploy/cli/mock.go b/keploy/cli/mock.go
new file mode 100644
index 0000000..65150ba
--- /dev/null
+++ b/keploy/cli/mock.go
@@ -0,0 +1,94 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ replaySvc "go.keploy.io/server/v2/pkg/service/replay"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("mock", Mock)
+}
+
+func Mock(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "mock",
+ Short: "Managing mocks",
+ }
+
+ cmd.AddCommand(DownloadMocks(ctx, logger, serviceFactory, cmdConfigurator))
+ cmd.AddCommand(UploadMocks(ctx, logger, serviceFactory, cmdConfigurator))
+ for _, subCmd := range cmd.Commands() {
+ err := cmdConfigurator.AddFlags(subCmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add flags to command", zap.String("command", subCmd.Name()))
+ }
+ }
+ return cmd
+}
+
+func DownloadMocks(ctx context.Context, logger *zap.Logger, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "download",
+ Short: "Download mocks from the keploy registry",
+ Example: `keploy mock download`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Parent().Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Parent().Name()))
+ return nil
+ }
+ var replay replaySvc.Service
+ var ok bool
+ if replay, ok = svc.(replaySvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy replay service interface")
+ return nil
+ }
+ if err := replay.DownloadMocks(ctx); err != nil {
+ utils.LogError(logger, err, "failed to download mocks from keploy registry")
+ return nil
+ }
+ return nil
+ },
+ }
+
+ return cmd
+}
+
+func UploadMocks(ctx context.Context, logger *zap.Logger, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "upload",
+ Short: "Upload mocks to the keploy registry",
+ Example: `keploy mock upload`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Parent().Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Parent().Name()))
+ return nil
+ }
+ var replay replaySvc.Service
+ var ok bool
+ if replay, ok = svc.(replaySvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy replay service interface")
+ return nil
+ }
+ if err := replay.UploadMocks(ctx); err != nil {
+ utils.LogError(logger, err, "failed to upload mocks to the keploy registry")
+ return nil
+ }
+ return nil
+ },
+ }
+
+ return cmd
+}
diff --git a/keploy/cli/normalise.go b/keploy/cli/normalise.go
new file mode 100644
index 0000000..ff5a33b
--- /dev/null
+++ b/keploy/cli/normalise.go
@@ -0,0 +1,50 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ replaySvc "go.keploy.io/server/v2/pkg/service/replay"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("normalize", Normalize)
+}
+
+// Normalize retrieves the command to normalize Keploy
+func Normalize(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var normalizeCmd = &cobra.Command{
+ Use: "normalize",
+ Short: "Normalize Keploy",
+ Example: "keploy normalize --test-run testrun --tests test-set-1:test-case-1 test-case-2,test-set-2:test-case-1 test-case-2 ",
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.ValidateFlags(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var replay replaySvc.Service
+ var ok bool
+ if replay, ok = svc.(replaySvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy replay service interface")
+ return nil
+ }
+ if err := replay.Normalize(ctx); err != nil {
+ utils.LogError(logger, err, "failed to normalize test cases")
+ return nil
+ }
+ return nil
+ },
+ }
+ if err := cmdConfigurator.AddFlags(normalizeCmd); err != nil {
+ utils.LogError(logger, err, "failed to add normalize cmd flags")
+ return nil
+ }
+ return normalizeCmd
+}
diff --git a/keploy/cli/provider/cmd.go b/keploy/cli/provider/cmd.go
new file mode 100644
index 0000000..41622e8
--- /dev/null
+++ b/keploy/cli/provider/cmd.go
@@ -0,0 +1,1079 @@
+// Package provider provides functionality for the keploy provider.
+package provider
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "strings"
+ "time"
+
+ "gopkg.in/yaml.v3"
+
+ "github.com/fatih/color"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+ "github.com/spf13/viper"
+ "go.keploy.io/server/v2/config"
+ "go.keploy.io/server/v2/pkg"
+ "go.keploy.io/server/v2/pkg/models"
+ "go.keploy.io/server/v2/pkg/service/tools"
+ "go.keploy.io/server/v2/utils"
+ "go.keploy.io/server/v2/utils/log"
+ "go.uber.org/zap"
+)
+
+func LogExample(example string) string {
+ return fmt.Sprintf("Example usage: %s", example)
+}
+
+var CustomHelpTemplate = `
+{{if .Example}}Examples:
+{{.Example}}
+{{end}}
+{{if .HasAvailableSubCommands}}Guided Commands:{{range .Commands}}{{if .IsAvailableCommand}}
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}
+{{end}}
+{{if .HasAvailableFlags}}Flags:
+{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}
+{{end}}
+Use "{{.CommandPath}} [command] --help" for more information about a command.
+`
+
+var WithoutexampleOneClickInstall = `
+Note: If installed keploy without One Click Install, use "keploy example --customSetup true"
+`
+var Examples = `
+Golang Application
+ Record:
+ sudo -E env PATH=$PATH keploy record -c "/path/to/user/app/binary"
+
+ Test:
+ sudo -E env PATH=$PATH keploy test -c "/path/to/user/app/binary" --delay 10
+
+Node Application
+ Record:
+ sudo -E env PATH=$PATH keploy record -c “npm start --prefix /path/to/node/app"
+
+ Test:
+ sudo -E env PATH=$PATH keploy test -c “npm start --prefix /path/to/node/app" --delay 10
+
+Java
+ Record:
+ sudo -E env PATH=$PATH keploy record -c "java -jar /path/to/java-project/target/jar"
+
+ Test:
+ sudo -E env PATH=$PATH keploy test -c "java -jar /path/to/java-project/target/jar" --delay 10
+
+Docker
+ Alias:
+ alias keploy='sudo docker run --name keploy-ebpf -p 16789:16789 --privileged --pid=host -it -v $(pwd):$(pwd) -w $(pwd) -v /sys/fs/cgroup:/sys/fs/cgroup
+ -v /sys/kernel/debug:/sys/kernel/debug -v /sys/fs/bpf:/sys/fs/bpf -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/keploy/keploy'
+
+ Record:
+ keploy record -c "docker run -p 8080:8080 --name --network " --buildDelay 60
+
+ Test:
+ keploy test -c "docker run -p 8080:8080 --name --network " --delay 10 --buildDelay 60
+
+`
+
+var ExampleOneClickInstall = `
+Golang Application
+ Record:
+ keploy record -c "/path/to/user/app/binary"
+
+ Test:
+ keploy test -c "/path/to/user/app/binary" --delay 10
+
+Node Application
+ Record:
+ keploy record -c “npm start --prefix /path/to/node/app"
+
+ Test:
+ keploy test -c “npm start --prefix /path/to/node/app" --delay 10
+
+Java
+ Record:
+ keploy record -c "java -jar /path/to/java-project/target/jar"
+
+ Test:
+ keploy test -c "java -jar /path/to/java-project/target/jar" --delay 10
+
+Docker
+ Record:
+ keploy record -c "docker run -p 8080:8080 --name --network " --buildDelay 60
+
+ Test:
+ keploy test -c "docker run -p 8080:8080 --name --network " --delay 1 --buildDelay 60
+`
+
+var RootCustomHelpTemplate = `{{.Short}}
+
+Usage:{{if .Runnable}}
+ {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
+ {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
+
+Aliases:
+ {{.NameAndAliases}}{{end}}{{if .HasExample}}
+
+Available Commands:{{range .Commands}}{{if .IsAvailableCommand}}
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableFlags}}
+
+Flags:
+{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableLocalFlags}}
+
+Guided Commands:{{range .Commands}}{{if and (not .IsAvailableCommand) (not .Hidden)}}
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}
+
+Examples:
+{{.Example}}
+
+Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
+`
+
+var RootExamples = `
+ Record:
+ keploy record -c "docker run -p 8080:8080 --name --network keploy-network " --container-name "" --buildDelay 60
+
+ Test:
+ keploy test --c "docker run -p 8080:8080 --name --network keploy-network " --delay 10 --buildDelay 60
+
+ Config:
+ keploy config --generate -p "/path/to/localdir"
+`
+
+var VersionTemplate = `{{with .Version}}{{printf "Keploy %s" .}}{{end}}{{"\n"}}`
+var IsConfigFileFound = true
+
+type CmdConfigurator struct {
+ logger *zap.Logger
+ cfg *config.Config
+}
+
+func NewCmdConfigurator(logger *zap.Logger, config *config.Config) *CmdConfigurator {
+ return &CmdConfigurator{
+ logger: logger,
+ cfg: config,
+ }
+}
+
+func (c *CmdConfigurator) AddFlags(cmd *cobra.Command) error {
+ //sets the displayment of flag-related errors
+ cmd.SilenceErrors = true
+ cmd.SetFlagErrorFunc(func(_ *cobra.Command, err error) error {
+ PrintLogo(os.Stdout, true)
+ color.Red(fmt.Sprintf("❌ error: %v", err))
+ fmt.Println()
+ return err
+ })
+
+ //add flags
+ var err error
+ cmd.Flags().SetNormalizeFunc(aliasNormalizeFunc)
+ cmd.Flags().String("configPath", ".", "Path to the local directory where keploy configuration file is stored")
+
+ // Flags for secure subcommands
+ if cmd.Parent() != nil && cmd.Parent().Name() == "secure" {
+ switch cmd.Name() {
+ case "add":
+ cmd.Flags().String("checks-path", "keploy/secure/custom-checks.yaml", "Path to the custom checks file")
+ case "remove":
+ cmd.Flags().String("checks-path", "keploy/secure/custom-checks.yaml", "Path to the custom checks file")
+ cmd.Flags().String("id", "", "ID of the custom check to remove")
+ case "update":
+ cmd.Flags().String("checks-path", "keploy/secure/custom-checks.yaml", "Path to the custom checks file")
+ cmd.Flags().String("id", "", "ID of the custom check to update")
+ case "list":
+ cmd.Flags().String("rule-set", "basic", "Specify which checks to list: 'basic' (built-in), 'custom'")
+ cmd.Flags().String("checks-path", "keploy/secure/custom-checks.yaml", "Path to the custom checks file")
+ }
+ return nil
+ }
+
+ switch cmd.Name() {
+ case "secure":
+ cmd.Flags().String("base-url", "", "Base URL of the application to be tested.")
+ cmd.Flags().String("ts-path", "keploy/testsuite", "Directory path containing test suite YAML files.")
+ cmd.Flags().String("ts-file", "suite-0.yaml", "Name of the testsuite YAML file.")
+ cmd.Flags().String("rule-set", "basic", "Specify which checks to execute: 'basic' (built-in), 'custom'")
+ cmd.Flags().String("checks-path", "keploy/secure/custom-checks.yaml", "Path to the custom checks file")
+ case "load":
+ cmd.Flags().String("base-url", "", "Base URL of the application to be tested.")
+ cmd.Flags().String("ts-path", "keploy/testsuite", "Directory path containing test suite YAML files.")
+ cmd.Flags().String("ts-file", "suite-0.yaml", "Name of the testsuite YAML file.")
+ cmd.Flags().Int("vus", 1, "Number of virtual users to run.")
+ cmd.Flags().String("duration", "", "Time for the load test to keep running.")
+ cmd.Flags().Int("rps", 0, "Number of requests per second to run.")
+ case "testsuite":
+ cmd.Flags().String("base-url", "", "Base URL of the application to be tested.")
+ cmd.Flags().String("ts-path", "keploy/testsuite", "Directory path containing test suite YAML files.")
+ cmd.Flags().String("ts-file", "suite-0.yaml", "Name of the testsuite YAML file.")
+
+ case "upload": //for uploading mocks
+ cmd.Flags().StringP("path", "p", ".", "Path to local keploy directory where generated mocks are stored")
+ cmd.Flags().StringSliceP("test-sets", "t", utils.Keys(c.cfg.Test.SelectedTests), "Testsets to consider e.g. -t \"test-set-1, test-set-2\"")
+ case "generate", "download":
+
+ if cmd.Name() == "download" && cmd.Parent() != nil && cmd.Parent().Name() == "mock" { // for downloading mocks
+ cmd.Flags().StringP("path", "p", ".", "Path to local keploy directory where generated mocks are stored")
+ cmd.Flags().StringSliceP("test-sets", "t", utils.Keys(c.cfg.Test.SelectedTests), "Testsets to consider e.g. -t \"test-set-1, test-set-2\"")
+ return nil
+ }
+
+ cmd.Flags().StringSliceP("services", "s", c.cfg.Contract.Services, "Specify the services for which to generate/download contracts")
+ cmd.Flags().StringSliceP("tests", "t", c.cfg.Contract.Tests, "Specify the tests for which to generate/download contracts")
+ cmd.Flags().StringP("path", "p", ".", "Specify the path to generate/download contracts")
+ if cmd.Name() == "download" { // for downloading contracts
+ cmd.Flags().String("driven", c.cfg.Contract.Driven, "Specify the path to download contracts")
+ }
+
+ case "update", "export", "import":
+ return nil
+ case "postman":
+ cmd.Flags().StringP("path", "p", "", "Specify the path to the postman collection")
+ cmd.Flags().String("base-path", c.cfg.Test.BasePath, "basePath to hit the server while importing keploy tests from postman collection with no response in the collection")
+ case "normalize":
+ cmd.Flags().StringP("path", "p", ".", "Path to local directory where generated testcases/mocks/reports are stored")
+ cmd.Flags().String("test-run", "", "Test Run to be normalized")
+ cmd.Flags().String("tests", "", "Test Sets to be normalized")
+ case "config":
+ cmd.Flags().StringP("path", "p", ".", "Path to local directory where generated config is stored")
+ cmd.Flags().Bool("generate", false, "Generate a new keploy configuration file")
+ case "templatize":
+ cmd.Flags().StringP("path", "p", ".", "Path to local directory where generated testcases/mocks are stored")
+ cmd.Flags().StringSliceP("testsets", "t", c.cfg.Templatize.TestSets, "Testsets to run e.g. --testsets \"test-set-1, test-set-2\"")
+ case "gen":
+ cmd.Flags().String("source-file-path", "", "Path to the source file.")
+ cmd.Flags().String("test-file-path", "", "Path to the input test file.")
+ cmd.Flags().String("coverage-report-path", "coverage.xml", "Path to the code coverage report file.")
+ cmd.Flags().String("test-command", "", "The command to run tests and generate coverage report.")
+ cmd.Flags().String("coverage-format", "cobertura", "Type of coverage report.")
+ cmd.Flags().Int("expected-coverage", 80, "The desired coverage percentage.")
+ cmd.Flags().Int("max-iterations", 5, "The maximum number of iterations.")
+ cmd.Flags().String("test-dir", "", "Path to the test directory.")
+ cmd.Flags().String("llm-base-url", "", "Base URL for the AI model.")
+ cmd.Flags().String("model", "gpt-4o", "Model to use for the AI.")
+ cmd.Flags().String("llm-api-version", "", "API version of the llm")
+ cmd.Flags().String("additional-prompt", "", "Additional prompt to be used for the AI model.")
+ cmd.Flags().String("function-under-test", "", "The specific function for which tests will be generated.")
+ cmd.Flags().Bool("flakiness", false, "The flakiness check to run the passed tests for flakiness")
+ err := cmd.MarkFlagRequired("test-command")
+ if err != nil {
+ errMsg := "failed to mark testCommand as required flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ case "record", "test", "rerecord":
+ if cmd.Parent() != nil && cmd.Parent().Name() == "contract" {
+ cmd.Flags().StringSliceP("services", "s", c.cfg.Contract.Services, "Specify the services for which to generate contracts")
+ cmd.Flags().StringP("path", "p", ".", "Specify the path to generate contracts")
+ cmd.Flags().Bool("download", true, "Specify whether to download contracts or not")
+ cmd.Flags().Bool("generate", true, "Specify whether to generate schemas for the current service or not")
+ cmd.Flags().String("driven", c.cfg.Contract.Driven, "Specify the driven flag to validate contracts")
+ return nil
+ }
+
+ cmd.Flags().StringP("path", "p", ".", "Path to local directory where generated testcases/mocks are stored")
+ cmd.Flags().Uint32("proxy-port", c.cfg.ProxyPort, "Port used by the Keploy proxy server to intercept the outgoing dependency calls")
+ cmd.Flags().Uint32("dns-port", c.cfg.DNSPort, "Port used by the Keploy DNS server to intercept the DNS queries")
+ cmd.Flags().StringP("command", "c", c.cfg.Command, "Command to start the user application")
+ cmd.Flags().String("cmd-type", c.cfg.CommandType, "Type of command to start the user application (native/docker/docker-compose)")
+ cmd.Flags().Uint64P("build-delay", "b", c.cfg.BuildDelay, "User provided time to wait docker container build")
+ cmd.Flags().String("container-name", c.cfg.ContainerName, "Name of the application's docker container")
+ cmd.Flags().StringP("network-name", "n", c.cfg.NetworkName, "Name of the application's docker network")
+ cmd.Flags().UintSlice("pass-through-ports", config.GetByPassPorts(c.cfg), "Ports to bypass the proxy server and ignore the traffic")
+ cmd.Flags().Uint64P("app-id", "a", c.cfg.AppID, "A unique name for the user's application")
+ cmd.Flags().String("app-name", c.cfg.AppName, "Name of the user's application")
+ cmd.Flags().Bool("generate-github-actions", c.cfg.GenerateGithubActions, "Generate Github Actions workflow file")
+ cmd.Flags().Bool("in-ci", c.cfg.InCi, "is CI Running or not")
+ //add rest of the uncommon flags for record, test, rerecord commands
+ c.AddUncommonFlags(cmd)
+
+ case "report":
+ cmd.Flags().StringSliceP("test-sets", "t", utils.Keys(c.cfg.Test.SelectedTests), "Testsets to report e.g. --testsets \"test-set-1, test-set-2\"")
+ cmd.Flags().StringP("path", "p", ".", "Path to local directory where generated testcases/mocks are stored")
+
+ case "keploy":
+ cmd.PersistentFlags().Bool("debug", c.cfg.Debug, "Run in debug mode")
+ cmd.PersistentFlags().Bool("disable-tele", c.cfg.DisableTele, "Run in telemetry mode")
+ cmd.PersistentFlags().Bool("disable-ansi", c.cfg.DisableANSI, "Disable ANSI color in logs")
+ err = cmd.PersistentFlags().MarkHidden("disable-tele")
+ if err != nil {
+ errMsg := "failed to mark telemetry as hidden flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ cmd.PersistentFlags().Bool("enable-testing", c.cfg.EnableTesting, "Enable testing keploy with keploy")
+ err = cmd.PersistentFlags().MarkHidden("enable-testing")
+ if err != nil {
+ errMsg := "failed to mark enableTesting as hidden flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ default:
+ return errors.New("unknown command name")
+ }
+
+ return nil
+}
+
+func (c *CmdConfigurator) AddUncommonFlags(cmd *cobra.Command) {
+ switch cmd.Name() {
+ case "record":
+ cmd.Flags().Duration("record-timer", 0, "User provided time to record its application (e.g., \"5s\" for 5 seconds, \"1m\" for 1 minute)")
+ cmd.Flags().String("base-path", c.cfg.Record.BasePath, "Base URL to hit the server while recording the testcases")
+ cmd.Flags().String("metadata", c.cfg.Record.Metadata, "Metadata to be stored in config.yaml as key-value pairs (e.g., \"key1=value1,key2=value2\")")
+ case "test", "rerecord":
+ cmd.Flags().StringSliceP("test-sets", "t", utils.Keys(c.cfg.Test.SelectedTests), "Testsets to run e.g. --testsets \"test-set-1, test-set-2\"")
+ cmd.Flags().String("host", c.cfg.Test.Host, "Custom host to replace the actual host in the testcases")
+ cmd.Flags().Uint32("port", c.cfg.Test.Port, "Custom port to replace the actual port in the testcases")
+ cmd.Flags().Uint64P("delay", "d", 5, "User provided time to run its application")
+ if cmd.Name() == "test" {
+ cmd.Flags().Uint64("api-timeout", c.cfg.Test.APITimeout, "User provided timeout for calling its application")
+ cmd.Flags().String("mongo-password", c.cfg.Test.MongoPassword, "Authentication password for mocking MongoDB conn")
+ cmd.Flags().String("coverage-report-path", c.cfg.Test.CoverageReportPath, "Write a go coverage profile to the file in the given directory.")
+ cmd.Flags().VarP(&c.cfg.Test.Language, "language", "l", "Application programming language")
+ cmd.Flags().Bool("ignore-ordering", c.cfg.Test.IgnoreOrdering, "Ignore ordering of array in response")
+ cmd.Flags().Bool("skip-coverage", c.cfg.Test.SkipCoverage, "skip code coverage computation while running the test cases")
+ cmd.Flags().Bool("remove-unused-mocks", c.cfg.Test.RemoveUnusedMocks, "Clear the unused mocks for the passed test-sets")
+ cmd.Flags().Bool("fallBack-on-miss", c.cfg.Test.FallBackOnMiss, "Enable connecting to actual service if mock not found during test mode")
+ cmd.Flags().String("jacoco-agent-path", c.cfg.Test.JacocoAgentPath, "Only applicable for test coverage for Java projects. You can override the jacoco agent jar by proving its path")
+ cmd.Flags().String("base-path", c.cfg.Test.BasePath, "Custom api basePath/origin to replace the actual basePath/origin in the testcases; App flag is ignored and app will not be started & instrumented when this is set since the application running on a different machine")
+ cmd.Flags().Bool("update-template", c.cfg.Test.UpdateTemplate, "Update the template with the result of the testcases.")
+ cmd.Flags().Bool("mocking", true, "enable/disable mocking for the testcases")
+ cmd.Flags().Bool("disableMockUpload", c.cfg.Test.DisableMockUpload, "Store/Fetch mocks locally")
+ cmd.Flags().Bool("useLocalMock", false, "Use local mocks instead of fetching from the cloud")
+ cmd.Flags().Bool("disable-line-coverage", c.cfg.Test.DisableLineCoverage, "Disable line coverage generation.")
+ cmd.Flags().Bool("must-pass", c.cfg.Test.MustPass, "enforces that the tests must pass, if it doesn't, remove failing testcases")
+ cmd.Flags().Uint32Var(&c.cfg.Test.MaxFailAttempts, "max-fail-attempts", 5, "maximum number of testset failure that can be allowed during must-pass mode")
+ cmd.Flags().Uint32Var(&c.cfg.Test.MaxFlakyChecks, "flaky-check-retry", 1, "maximum number of retries to check for flakiness")
+ }
+ }
+}
+
+func aliasNormalizeFunc(_ *pflag.FlagSet, name string) pflag.NormalizedName {
+ var flagNameMapping = map[string]string{
+ "testsets": "test-sets",
+ "delay": "delay",
+ "apiTimeout": "api-timeout",
+ "mongoPassword": "mongo-password",
+ "coverageReportPath": "coverage-report-path",
+ "language": "language",
+ "ignoreOrdering": "ignore-ordering",
+ "coverage": "coverage",
+ "removeUnusedMocks": "remove-unused-mocks",
+ "goCoverage": "go-coverage",
+ "fallBackOnMiss": "fallBack-on-miss",
+ "basePath": "base-path",
+ "updateTemplate": "update-template",
+ "mocking": "mocking",
+ "sourceFilePath": "source-file-path",
+ "testFilePath": "test-file-path",
+ "testCommand": "test-command",
+ "coverageFormat": "coverage-format",
+ "expectedCoverage": "expected-coverage",
+ "maxIterations": "max-iterations",
+ "testDir": "test-dir",
+ "llmBaseUrl": "llm-base-url",
+ "model": "model",
+ "llmApiVersion": "llm-api-version",
+ "configPath": "config-path",
+ "path": "path",
+ "port": "port",
+ "proxyPort": "proxy-port",
+ "dnsPort": "dns-port",
+ "command": "command",
+ "cmdType": "cmd-type",
+ "buildDelay": "build-delay",
+ "containerName": "container-name",
+ "networkName": "network-name",
+ "passThroughPorts": "pass-through-ports",
+ "appId": "app-id",
+ "appName": "app-name",
+ "generateGithubActions": "generate-github-actions",
+ "disableTele": "disable-tele",
+ "disableANSI": "disable-ansi",
+ "selectedTests": "selected-tests",
+ "testReport": "test-report",
+ "enableTesting": "enable-testing",
+ "inDocker": "in-docker",
+ "keployContainer": "keploy-container",
+ "keployNetwork": "keploy-network",
+ "recordTimer": "record-timer",
+ "urlMethods": "url-methods",
+ "inCi": "in-ci",
+ }
+
+ if newName, ok := flagNameMapping[name]; ok {
+ name = newName
+ }
+ return pflag.NormalizedName(name)
+}
+
+func (c *CmdConfigurator) Validate(ctx context.Context, cmd *cobra.Command) error {
+ err := isCompatible(c.logger)
+ if err != nil {
+ return err
+ }
+ defaultCfg := *c.cfg
+ err = c.PreProcessFlags(cmd)
+ if err != nil {
+ c.logger.Error("failed to preprocess flags", zap.Error(err))
+ return err
+ }
+ err = c.ValidateFlags(ctx, cmd)
+ if err != nil {
+ if err == c.noCommandError() {
+ utils.LogError(c.logger, nil, "missing required -c flag or appCmd in config file")
+ if c.cfg.InDocker {
+ c.logger.Info(`Example usage: keploy test -c "docker run -p 8080:8080 --network myNetworkName myApplicationImageName" --delay 6`)
+ } else {
+ c.logger.Info(LogExample(RootExamples))
+ }
+ }
+ c.logger.Error("failed to validate flags", zap.Error(err))
+ return err
+ }
+
+ appName, err := utils.GetLastDirectory()
+ if err != nil {
+ return fmt.Errorf("failed to get the last directory for appName: %v", err)
+ }
+
+ if c.cfg.AppName == "" {
+ c.logger.Info("Using the last directory name as appName : " + appName)
+ c.cfg.AppName = appName
+ } else if c.cfg.AppName != appName {
+ c.logger.Warn("AppName in config (" + c.cfg.AppName + ") does not match current directory name (" + appName + "). using current directory name as appName")
+ c.cfg.AppName = appName
+ }
+
+ if !IsConfigFileFound {
+ err := c.CreateConfigFile(ctx, defaultCfg)
+ if err != nil {
+ c.logger.Error("failed to create config file", zap.Error(err))
+ return err
+ }
+ }
+ return nil
+}
+
+func (c *CmdConfigurator) PreProcessFlags(cmd *cobra.Command) error {
+ // used to bind common flags for commands like record, test. For eg: PATH, PORT, COMMAND etc.
+ err := viper.BindPFlags(cmd.Flags())
+ if err != nil {
+ errMsg := "failed to bind flags to config"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ // used to bind flags with environment variables
+ viper.AutomaticEnv()
+ viper.SetEnvPrefix("KEPLOY")
+
+ //used to bind flags specific to the command for eg: testsets, delay, recordTimer etc. (nested flags)
+ err = utils.BindFlagsToViper(c.logger, cmd, "")
+ if err != nil {
+ errMsg := "failed to bind cmd specific flags to viper"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ configPath, err := cmd.Flags().GetString("configPath")
+ if err != nil {
+ utils.LogError(c.logger, nil, "failed to read the config path")
+ return err
+ }
+ viper.SetConfigName("keploy")
+ viper.SetConfigType("yml")
+ viper.AddConfigPath(configPath)
+ if err := viper.ReadInConfig(); err != nil {
+ var configFileNotFoundError viper.ConfigFileNotFoundError
+ if !errors.As(err, &configFileNotFoundError) {
+ errMsg := "failed to read config file"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ IsConfigFileFound = false
+ c.logger.Info("config file not found; proceeding with flags only")
+ }
+
+ if err := viper.Unmarshal(c.cfg); err != nil {
+ errMsg := "failed to unmarshal the config"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ c.cfg.ConfigPath = configPath
+ return nil
+}
+func (c *CmdConfigurator) ValidateFlags(ctx context.Context, cmd *cobra.Command) error {
+ disableAnsi, _ := (cmd.Flags().GetBool("disable-ansi"))
+ PrintLogo(os.Stdout, disableAnsi)
+ if c.cfg.Debug {
+ logger, err := log.ChangeLogLevel(zap.DebugLevel)
+ *c.logger = *logger
+ if err != nil {
+ errMsg := "failed to change log level"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ }
+
+ if c.cfg.Record.BasePath != "" {
+ port, err := pkg.ExtractPort(c.cfg.Record.BasePath)
+ if err != nil {
+ errMsg := "failed to extract port from base URL"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.Port = port
+ c.cfg.E2E = true
+ }
+
+ if c.cfg.EnableTesting {
+ // Add mode to logger to debug the keploy during testing
+ logger, err := log.AddMode(cmd.Name())
+ *c.logger = *logger
+ if err != nil {
+ errMsg := "failed to add mode to logger"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.DisableTele = true
+ }
+
+ if c.cfg.DisableANSI {
+ logger, err := log.ChangeColorEncoding()
+ models.IsAnsiDisabled = true
+ *c.logger = *logger
+ if err != nil {
+ errMsg := "failed to change color encoding"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.logger.Info("Color encoding is disabled")
+ }
+
+ c.logger.Debug("config has been initialised", zap.Any("for cmd", cmd.Name()), zap.Any("config", c.cfg))
+
+ switch cmd.Name() {
+
+ case "secure":
+ baseURL, err := cmd.Flags().GetString("base-url")
+ if err != nil {
+ errMsg := "failed to get base-url flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.TestSuite.BaseURL = baseURL
+
+ tsPath, err := cmd.Flags().GetString("ts-path")
+ if err != nil {
+ errMsg := "failed to get ts-path flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.TestSuite.TSPath = tsPath
+
+ file, err := cmd.Flags().GetString("ts-file")
+ if err != nil {
+ errMsg := "failed to get ts-file flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.TestSuite.TSFile = file
+
+ case "load":
+ baseURL, err := cmd.Flags().GetString("base-url")
+ if err != nil {
+ errMsg := "failed to get base-url flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.TestSuite.BaseURL = baseURL
+
+ tsPath, err := cmd.Flags().GetString("ts-path")
+ if err != nil {
+ errMsg := "failed to get ts-path flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.TestSuite.TSPath = tsPath
+
+ file, err := cmd.Flags().GetString("ts-file")
+ if err != nil {
+ errMsg := "failed to get ts-file flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.TestSuite.TSFile = file
+
+ _, err = cmd.Flags().GetInt("vus")
+ if err != nil {
+ errMsg := "failed to get vus flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ _, err = cmd.Flags().GetString("duration")
+ if err != nil {
+ errMsg := "failed to get duration flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ _, err = cmd.Flags().GetInt("rps")
+ if err != nil {
+ errMsg := "failed to get rps flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ case "testsuite":
+ baseURL, err := cmd.Flags().GetString("base-url")
+ if err != nil {
+ errMsg := "failed to get base-url flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.TestSuite.BaseURL = baseURL
+
+ tsPath, err := cmd.Flags().GetString("ts-path")
+ if err != nil {
+ errMsg := "failed to get ts-path flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.TestSuite.TSPath = tsPath
+
+ tsFile, err := cmd.Flags().GetString("ts-file")
+ if err != nil {
+ errMsg := "failed to get ts-file flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.TestSuite.TSFile = tsFile
+ case "upload": //for uploading mocks
+ path, err := cmd.Flags().GetString("path")
+ if err != nil {
+ errMsg := "failed to get the path"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.Path = utils.ToAbsPath(c.logger, path)
+
+ testSets, err := cmd.Flags().GetStringSlice("test-sets")
+ if err != nil {
+ errMsg := "failed to get the test-sets"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ config.SetSelectedTests(c.cfg, testSets)
+
+ case "report":
+ path, err := cmd.Flags().GetString("path")
+ if err != nil {
+ errMsg := "failed to get the path"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.Path = utils.ToAbsPath(c.logger, path)
+
+ testSets, err := cmd.Flags().GetStringSlice("test-sets")
+ if err != nil {
+ errMsg := "failed to get the test-sets"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ config.SetSelectedTestSets(c.cfg, testSets)
+
+ case "generate", "download":
+
+ if cmd.Name() == "download" && cmd.Parent() != nil && cmd.Parent().Name() == "mock" {
+ path, err := cmd.Flags().GetString("path")
+ if err != nil {
+ errMsg := "failed to get the path"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.Path = utils.ToAbsPath(c.logger, path)
+
+ testSets, err := cmd.Flags().GetStringSlice("testsets")
+ if err != nil {
+ errMsg := "failed to get the testsets"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ config.SetSelectedTests(c.cfg, testSets)
+ return nil
+ }
+
+ path, err := cmd.Flags().GetString("path")
+ if err != nil {
+ errMsg := "failed to get the path"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ c.cfg.Contract.Path = utils.ToAbsPath(c.logger, path)
+
+ services, err := cmd.Flags().GetStringSlice("services")
+ if err != nil {
+ errMsg := "failed to get the services"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ config.SetSelectedServices(c.cfg, services)
+
+ selectedTests, err := cmd.Flags().GetStringSlice("tests")
+ if err != nil {
+ errMsg := "failed to get the tests"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ config.SetSelectedContractTests(c.cfg, selectedTests)
+
+ if cmd.Name() == "download" {
+ c.cfg.Contract.Driven, err = cmd.Flags().GetString("driven")
+ if err != nil {
+ errMsg := "failed to get the driven flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ }
+
+ c.cfg.Path = utils.ToAbsPath(c.logger, path)
+
+ case "config":
+ path, err := cmd.Flags().GetString("path")
+ if err != nil {
+ errMsg := "failed to get the path"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.Path, err = utils.GetAbsPath(path)
+ if err != nil {
+ errMsg := "failed to get the absolute path"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ case "record", "test", "rerecord":
+
+ if cmd.Parent() != nil && cmd.Parent().Name() == "contract" {
+ path, err := cmd.Flags().GetString("path")
+ if err != nil {
+ errMsg := "failed to get the path"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ c.cfg.Contract.Path = utils.ToAbsPath(c.logger, path)
+
+ services, err := cmd.Flags().GetStringSlice("services")
+ if err != nil {
+ errMsg := "failed to get the services"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ config.SetSelectedServices(c.cfg, services)
+
+ c.cfg.Contract.Download, err = cmd.Flags().GetBool("download")
+ if err != nil {
+ errMsg := "failed to get the download flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.Contract.Generate, err = cmd.Flags().GetBool("generate")
+ if err != nil {
+ errMsg := "failed to get the generate flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.Contract.Driven, err = cmd.Flags().GetString("driven")
+ if err != nil {
+ errMsg := "failed to get the driven flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ c.cfg.Path = utils.ToAbsPath(c.logger, path)
+ return nil
+ }
+
+ // set the command type
+ c.cfg.CommandType = string(utils.FindDockerCmd(c.cfg.Command))
+
+ // empty the command if base path is provided, because no need of command even if provided
+ if c.cfg.Test.BasePath != "" {
+ c.cfg.CommandType = string(utils.Empty)
+ c.cfg.Command = ""
+ }
+
+ if c.cfg.GenerateGithubActions && utils.CmdType(c.cfg.CommandType) != utils.Empty {
+ defer utils.GenerateGithubActions(c.logger, c.cfg.Command)
+ }
+ if c.cfg.InDocker {
+ c.logger.Info("detected that Keploy is running in a docker container")
+ if len(c.cfg.Path) > 0 {
+ curDir, err := os.Getwd()
+ if err != nil {
+ errMsg := "failed to get current working directory"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ if strings.Contains(c.cfg.Path, "..") {
+
+ c.cfg.Path, err = utils.GetAbsPath(filepath.Clean(c.cfg.Path))
+ if err != nil {
+ return fmt.Errorf("failed to get the absolute path from relative path: %w", err)
+ }
+
+ relativePath, err := filepath.Rel(curDir, c.cfg.Path)
+ if err != nil {
+ errMsg := "failed to get the relative path from absolute path"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ if relativePath == ".." || strings.HasPrefix(relativePath, "../") {
+ errMsg := "path provided is not a subdirectory of current directory. Keploy only supports recording testcases in the current directory or its subdirectories"
+ utils.LogError(c.logger, err, errMsg, zap.String("path:", c.cfg.Path))
+ return errors.New(errMsg)
+ }
+ }
+ }
+ // check if the buildDelay is less than 30 seconds
+ if time.Duration(c.cfg.BuildDelay)*time.Second <= 30*time.Second {
+ c.logger.Warn(fmt.Sprintf("buildDelay is set to %v, incase your docker container takes more time to build use --buildDelay to set custom delay", c.cfg.BuildDelay))
+ c.logger.Info(`Example usage: keploy record -c "docker-compose up --build" --buildDelay 35`)
+ }
+ if utils.CmdType(c.cfg.Command) == utils.DockerCompose {
+ if c.cfg.ContainerName == "" {
+ utils.LogError(c.logger, nil, "Couldn't find containerName")
+ c.logger.Info(`Example usage: keploy record -c "docker run -p 8080:8080 --network myNetworkName myApplicationImageName" --delay 6`)
+ return errors.New("missing required --container-name flag or containerName in config file")
+ }
+ }
+ }
+ err := StartInDocker(ctx, c.logger, c.cfg)
+ if err != nil {
+ return err
+ }
+
+ absPath, err := utils.GetAbsPath(c.cfg.Path)
+ if err != nil {
+ utils.LogError(c.logger, err, "error while getting absolute path")
+ return errors.New("failed to get the absolute path")
+ }
+ c.cfg.Path = absPath + "/keploy"
+
+ // handle the app command
+ if c.cfg.Command == "" {
+ if !alreadyRunning(cmd.Name(), c.cfg.Test.BasePath) {
+ return c.noCommandError()
+ }
+ }
+
+ bypassPorts, err := cmd.Flags().GetUintSlice("passThroughPorts")
+ if err != nil {
+ errMsg := "failed to read the ports of outgoing calls to be ignored"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ config.SetByPassPorts(c.cfg, bypassPorts)
+
+ if cmd.Name() == "record" {
+ metadata, err := cmd.Flags().GetString("metadata")
+ if err != nil {
+ errMsg := "failed to get the metadata flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.Record.Metadata = metadata
+ }
+
+ if cmd.Name() == "test" || cmd.Name() == "rerecord" {
+ //check if the keploy folder exists
+ if _, err := os.Stat(c.cfg.Path); os.IsNotExist(err) {
+ recordCmd := models.HighlightGrayString("keploy record")
+ errMsg := fmt.Sprintf("No test-sets found. Please record testcases using %s command", recordCmd)
+ utils.LogError(c.logger, nil, errMsg)
+ return errors.New(errMsg)
+ }
+
+ testSets, err := cmd.Flags().GetStringSlice("testsets")
+ if err != nil {
+ errMsg := "failed to get the testsets"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ config.SetSelectedTests(c.cfg, testSets)
+ if cmd.Name() == "rerecord" {
+ c.cfg.Test.SkipCoverage = true
+ host, err := cmd.Flags().GetString("host")
+ if err != nil {
+ errMsg := "failed to get the provided host"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.ReRecord.Host = host
+ port, err := cmd.Flags().GetUint32("port")
+ if err != nil {
+ errMsg := "failed to get the provided port"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ c.cfg.ReRecord.Port = port
+ c.cfg.Test.Delay, err = cmd.Flags().GetUint64("delay")
+ if err != nil {
+ errMsg := "failed to get the provided delay"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ return nil
+ }
+
+ // enforce that the test-sets are provided when --must-pass is set to true
+ // to prevent accidental deletion of failed testcases in testsets which was due to application changes
+ // and not due to flakiness or our internal issue.
+ mustPass, err := cmd.Flags().GetBool("must-pass")
+ if err != nil {
+ errMsg := "failed to get the must-pass flag"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ if mustPass {
+ c.cfg.Test.SkipCoverage = true
+ c.cfg.Test.DisableMockUpload = true
+ }
+
+ // in mustpass mode, set the maxFlakyChecks count to 3 explicitly,
+ // if it is not set through cmd flag.
+ if mustPass && !cmd.Flags().Changed("flaky-check-retry") {
+ c.cfg.Test.MaxFlakyChecks = 3
+ }
+
+ // if the user passes a value for this field, store it
+ if cmd.Flags().Changed("flaky-check-retry") {
+ c.cfg.Test.MaxFlakyChecks, err = cmd.Flags().GetUint32("flaky-check-retry")
+ if err != nil {
+ errMsg := "failed to get the provided flaky-check-retry count"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ }
+
+ // if the user passes a value for this field, store it
+ if cmd.Flags().Changed("max-fail-attempts") {
+ c.cfg.Test.MaxFailAttempts, err = cmd.Flags().GetUint32("max-fail-attempts")
+ if err != nil {
+ errMsg := "failed to get the provided max-fail-attempts count"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ }
+
+ // don't allow zero maxFlakyChecks and if must pass mode is enabled, then maxFailAttempts can't be zero.
+ if c.cfg.Test.MaxFlakyChecks == 0 {
+ return fmt.Errorf("value for maxFlakyChecks cannot be zero")
+ }
+ if mustPass && c.cfg.Test.MaxFailAttempts == 0 {
+ return fmt.Errorf("in must pass mode, value for maxFailAttempts cannot be zero")
+ }
+
+ if mustPass && !cmd.Flags().Changed("test-sets") {
+ return fmt.Errorf("--test-sets flag must be set to use --must-pass=true")
+ }
+
+ // skip coverage by default if command is of type docker
+ if utils.CmdType(c.cfg.CommandType) != "native" && !cmd.Flags().Changed("skip-coverage") {
+ c.cfg.Test.SkipCoverage = true
+ }
+
+ if c.cfg.Test.Delay <= 5 {
+ c.logger.Warn(fmt.Sprintf("Delay is set to %d seconds, incase your app takes more time to start use --delay to set custom delay", c.cfg.Test.Delay))
+ if c.cfg.InDocker {
+ c.logger.Info(`Example usage: keploy test -c "docker run -p 8080:8080 --network myNetworkName myApplicationImageName" --delay 6`)
+ } else {
+ c.logger.Info("Example usage: " + cmd.Example)
+ }
+ }
+ }
+
+ case "normalize":
+ c.cfg.Path = utils.ToAbsPath(c.logger, c.cfg.Path)
+ tests, err := cmd.Flags().GetString("tests")
+ if err != nil {
+ errMsg := "failed to read tests to be normalized"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+ err = config.SetSelectedTestsNormalize(c.cfg, tests)
+ if err != nil {
+ errMsg := "failed to normalize the selected tests"
+ utils.LogError(c.logger, err, errMsg)
+ return errors.New(errMsg)
+ }
+
+ case "templatize":
+ c.cfg.Path = utils.ToAbsPath(c.logger, c.cfg.Path)
+ case "gen":
+ if os.Getenv("API_KEY") == "" {
+ utils.LogError(c.logger, nil, "API_KEY is not set")
+ return errors.New("API_KEY is not set")
+ }
+ if (c.cfg.Gen.SourceFilePath == "" && c.cfg.Gen.TestFilePath != "") || c.cfg.Gen.SourceFilePath != "" && c.cfg.Gen.TestFilePath == "" {
+ utils.LogError(c.logger, nil, "One of the SourceFilePath and TestFilePath is mentioned. Either provide both or neither")
+ return errors.New("sourceFilePath and testFilePath misconfigured")
+ } else if c.cfg.Gen.SourceFilePath == "" && c.cfg.Gen.TestFilePath == "" {
+ if c.cfg.Gen.TestDir == "" {
+ utils.LogError(c.logger, nil, "TestDir is not set, Please specify the test directory")
+ return errors.New("TestDir is not set")
+ }
+ }
+ }
+
+ return nil
+}
+
+func (c *CmdConfigurator) CreateConfigFile(ctx context.Context, defaultCfg config.Config) error {
+ defaultCfg = c.UpdateConfigData(defaultCfg)
+ toolSvc := tools.NewTools(c.logger, nil, nil, nil, nil, nil)
+ configData := defaultCfg
+ configDataBytes, err := yaml.Marshal(configData)
+ if err != nil {
+ utils.LogError(c.logger, err, "failed to marshal config data")
+ return errors.New("failed to marshal config data")
+ }
+ err = toolSvc.CreateConfig(ctx, c.cfg.ConfigPath+"/keploy.yml", string(configDataBytes))
+ if err != nil {
+ utils.LogError(c.logger, err, "failed to create config file")
+ return errors.New("failed to create config file")
+ }
+ c.logger.Info("Generated config file based on the flags that are used")
+ return nil
+}
+
+func (c *CmdConfigurator) UpdateConfigData(defaultCfg config.Config) config.Config {
+ defaultCfg.Command = c.cfg.Command
+ defaultCfg.Test.Delay = c.cfg.Test.Delay
+ defaultCfg.AppName = c.cfg.AppName
+ defaultCfg.Test.APITimeout = c.cfg.Test.APITimeout
+ defaultCfg.ContainerName = c.cfg.ContainerName
+ defaultCfg.Test.IgnoreOrdering = c.cfg.Test.IgnoreOrdering
+ defaultCfg.Test.Language = c.cfg.Test.Language
+ defaultCfg.DisableANSI = c.cfg.DisableANSI
+ defaultCfg.Test.SkipCoverage = c.cfg.Test.SkipCoverage
+ defaultCfg.Test.Mocking = c.cfg.Test.Mocking
+ defaultCfg.Test.DisableLineCoverage = c.cfg.Test.DisableLineCoverage
+
+ defaultCfg.TestSuite.TSPath = c.cfg.TestSuite.TSPath
+ defaultCfg.TestSuite.TSFile = c.cfg.TestSuite.TSFile
+ return defaultCfg
+}
diff --git a/keploy/cli/provider/common.go b/keploy/cli/provider/common.go
new file mode 100644
index 0000000..807d8da
--- /dev/null
+++ b/keploy/cli/provider/common.go
@@ -0,0 +1,20 @@
+package provider
+
+import (
+ "go.keploy.io/server/v2/pkg/models"
+ "go.keploy.io/server/v2/pkg/platform/storage"
+ "go.keploy.io/server/v2/pkg/platform/yaml/configdb/testset"
+ mockdb "go.keploy.io/server/v2/pkg/platform/yaml/mockdb"
+ openapidb "go.keploy.io/server/v2/pkg/platform/yaml/openapidb"
+ reportdb "go.keploy.io/server/v2/pkg/platform/yaml/reportdb"
+ testdb "go.keploy.io/server/v2/pkg/platform/yaml/testdb"
+)
+
+type commonPlatformServices struct {
+ YamlTestDB *testdb.TestYaml
+ YamlMockDb *mockdb.MockYaml
+ YamlOpenAPIDb *openapidb.OpenAPIYaml
+ YamlReportDb *reportdb.TestReport
+ YamlTestSetDB *testset.Db[*models.TestSet]
+ Storage *storage.Storage
+}
diff --git a/keploy/cli/provider/compat_linux.go b/keploy/cli/provider/compat_linux.go
new file mode 100644
index 0000000..1951cd5
--- /dev/null
+++ b/keploy/cli/provider/compat_linux.go
@@ -0,0 +1,27 @@
+//go:build linux
+
+package provider
+
+import (
+ "errors"
+
+ "github.com/moby/moby/pkg/parsers/kernel"
+ "go.uber.org/zap"
+)
+
+func isCompatible(logger *zap.Logger) error {
+ //check if the version of the kernel is above 5.10 for eBPF support
+ isValid := kernel.CheckKernelVersion(5, 10, 0)
+ if !isValid {
+ c, err := kernel.GetKernelVersion()
+ if err != nil {
+ logger.Error("Error getting kernel version", zap.Error(err))
+ return err
+ }
+ errMsg := "detected linux kernel version" + c.String() + ". Keploy requires linux kernel version 5.10 or above. Please upgrade your kernel or docker version.\n"
+ logger.Error(errMsg)
+ return errors.New(errMsg)
+ }
+ // TODO check for cgroup v2 support
+ return nil
+}
diff --git a/keploy/cli/provider/compat_others.go b/keploy/cli/provider/compat_others.go
new file mode 100644
index 0000000..5d47879
--- /dev/null
+++ b/keploy/cli/provider/compat_others.go
@@ -0,0 +1,11 @@
+//go:build !linux
+
+// This is a placeholder file for other OSes.
+
+package provider
+
+import "go.uber.org/zap"
+
+func isCompatible(logger *zap.Logger) error {
+ return nil
+}
diff --git a/keploy/cli/provider/core_service_linux.go b/keploy/cli/provider/core_service_linux.go
new file mode 100644
index 0000000..3f1773c
--- /dev/null
+++ b/keploy/cli/provider/core_service_linux.go
@@ -0,0 +1,125 @@
+//go:build linux
+
+package provider
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "path/filepath"
+
+ "go.keploy.io/server/v2/config"
+ "go.keploy.io/server/v2/pkg/core"
+ "go.keploy.io/server/v2/pkg/core/hooks"
+ "go.keploy.io/server/v2/pkg/core/proxy"
+ "go.keploy.io/server/v2/pkg/core/tester"
+ "go.keploy.io/server/v2/pkg/models"
+ "go.keploy.io/server/v2/pkg/platform/docker"
+ "go.keploy.io/server/v2/pkg/platform/storage"
+ "go.keploy.io/server/v2/pkg/platform/telemetry"
+ "go.keploy.io/server/v2/pkg/platform/yaml/configdb/testset"
+ mockdb "go.keploy.io/server/v2/pkg/platform/yaml/mockdb"
+ openapidb "go.keploy.io/server/v2/pkg/platform/yaml/openapidb"
+ reportdb "go.keploy.io/server/v2/pkg/platform/yaml/reportdb"
+ testdb "go.keploy.io/server/v2/pkg/platform/yaml/testdb"
+ "go.keploy.io/server/v2/pkg/service"
+ "go.keploy.io/server/v2/pkg/service/contract"
+ "go.keploy.io/server/v2/pkg/service/orchestrator"
+ "go.keploy.io/server/v2/pkg/service/record"
+ "go.keploy.io/server/v2/pkg/service/replay"
+ "go.keploy.io/server/v2/pkg/service/report"
+ "go.keploy.io/server/v2/pkg/service/tools"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+type CommonInternalService struct {
+ commonPlatformServices
+ Instrumentation *core.Core
+}
+
+func Get(ctx context.Context, cmd string, cfg *config.Config, logger *zap.Logger, tel *telemetry.Telemetry, auth service.Auth) (interface{}, error) {
+ commonServices, err := GetCommonServices(ctx, cfg, logger)
+ if err != nil {
+ return nil, err
+ }
+ contractSvc := contract.New(logger, commonServices.YamlTestDB, commonServices.YamlMockDb, commonServices.YamlOpenAPIDb, cfg)
+ recordSvc := record.New(logger, commonServices.YamlTestDB, commonServices.YamlMockDb, tel, commonServices.Instrumentation, commonServices.YamlTestSetDB, cfg)
+ replaySvc := replay.NewReplayer(logger, commonServices.YamlTestDB, commonServices.YamlMockDb, commonServices.YamlReportDb, commonServices.YamlTestSetDB, tel, commonServices.Instrumentation, auth, commonServices.Storage, cfg)
+ toolsSvc := tools.NewTools(logger, commonServices.YamlTestSetDB, commonServices.YamlTestDB, tel, auth, cfg)
+ reportSvc := report.New(logger, cfg, commonServices.YamlReportDb, commonServices.YamlTestDB)
+ switch cmd {
+ case "rerecord":
+ return orchestrator.New(logger, recordSvc, toolsSvc, replaySvc, cfg), nil
+ case "record":
+ return recordSvc, nil
+ case "test", "normalize", "mock":
+ return replaySvc, nil
+ case "templatize", "config", "update", "login", "export", "import":
+ return toolsSvc, nil
+ case "contract":
+ return contractSvc, nil
+ case "report":
+ return reportSvc, nil
+ default:
+ return nil, errors.New("invalid command")
+ }
+
+}
+
+func GetCommonServices(_ context.Context, c *config.Config, logger *zap.Logger) (*CommonInternalService, error) {
+
+ h := hooks.NewHooks(logger, c)
+ p := proxy.New(logger, h, c)
+ //for keploy test bench
+ t := tester.New(logger, h)
+
+ var client docker.Client
+ var err error
+ if utils.IsDockerCmd(utils.CmdType(c.CommandType)) {
+ client, err = docker.New(logger)
+ if err != nil {
+ utils.LogError(logger, err, "failed to create docker client")
+ }
+
+ //parse docker command only in case of docker start or docker run commands
+ if utils.CmdType(c.CommandType) != utils.DockerCompose {
+ cont, net, err := docker.ParseDockerCmd(c.Command, utils.CmdType(c.CommandType), client)
+ logger.Debug("container and network parsed from command", zap.String("container", cont), zap.String("network", net), zap.String("command", c.Command))
+ if err != nil {
+ utils.LogError(logger, err, "failed to parse container name from given docker command", zap.String("cmd", c.Command))
+ }
+ if c.ContainerName != "" && c.ContainerName != cont {
+ logger.Warn(fmt.Sprintf("given app container:(%v) is different from parsed app container:(%v), taking parsed value", c.ContainerName, cont))
+ }
+ c.ContainerName = cont
+
+ if c.NetworkName != "" && c.NetworkName != net {
+ logger.Warn(fmt.Sprintf("given docker network:(%v) is different from parsed docker network:(%v), taking parsed value", c.NetworkName, net))
+ }
+ c.NetworkName = net
+
+ logger.Debug("Using container and network", zap.String("container", c.ContainerName), zap.String("network", c.NetworkName))
+ }
+ }
+
+ instrumentation := core.New(logger, h, p, t, client)
+
+ testDB := testdb.New(logger, c.Path)
+ mockDB := mockdb.New(logger, c.Path, "")
+ openAPIdb := openapidb.New(logger, filepath.Join(c.Path, "schema"))
+ reportDB := reportdb.New(logger, c.Path+"/reports")
+ testSetDb := testset.New[*models.TestSet](logger, c.Path)
+ storage := storage.New(c.APIServerURL, logger)
+ return &CommonInternalService{
+ commonPlatformServices{
+ YamlTestDB: testDB,
+ YamlMockDb: mockDB,
+ YamlOpenAPIDb: openAPIdb,
+ YamlReportDb: reportDB,
+ YamlTestSetDB: testSetDb,
+ Storage: storage,
+ },
+ instrumentation,
+ }, nil
+}
diff --git a/keploy/cli/provider/core_service_others.go b/keploy/cli/provider/core_service_others.go
new file mode 100644
index 0000000..4b982aa
--- /dev/null
+++ b/keploy/cli/provider/core_service_others.go
@@ -0,0 +1,83 @@
+//go:build !linux
+
+package provider
+
+import (
+ "context"
+ "errors"
+
+ "go.keploy.io/server/v2/config"
+ "go.keploy.io/server/v2/pkg/core"
+ "go.keploy.io/server/v2/pkg/models"
+ "go.keploy.io/server/v2/pkg/platform/storage"
+ "go.keploy.io/server/v2/pkg/platform/telemetry"
+ "go.keploy.io/server/v2/pkg/platform/yaml/configdb/testset"
+ mockdb "go.keploy.io/server/v2/pkg/platform/yaml/mockdb"
+ openapidb "go.keploy.io/server/v2/pkg/platform/yaml/openapidb"
+ reportdb "go.keploy.io/server/v2/pkg/platform/yaml/reportdb"
+ testdb "go.keploy.io/server/v2/pkg/platform/yaml/testdb"
+
+ "go.keploy.io/server/v2/pkg/service"
+ "go.keploy.io/server/v2/pkg/service/contract"
+ "go.keploy.io/server/v2/pkg/service/replay"
+ "go.keploy.io/server/v2/pkg/service/report"
+ "go.keploy.io/server/v2/pkg/service/tools"
+ "go.uber.org/zap"
+)
+
+type CommonInternalService struct {
+ commonPlatformServices
+ Instrumentation *core.Core
+}
+
+func Get(ctx context.Context, cmd string, c *config.Config, logger *zap.Logger, tel *telemetry.Telemetry, auth service.Auth) (interface{}, error) {
+ commonServices, err := GetCommonServices(ctx, c, logger)
+ if err != nil {
+ return nil, err
+ }
+ contractSvc := contract.New(logger, commonServices.YamlTestDB, commonServices.YamlMockDb, commonServices.YamlOpenAPIDb, c)
+
+ replaySvc := replay.NewReplayer(logger, commonServices.YamlTestDB, commonServices.YamlMockDb, commonServices.YamlReportDb, commonServices.YamlTestSetDB, tel, commonServices.Instrumentation, auth, commonServices.Storage, c)
+
+ toolsSvc := tools.NewTools(logger, commonServices.YamlTestSetDB, commonServices.YamlTestDB, tel, auth, c)
+ reportSvc := report.New(logger, c, commonServices.YamlReportDb, commonServices.YamlTestDB)
+
+ if (cmd == "test" && c.Test.BasePath != "") || cmd == "normalize" || cmd == "mock" {
+ return replaySvc, nil
+ }
+
+ if cmd == "templatize" || cmd == "config" || cmd == "update" || cmd == "login" || cmd == "export" || cmd == "import" {
+ return toolsSvc, nil
+ }
+
+ if cmd == "contract" {
+ return contractSvc, nil
+ }
+
+ if cmd == "report" {
+ return reportSvc, nil
+ }
+
+ return nil, errors.New("command not supported in non linux os. if you are on windows or mac, please use the dockerized version of your application")
+}
+
+func GetCommonServices(_ context.Context, c *config.Config, logger *zap.Logger) (*CommonInternalService, error) {
+ instrumentation := core.New(logger)
+ testDB := testdb.New(logger, c.Path)
+ mockDB := mockdb.New(logger, c.Path, "")
+ openAPIdb := openapidb.New(logger, c.Path)
+ reportDB := reportdb.New(logger, c.Path+"/reports")
+ testSetDb := testset.New[*models.TestSet](logger, c.Path)
+ storage := storage.New(c.APIServerURL, logger)
+ return &CommonInternalService{
+ commonPlatformServices{
+ YamlTestDB: testDB,
+ YamlMockDb: mockDB,
+ YamlOpenAPIDb: openAPIdb,
+ YamlReportDb: reportDB,
+ YamlTestSetDB: testSetDb,
+ Storage: storage,
+ },
+ instrumentation,
+ }, nil
+}
diff --git a/keploy/cli/provider/docker.go b/keploy/cli/provider/docker.go
new file mode 100644
index 0000000..be16a52
--- /dev/null
+++ b/keploy/cli/provider/docker.go
@@ -0,0 +1,297 @@
+package provider
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "os"
+ "os/exec"
+ "runtime"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "github.com/docker/docker/api/types"
+ "go.keploy.io/server/v2/config"
+ "go.keploy.io/server/v2/pkg/platform/docker"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+ "golang.org/x/term"
+)
+
+type DockerConfigStruct struct {
+ DockerImage string
+ Envs map[string]string
+}
+
+var DockerConfig = DockerConfigStruct{
+ DockerImage: "ghcr.io/keploy/keploy",
+}
+
+func GenerateDockerEnvs(config DockerConfigStruct) string {
+ var envs []string
+ for key, value := range config.Envs {
+ if runtime.GOOS == "windows" {
+ envs = append(envs, fmt.Sprintf("-e %s=%s", key, value))
+ } else {
+ envs = append(envs, fmt.Sprintf("-e %s='%s'", key, value))
+ }
+ }
+ return strings.Join(envs, " ")
+}
+
+// StartInDocker will check if the docker command is provided as an input
+// then start the Keploy as a docker container and run the command
+// should also return a boolean if the execution is moved to docker
+func StartInDocker(ctx context.Context, logger *zap.Logger, conf *config.Config) error {
+
+ if DockerConfig.Envs == nil {
+ DockerConfig.Envs = map[string]string{
+ "INSTALLATION_ID": conf.InstallationID,
+ }
+ } else {
+ DockerConfig.Envs["INSTALLATION_ID"] = conf.InstallationID
+ }
+
+ //Check if app command starts with docker or docker-compose.
+ // If it does, then we would run the docker version of keploy and
+ // pass the command and control to it.
+ cmdType := utils.FindDockerCmd(conf.Command)
+ if conf.InDocker || !(utils.IsDockerCmd(cmdType)) {
+ return nil
+ }
+ // pass the all the commands and args to the docker version of Keploy
+ err := RunInDocker(ctx, logger)
+ if err != nil {
+ utils.LogError(logger, err, "failed to run the command in docker")
+ return err
+ }
+ // gracefully exit the current process
+ logger.Info("exiting the current process as the command is moved to docker")
+
+ if utils.LogFile != nil {
+ err := utils.LogFile.Close()
+ if err != nil {
+ utils.LogError(logger, err, "Failed to close Keploy Logs")
+ }
+ if err := utils.DeleteFileIfNotExists(logger, "keploy-logs.txt"); err != nil {
+ return nil
+ }
+ if err := utils.DeleteFileIfNotExists(logger, "docker-compose-tmp.yaml"); err != nil {
+ return nil
+ }
+ }
+
+ os.Exit(0)
+ return nil
+}
+
+func RunInDocker(ctx context.Context, logger *zap.Logger) error {
+ //Get the correct keploy alias.
+ keployAlias, err := getAlias(ctx, logger)
+ if err != nil {
+ return err
+ }
+
+ var quotedArgs []string
+
+ for _, arg := range os.Args[1:] {
+ quotedArgs = append(quotedArgs, strconv.Quote(arg))
+ }
+ client, err := docker.New(logger)
+ if err != nil {
+ utils.LogError(logger, err, "failed to initalise docker")
+ return err
+ }
+ addKeployNetwork(ctx, logger, client)
+ err = client.CreateVolume(ctx, "debugfs", true)
+ if err != nil {
+ utils.LogError(logger, err, "failed to debugfs volume")
+ return err
+ }
+
+ var cmd *exec.Cmd
+
+ // Detect the operating system
+ if runtime.GOOS == "windows" {
+ var args []string
+ args = append(args, "/C")
+ args = append(args, strings.Split(keployAlias, " ")...)
+ args = append(args, os.Args[1:]...)
+ // Use cmd.exe /C for Windows
+ cmd = exec.CommandContext(
+ ctx,
+ "cmd.exe",
+ args...,
+ )
+ } else {
+ // Use sh -c for Unix-like systems
+ cmd = exec.CommandContext(
+ ctx,
+ "sh",
+ "-c",
+ keployAlias+" "+strings.Join(quotedArgs, " "),
+ )
+ }
+
+ cmd.Cancel = func() error {
+ err := utils.SendSignal(logger, -cmd.Process.Pid, syscall.SIGINT)
+ if err != nil {
+ utils.LogError(logger, err, "failed to start stop docker")
+ return err
+ }
+ return nil
+ }
+
+ cmd.Stdout = os.Stdout
+ cmd.Stdin = os.Stdin
+ cmd.Stderr = os.Stderr
+
+ logger.Debug("running the following command in docker", zap.String("command", cmd.String()))
+ err = cmd.Run()
+ if err != nil {
+ if ctx.Err() == context.Canceled {
+ return ctx.Err()
+ }
+ utils.LogError(logger, err, "failed to start keploy in docker")
+ return err
+ }
+ return nil
+}
+
+func getAlias(ctx context.Context, logger *zap.Logger) (string, error) {
+ // Get the name of the operating system.
+ osName := runtime.GOOS
+ //TODO: configure the hardcoded port mapping
+ img := DockerConfig.DockerImage + ":v" + utils.Version
+ logger.Info("Starting keploy in docker with image", zap.String("image:", img))
+ envs := GenerateDockerEnvs(DockerConfig)
+ if envs != "" {
+ envs = envs + " "
+ }
+ var ttyFlag string
+
+ if term.IsTerminal(int(os.Stdin.Fd())) {
+ ttyFlag = " -it "
+ } else {
+ ttyFlag = " "
+ }
+
+ switch osName {
+ case "linux":
+ alias := "sudo docker container run --name keploy-v2 " + envs + "-e BINARY_TO_DOCKER=true -p 16789:16789 --privileged --pid=host" + ttyFlag + " -v " + os.Getenv("PWD") + ":" + os.Getenv("PWD") + " -w " + os.Getenv("PWD") + " -v /sys/fs/cgroup:/sys/fs/cgroup -v /sys/kernel/debug:/sys/kernel/debug -v /sys/fs/bpf:/sys/fs/bpf -v /var/run/docker.sock:/var/run/docker.sock -v " + os.Getenv("HOME") + "/.keploy-config:/root/.keploy-config -v " + os.Getenv("HOME") + "/.keploy:/root/.keploy --rm " + img
+ return alias, nil
+ case "windows":
+ // Get the current working directory
+ pwd, err := os.Getwd()
+ if err != nil {
+ utils.LogError(logger, err, "failed to get the current working directory")
+ }
+ dpwd := convertPathToUnixStyle(pwd)
+ cmd := exec.CommandContext(ctx, "docker", "context", "ls", "--format", "{{.Name}}\t{{.Current}}")
+ out, err := cmd.Output()
+ if err != nil {
+ utils.LogError(logger, err, "failed to get the current docker context")
+ return "", errors.New("failed to get alias")
+ }
+ dockerContext := strings.Split(strings.TrimSpace(string(out)), "\n")[0]
+ if len(dockerContext) == 0 {
+ utils.LogError(logger, nil, "failed to get the current docker context")
+ return "", errors.New("failed to get alias")
+ }
+ dockerContext = strings.Split(dockerContext, "\n")[0]
+ if dockerContext == "colima" {
+ logger.Info("Starting keploy in docker with colima context, as that is the current context.")
+ alias := "docker container run --name keploy-v2 " + envs + "-e BINARY_TO_DOCKER=true -p 16789:16789 --privileged --pid=host" + ttyFlag + "-v " + pwd + ":" + dpwd + " -w " + dpwd + " -v /sys/fs/cgroup:/sys/fs/cgroup -v /sys/kernel/debug:/sys/kernel/debug -v /sys/fs/bpf:/sys/fs/bpf -v /var/run/docker.sock:/var/run/docker.sock -v " + os.Getenv("USERPROFILE") + "\\.keploy-config:/root/.keploy-config -v " + os.Getenv("USERPROFILE") + "\\.keploy:/root/.keploy --rm " + img
+ return alias, nil
+ }
+ // if default docker context is used
+ logger.Info("Starting keploy in docker with default context, as that is the current context.")
+ alias := "docker container run --name keploy-v2 " + envs + "-e BINARY_TO_DOCKER=true -p 16789:16789 --privileged --pid=host" + ttyFlag + "-v " + pwd + ":" + dpwd + " -w " + dpwd + " -v /sys/fs/cgroup:/sys/fs/cgroup -v debugfs:/sys/kernel/debug:rw -v /sys/fs/bpf:/sys/fs/bpf -v /var/run/docker.sock:/var/run/docker.sock -v " + os.Getenv("USERPROFILE") + "\\.keploy-config:/root/.keploy-config -v " + os.Getenv("USERPROFILE") + "\\.keploy:/root/.keploy --rm " + img
+ return alias, nil
+ case "darwin":
+ // Get the context and docker daemon endpoint.
+ cmd := exec.CommandContext(ctx, "docker", "context", "inspect", "--format", "{{if .Metadata}}Name={{.Name}} {{end}}{{if .Endpoints.docker}}Endpoint={{.Endpoints.docker.Host}}{{end}}")
+ out, err := cmd.Output()
+ if err != nil {
+ utils.LogError(logger, err, "failed to inspect the docker context")
+ return "", errors.New("failed to get alias")
+ }
+
+ output := strings.TrimSpace(string(out))
+ var currentContext, dockerEndpoint string
+
+ // Parse the output for current context and endpoint
+ for _, part := range strings.Fields(output) {
+ if strings.HasPrefix(part, "Name=") {
+ currentContext = strings.TrimPrefix(part, "Name=")
+ } else if strings.HasPrefix(part, "Endpoint=") {
+ dockerEndpoint = strings.TrimPrefix(part, "Endpoint=")
+ }
+ }
+
+ // Check if we found a current context
+ if currentContext == "" {
+ utils.LogError(logger, nil, "failed to find the current docker context")
+ return "", errors.New("failed to get alias")
+ }
+
+ // Construct the alias command based on context-specific `debugfs` mount
+ var alias string
+ if currentContext == "colima" {
+
+ // To allow docker client to connect to the colima daemon because by default it uses the default docker daemon
+ err := os.Setenv("DOCKER_HOST", dockerEndpoint)
+ if err != nil {
+ utils.LogError(logger, err, "failed to set DOCKER_HOST environment variable for colima context")
+ return "", errors.New("failed to get alias")
+ }
+ logger.Info("Starting keploy in docker with colima context, as that is the current context.")
+ alias := "docker container run --name keploy-v2 " + envs + "-e BINARY_TO_DOCKER=true -p 16789:16789 --privileged --pid=host" + ttyFlag + "-v " + os.Getenv("PWD") + ":" + os.Getenv("PWD") + " -w " + os.Getenv("PWD") + " -v /sys/fs/cgroup:/sys/fs/cgroup -v /sys/kernel/debug:/sys/kernel/debug -v /sys/fs/bpf:/sys/fs/bpf -v /var/run/docker.sock:/var/run/docker.sock -v " + os.Getenv("HOME") + "/.keploy-config:/root/.keploy-config -v " + os.Getenv("HOME") + "/.keploy:/root/.keploy --rm " + img
+ return alias, nil
+ }
+ // if default docker context is used
+ logger.Info("Starting keploy in docker with default context, as that is the current context.")
+ alias = "docker container run --name keploy-v2 " + envs + "-e BINARY_TO_DOCKER=true -p 16789:16789 --privileged --pid=host" + ttyFlag + "-v " + os.Getenv("PWD") + ":" + os.Getenv("PWD") + " -w " + os.Getenv("PWD") + " -v /sys/fs/cgroup:/sys/fs/cgroup -v debugfs:/sys/kernel/debug:rw -v /sys/fs/bpf:/sys/fs/bpf -v /var/run/docker.sock:/var/run/docker.sock -v " + os.Getenv("HOME") + "/.keploy-config:/root/.keploy-config -v " + os.Getenv("HOME") + "/.keploy:/root/.keploy --rm " + img
+ return alias, nil
+ }
+ return "", errors.New("failed to get alias")
+}
+
+func addKeployNetwork(ctx context.Context, logger *zap.Logger, client docker.Client) {
+
+ // Check if the 'keploy-network' network exists
+ networks, err := client.NetworkList(ctx, types.NetworkListOptions{})
+ if err != nil {
+ logger.Debug("failed to list docker networks")
+ return
+ }
+
+ for _, network := range networks {
+ if network.Name == "keploy-network" {
+ logger.Debug("keploy network already exists")
+ return
+ }
+ }
+
+ // Create the 'keploy' network if it doesn't exist
+ _, err = client.NetworkCreate(ctx, "keploy-network", types.NetworkCreate{
+ CheckDuplicate: true,
+ })
+ if err != nil {
+ logger.Debug("failed to create keploy network")
+ return
+ }
+
+ logger.Debug("keploy network created")
+}
+
+func convertPathToUnixStyle(path string) string {
+ // Replace backslashes with forward slashes
+ unixPath := strings.ReplaceAll(path, "\\", "/")
+ // Remove 'C:'
+ if len(unixPath) > 1 && unixPath[1] == ':' {
+ unixPath = unixPath[2:]
+ }
+ return unixPath
+}
diff --git a/keploy/cli/provider/service.go b/keploy/cli/provider/service.go
new file mode 100644
index 0000000..1a38790
--- /dev/null
+++ b/keploy/cli/provider/service.go
@@ -0,0 +1,60 @@
+package provider
+
+import (
+ "context"
+ "errors"
+ "sync"
+
+ "go.keploy.io/server/v2/config"
+ "go.keploy.io/server/v2/pkg/platform/telemetry"
+ "go.keploy.io/server/v2/pkg/service"
+ "go.keploy.io/server/v2/utils"
+
+ "go.keploy.io/server/v2/pkg/service/load"
+ "go.keploy.io/server/v2/pkg/service/secure"
+ "go.keploy.io/server/v2/pkg/service/testsuite"
+ "go.keploy.io/server/v2/pkg/service/utgen"
+ "go.uber.org/zap"
+)
+
+var TeleGlobalMap sync.Map
+
+type ServiceProvider struct {
+ logger *zap.Logger
+ cfg *config.Config
+ auth service.Auth
+}
+
+func NewServiceProvider(logger *zap.Logger, cfg *config.Config, auth service.Auth) *ServiceProvider {
+ return &ServiceProvider{
+ logger: logger,
+ cfg: cfg,
+ auth: auth,
+ }
+}
+
+func (n *ServiceProvider) GetService(ctx context.Context, cmd string) (interface{}, error) {
+
+ tel := telemetry.NewTelemetry(n.logger, telemetry.Options{
+ Enabled: !n.cfg.DisableTele,
+ Version: utils.Version,
+ GlobalMap: TeleGlobalMap,
+ InstallationID: n.cfg.InstallationID,
+ })
+ tel.Ping()
+
+ switch cmd {
+ case "secure":
+ return secure.NewSecurityChecker(n.cfg, n.logger)
+ case "load":
+ return load.NewLoadTester(n.cfg, n.logger)
+ case "testsuite":
+ return testsuite.NewTSExecutor(n.cfg, n.logger, false)
+ case "gen":
+ return utgen.NewUnitTestGenerator(n.cfg, tel, n.auth, n.logger)
+ case "record", "test", "mock", "normalize", "rerecord", "contract", "config", "update", "login", "export", "import", "templatize", "report":
+ return Get(ctx, cmd, n.cfg, n.logger, tel, n.auth)
+ default:
+ return nil, errors.New("invalid command")
+ }
+}
diff --git a/keploy/cli/provider/util.go b/keploy/cli/provider/util.go
new file mode 100644
index 0000000..eb743c7
--- /dev/null
+++ b/keploy/cli/provider/util.go
@@ -0,0 +1,101 @@
+package provider
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "strings"
+
+ "go.keploy.io/server/v2/utils"
+)
+
+func (c *CmdConfigurator) noCommandError() error {
+ return errors.New("missing required -c flag or appCmd in config file")
+}
+
+// alreadyRunning checks that during test mode, if user provides the basePath, then it implies that the application is already running somewhere.
+func alreadyRunning(cmd, basePath string) bool {
+ return (cmd == "test" && basePath != "")
+}
+
+var Logo = `
+ ▓██▓▄
+ ▓▓▓▓██▓█▓▄
+ ████████▓▒
+ ▀▓▓███▄ ▄▄ ▄ ▌
+ ▄▌▌▓▓████▄ ██ ▓█▀ ▄▌▀▄ ▓▓▌▄ ▓█ ▄▌▓▓▌▄ ▌▌ ▓
+ ▓█████████▌▓▓ ██▓█▄ ▓█▄▓▓ ▐█▌ ██ ▓█ █▌ ██ █▌ █▓
+ ▓▓▓▓▀▀▀▀▓▓▓▓▓▓▌ ██ █▓ ▓▌▄▄ ▐█▓▄▓█▀ █▓█ ▀█▄▄█▀ █▓█
+ ▓▌ ▐█▌ █▌
+ ▓
+`
+
+func PrintLogo(wr io.Writer, disableANSI bool) {
+ if os.Getenv("BINARY_TO_DOCKER") != "true" {
+ printKeployLogo(wr, disableANSI, Logo)
+ // print version to the same writer
+ _, err := fmt.Fprintf(wr, "%s: %v\n\n", utils.VersionIdenitfier, utils.Version)
+ if err != nil {
+ log.Fatalf("Error printing version: %v", err)
+ }
+ }
+}
+
+func printKeployLogo(wr io.Writer, disableANSI bool, logo string) {
+ const reset = "\033[0m"
+ lines := strings.Split(logo, "\n")
+
+ if !disableANSI {
+ for i, line := range lines {
+ for j, ch := range line {
+ color := getLogoColor(i, j)
+ // wrapper now uses fmt.Fprint, so this will correctly print color + char + reset
+ FprintWrapper(false, wr, color, string(ch), reset)
+ }
+ FprintWrapper(true, wr) // newline after each line
+ }
+ } else {
+ // plain logo (no per-char coloring)
+ FprintWrapper(false, wr, logo)
+ FprintWrapper(true, wr)
+ }
+}
+
+// FprintWrapper prints all its args (like fmt.Fprint) and optionally a leading newline.
+func FprintWrapper(newLine bool, wr io.Writer, a ...interface{}) {
+ if newLine {
+ if _, err := fmt.Fprintln(wr); err != nil {
+ log.Fatalf("Error printing newline: %v", err)
+ }
+ }
+ if len(a) > 0 {
+ if _, err := fmt.Fprint(wr, a...); err != nil {
+ log.Fatalf("Error printing output: %v", err)
+ }
+ }
+}
+
+// Get the color for the logo at position (i, j)
+func getLogoColor(i, j int) string {
+ gradientColors := []string{
+ "\033[38;5;202m", // Dark Orange
+ "\033[38;5;208m",
+ "\033[38;5;214m", // Light Orange
+ "\033[38;5;226m", // Light Yellow
+ }
+
+ switch {
+ case i <= 5:
+ return gradientColors[0]
+ case i == 6 && j <= 42:
+ return gradientColors[1]
+ case i == 7 && j <= 49:
+ return gradientColors[2]
+ case j <= 38:
+ return gradientColors[3]
+ default:
+ return gradientColors[0]
+ }
+}
diff --git a/keploy/cli/record.go b/keploy/cli/record.go
new file mode 100755
index 0000000..8349543
--- /dev/null
+++ b/keploy/cli/record.go
@@ -0,0 +1,55 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ recordSvc "go.keploy.io/server/v2/pkg/service/record"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("record", Record)
+}
+
+func Record(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "record",
+ Short: "record the keploy testcases from the API calls",
+ Example: `keploy record -c "/path/to/user/app"`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var record recordSvc.Service
+ var ok bool
+ if record, ok = svc.(recordSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy record service interface")
+ return nil
+ }
+
+ err = record.Start(ctx, false)
+ if err != nil {
+ utils.LogError(logger, err, "failed to record")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ err := cmdConfigurator.AddFlags(cmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add record flags")
+ return nil
+ }
+
+ return cmd
+}
diff --git a/keploy/cli/report.go b/keploy/cli/report.go
new file mode 100644
index 0000000..4a01a56
--- /dev/null
+++ b/keploy/cli/report.go
@@ -0,0 +1,55 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ reportSvc "go.keploy.io/server/v2/pkg/service/report"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("report", Report)
+}
+
+func Report(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "report",
+ Short: "report the keploy test results from the API calls",
+ Example: `keploy report -t "test-set-id"`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var report reportSvc.Service
+ var ok bool
+ if report, ok = svc.(reportSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy report service interface")
+ return nil
+ }
+
+ err = report.GenerateReport(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to generate report")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ err := cmdConfigurator.AddFlags(cmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add report flags")
+ return nil
+ }
+
+ return cmd
+}
diff --git a/keploy/cli/rerecord.go b/keploy/cli/rerecord.go
new file mode 100644
index 0000000..17ffda8
--- /dev/null
+++ b/keploy/cli/rerecord.go
@@ -0,0 +1,56 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ "go.keploy.io/server/v2/pkg/service/orchestrator"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("rerecord", ReRecord)
+}
+
+func ReRecord(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "rerecord",
+ Short: "ReRecord new keploy testcases/mocks from the existing test cases for the given testset(s)",
+ Example: `keploy rerecord -c "user app cmd" -t "test-set-1,teset-set-3"`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+
+ var orch orchestrator.Service
+ var ok bool
+ if orch, ok = svc.(orchestrator.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy orchestrator service interface")
+ return nil
+ }
+
+ err = orch.ReRecord(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to re-record")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ err := cmdConfigurator.AddFlags(cmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add rerecord flags")
+ return nil
+ }
+
+ return cmd
+}
diff --git a/keploy/cli/root.go b/keploy/cli/root.go
new file mode 100755
index 0000000..ce7b8a4
--- /dev/null
+++ b/keploy/cli/root.go
@@ -0,0 +1,55 @@
+package cli
+
+import (
+ "context"
+ "os"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/cli/provider"
+ "go.keploy.io/server/v2/config"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func Root(ctx context.Context, logger *zap.Logger, svcFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ conf := config.New()
+
+ var rootCmd = &cobra.Command{
+ Use: "keploy",
+ Short: "Keploy CLI",
+ Example: provider.RootExamples,
+ Version: utils.Version,
+ PreRun: func(cmd *cobra.Command, _ []string) {
+ disableAnsi, _ := cmd.Flags().GetBool("disable-ansi")
+ provider.PrintLogo(os.Stdout, disableAnsi)
+ },
+ }
+
+ defaultHelpFunc := rootCmd.HelpFunc()
+
+ rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
+ disableAnsi, _ := cmd.Flags().GetBool("disable-ansi")
+ provider.PrintLogo(os.Stdout, disableAnsi)
+
+ // Use the default help function instead of calling the parent's HelpFunc
+ defaultHelpFunc(cmd, args)
+ })
+
+ rootCmd.CompletionOptions.DisableDefaultCmd = true
+
+ rootCmd.SetHelpTemplate(provider.RootCustomHelpTemplate)
+
+ rootCmd.SetVersionTemplate(provider.VersionTemplate)
+
+ err := cmdConfigurator.AddFlags(rootCmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to set flags")
+ return nil
+ }
+
+ for _, cmd := range Registered {
+ c := cmd(ctx, logger, conf, svcFactory, cmdConfigurator)
+ rootCmd.AddCommand(c)
+ }
+ return rootCmd
+}
diff --git a/keploy/cli/secure.go b/keploy/cli/secure.go
new file mode 100644
index 0000000..ede4010
--- /dev/null
+++ b/keploy/cli/secure.go
@@ -0,0 +1,286 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ secureSvc "go.keploy.io/server/v2/pkg/service/secure"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("secure", Secure)
+}
+
+func Secure(ctx context.Context, logger *zap.Logger, cfg *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "secure",
+ Short: "check security vulnerabilities against a given API url (--base-url)",
+ Example: `keploy secure --base-url "http://localhost:8080/path/to/user/app"`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service")
+ return nil
+ }
+
+ var secSvc secureSvc.Service
+ var ok bool
+ if secSvc, ok = svc.(secureSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy secure service interface")
+ return nil
+ }
+
+ // Get flag values
+ ruleSet, _ := cmd.Flags().GetString("rule-set")
+ checksPath, _ := cmd.Flags().GetString("checks-path")
+
+ // Add flag values to context
+ ctx = context.WithValue(ctx, "rule-set", ruleSet)
+ ctx = context.WithValue(ctx, "checks-path", checksPath)
+
+ _, err = secSvc.Start(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to start secure")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ // Add flags for the main secure command
+ err := cmdConfigurator.AddFlags(cmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add secure flags")
+ return nil
+ }
+
+ if addCmd := AddCRCommand(cmd, ctx, logger, cfg, serviceFactory, cmdConfigurator); addCmd != nil {
+ cmd.AddCommand(addCmd)
+ // Add flags for the subcommand
+ err := cmdConfigurator.AddFlags(addCmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add secure add flags")
+ }
+ }
+
+ if removeCmd := RemoveCRCommand(cmd, ctx, logger, cfg, serviceFactory, cmdConfigurator); removeCmd != nil {
+ cmd.AddCommand(removeCmd)
+ // Add flags for the subcommand
+ err := cmdConfigurator.AddFlags(removeCmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add secure remove flags")
+ }
+ }
+
+ if updateCmd := UpdateCRCommand(cmd, ctx, logger, cfg, serviceFactory, cmdConfigurator); updateCmd != nil {
+ cmd.AddCommand(updateCmd)
+ // Add flags for the subcommand
+ err := cmdConfigurator.AddFlags(updateCmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add secure update flags")
+ }
+ }
+
+ if listCmd := ListCRsCommand(cmd, ctx, logger, cfg, serviceFactory, cmdConfigurator); listCmd != nil {
+ cmd.AddCommand(listCmd)
+ // Add flags for the subcommand
+ err := cmdConfigurator.AddFlags(listCmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add secure list flags")
+ }
+ }
+
+ return cmd
+}
+
+func AddCRCommand(cmd *cobra.Command, ctx context.Context, logger *zap.Logger, cfg *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var addCmd = &cobra.Command{
+ Use: "add",
+ Short: "add a custom security check",
+ Example: `keploy secure add --checks-path "./custom-checks.yaml"`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ svc, err := serviceFactory.GetService(ctx, "secure")
+ if err != nil {
+ utils.LogError(logger, err, "failed to get secure service")
+ return nil
+ }
+
+ var secSvc secureSvc.Service
+ var ok bool
+ if secSvc, ok = svc.(secureSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy secure service interface")
+ return nil
+ }
+
+ // Get flag values
+ checksPath, _ := cmd.Flags().GetString("checks-path")
+
+ // Add flag values to context
+ ctx = context.WithValue(ctx, "checks-path", checksPath)
+
+ err = secSvc.AddCustomCheck(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add custom check")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ // addCmd.Flags().String("configPath", ".", "Path to the local directory where keploy configuration file is stored")
+ // addCmd.Flags().String("checks-path", "keploy/secure/custom-checks.yaml", "Path to the custom checks file")
+
+ return addCmd
+}
+
+func RemoveCRCommand(cmd *cobra.Command, ctx context.Context, logger *zap.Logger, cfg *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var removeCmd = &cobra.Command{
+ Use: "remove",
+ Short: "remove a custom security check",
+ Example: `keploy secure remove --id --checks-path "./custom-checks.yaml"`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ svc, err := serviceFactory.GetService(ctx, "secure")
+ if err != nil {
+ utils.LogError(logger, err, "failed to get secure service")
+ return nil
+ }
+
+ var secSvc secureSvc.Service
+ var ok bool
+ if secSvc, ok = svc.(secureSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy secure service interface")
+ return nil
+ }
+
+ // Get flag values
+ checksPath, _ := cmd.Flags().GetString("checks-path")
+ id, _ := cmd.Flags().GetString("id")
+
+ // Add flag values to context
+ ctx = context.WithValue(ctx, "checks-path", checksPath)
+ ctx = context.WithValue(ctx, "id", id)
+
+ err = secSvc.RemoveCustomCheck(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to remove custom check")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ // removeCmd.Flags().String("configPath", ".", "Path to the local directory where keploy configuration file is stored")
+ // removeCmd.Flags().String("checks-path", "keploy/secure/custom-checks.yaml", "Path to the custom checks file")
+ // removeCmd.Flags().String("id", "", "ID of the custom check to remove")
+
+ return removeCmd
+}
+
+func UpdateCRCommand(cmd *cobra.Command, ctx context.Context, logger *zap.Logger, cfg *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var updateCmd = &cobra.Command{
+ Use: "update",
+ Short: "update a custom security check",
+ Example: `keploy secure update --id --checks-path "./custom-checks.yaml"`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ svc, err := serviceFactory.GetService(ctx, "secure")
+ if err != nil {
+ utils.LogError(logger, err, "failed to get secure service")
+ return nil
+ }
+
+ var secSvc secureSvc.Service
+ var ok bool
+ if secSvc, ok = svc.(secureSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy secure service interface")
+ return nil
+ }
+
+ // Get flag values
+ checksPath, _ := cmd.Flags().GetString("checks-path")
+ id, _ := cmd.Flags().GetString("id")
+
+ // Add flag values to context
+ ctx = context.WithValue(ctx, "checks-path", checksPath)
+ ctx = context.WithValue(ctx, "id", id)
+
+ err = secSvc.UpdateCustomCheck(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to update custom check")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ // updateCmd.Flags().String("configPath", ".", "Path to the local directory where keploy configuration file is stored")
+ // updateCmd.Flags().String("checks-path", "keploy/secure/custom-checks.yaml", "Path to the custom checks file")
+ // updateCmd.Flags().String("id", "", "ID of the custom check to update")
+
+ return updateCmd
+}
+
+func ListCRsCommand(cmd *cobra.Command, ctx context.Context, logger *zap.Logger, cfg *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var listCmd = &cobra.Command{
+ Use: "list",
+ Short: "list all built-in or custom security checks",
+ Example: `keploy secure list --rule-set custom --checks-path "./custom-checks.yaml"`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ svc, err := serviceFactory.GetService(ctx, "secure")
+ if err != nil {
+ utils.LogError(logger, err, "failed to get secure service")
+ return nil
+ }
+
+ var secSvc secureSvc.Service
+ var ok bool
+ if secSvc, ok = svc.(secureSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy secure service interface")
+ return nil
+ }
+
+ // Get flag values
+ ruleSet, _ := cmd.Flags().GetString("rule-set")
+ checksPath, _ := cmd.Flags().GetString("checks-path")
+
+ // Add flag values to context
+ ctx = context.WithValue(ctx, "rule-set", ruleSet)
+ ctx = context.WithValue(ctx, "checks-path", checksPath)
+
+ err = secSvc.ListChecks(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to list checks")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ // listCmd.Flags().String("configPath", ".", "Path to the local directory where keploy configuration file is stored")
+ // listCmd.Flags().String("rule-set", "basic", "Specify which checks to list: 'basic' (built-in), 'custom'")
+ // listCmd.Flags().String("checks-path", "keploy/secure/custom-checks.yaml", "Path to the custom checks file")
+
+ return listCmd
+}
diff --git a/keploy/cli/service.go b/keploy/cli/service.go
new file mode 100644
index 0000000..8e0f815
--- /dev/null
+++ b/keploy/cli/service.go
@@ -0,0 +1,17 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+)
+
+type ServiceFactory interface {
+ GetService(ctx context.Context, cmd string) (interface{}, error)
+}
+
+type CmdConfigurator interface {
+ AddFlags(cmd *cobra.Command) error
+ ValidateFlags(ctx context.Context, cmd *cobra.Command) error
+ Validate(ctx context.Context, cmd *cobra.Command) error
+}
diff --git a/keploy/cli/templatize.go b/keploy/cli/templatize.go
new file mode 100644
index 0000000..6e63688
--- /dev/null
+++ b/keploy/cli/templatize.go
@@ -0,0 +1,53 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ toolsSvc "go.keploy.io/server/v2/pkg/service/tools"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("Templatize", Templatize)
+}
+
+func Templatize(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "templatize",
+ Short: "templatize the keploy testcases for re-record",
+ Example: `keploy templatize -t "test-set-1,teset-set-3" for particular testsets and keploy templatize for all testsets`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ // Get the replay service.
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var tools toolsSvc.Service
+ var ok bool
+ if tools, ok = svc.(toolsSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy tools service interface")
+ return nil
+ }
+ if err := tools.Templatize(ctx); err != nil {
+ utils.LogError(logger, err, "failed to templatize test cases")
+ return nil
+ }
+ return nil
+ },
+ }
+
+ err := cmdConfigurator.AddFlags(cmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add templatize flags")
+ return nil
+ }
+
+ return cmd
+}
diff --git a/keploy/cli/test.go b/keploy/cli/test.go
new file mode 100755
index 0000000..6b91330
--- /dev/null
+++ b/keploy/cli/test.go
@@ -0,0 +1,63 @@
+package cli
+
+import (
+ "context"
+
+ "go.keploy.io/server/v2/utils"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ replaySvc "go.keploy.io/server/v2/pkg/service/replay"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("test", Test)
+}
+
+func Test(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var testCmd = &cobra.Command{
+ Use: "test",
+ Short: "run the recorded testcases and execute assertions",
+ Example: `keploy test -c "/path/to/user/app" --delay 6`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var replay replaySvc.Service
+ var ok bool
+ if replay, ok = svc.(replaySvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy replay service interface")
+ return nil
+ }
+ // defering the stop function to stop keploy in case of any error in test or in case of context cancellation
+ defer func() {
+ select {
+ case <-ctx.Done():
+ break
+ default:
+ utils.ExecCancel()
+ }
+ }()
+ err = replay.Start(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to replay")
+ }
+
+ return nil
+ },
+ }
+
+ err := cmdConfigurator.AddFlags(testCmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add test flags")
+ return nil
+ }
+
+ return testCmd
+}
diff --git a/keploy/cli/testsuite.go b/keploy/cli/testsuite.go
new file mode 100644
index 0000000..501f3c5
--- /dev/null
+++ b/keploy/cli/testsuite.go
@@ -0,0 +1,60 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ testsuiteSvc "go.keploy.io/server/v2/pkg/service/testsuite"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("testsuite", TestSuite)
+}
+
+func TestSuite(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "testsuite",
+ Short: "execute a testsuite against a given url (--base-url)",
+ Example: `keploy testsuite --base-url "http://localhost:8080/path/to/user/app"`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service")
+ return nil
+ }
+
+ var tsSvc testsuiteSvc.Service
+ var ok bool
+ if tsSvc, ok = svc.(testsuiteSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy testsuite service interface")
+ return nil
+ }
+
+ // If any other command is using the TSExecutor there is no need to report to the screen.
+ // for example, the load command uses the TSExecutor to execute the testsuite. It does not need to report the execution to the screen.
+ ctx = context.WithValue(ctx, "command", cmd.Name())
+
+ _, err = tsSvc.Execute(ctx, nil)
+ if err != nil {
+ utils.LogError(logger, err, "failed to execute testsuite")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ err := cmdConfigurator.AddFlags(cmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add testsuite flags")
+ return nil
+ }
+
+ return cmd
+}
diff --git a/keploy/cli/update.go b/keploy/cli/update.go
new file mode 100644
index 0000000..64cbf3f
--- /dev/null
+++ b/keploy/cli/update.go
@@ -0,0 +1,51 @@
+package cli
+
+import (
+ "context"
+ "os"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/cli/provider"
+ "go.keploy.io/server/v2/config"
+ toolsSvc "go.keploy.io/server/v2/pkg/service/tools"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("update", Update)
+}
+
+// Update retrieves the command to tools Keploy
+func Update(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var updateCmd = &cobra.Command{
+ Use: "update",
+ Short: "Update Keploy ",
+ Example: "keploy update",
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ disableAnsi, _ := (cmd.Flags().GetBool("disable-ansi"))
+ provider.PrintLogo(os.Stdout, disableAnsi)
+ svc, err := serviceFactory.GetService(ctx, "update")
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var tools toolsSvc.Service
+ var ok bool
+ if tools, ok = svc.(toolsSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy tools service interface")
+ return nil
+ }
+ err = tools.Update(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to update")
+ }
+ return nil
+ },
+ }
+ if err := cmdConfigurator.AddFlags(updateCmd); err != nil {
+ utils.LogError(logger, err, "failed to add update cmd flags")
+ return nil
+ }
+ return updateCmd
+}
diff --git a/keploy/cli/utgen.go b/keploy/cli/utgen.go
new file mode 100644
index 0000000..5143225
--- /dev/null
+++ b/keploy/cli/utgen.go
@@ -0,0 +1,56 @@
+package cli
+
+import (
+ "context"
+
+ "github.com/spf13/cobra"
+ "go.keploy.io/server/v2/config"
+ utgenSvc "go.keploy.io/server/v2/pkg/service/utgen"
+ "go.keploy.io/server/v2/utils"
+
+ "go.uber.org/zap"
+)
+
+func init() {
+ Register("gen", GenerateUT)
+}
+
+func GenerateUT(ctx context.Context, logger *zap.Logger, _ *config.Config, serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "gen",
+ Short: "generate unit tests using AI",
+ Example: `keploy gen"`,
+ PreRunE: func(cmd *cobra.Command, _ []string) error {
+ return cmdConfigurator.Validate(ctx, cmd)
+ },
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service", zap.String("command", cmd.Name()))
+ return nil
+ }
+ var utg utgenSvc.Service
+ var ok bool
+ if utg, ok = svc.(utgenSvc.Service); !ok {
+ utils.LogError(logger, nil, "service doesn't satisfy unit test generation service interface")
+ return nil
+ }
+
+ err = utg.Start(ctx)
+ if err != nil {
+ utils.LogError(logger, err, "failed to generate unit tests")
+ return nil
+ }
+
+ return nil
+ },
+ }
+
+ err := cmdConfigurator.AddFlags(cmd)
+ if err != nil {
+ utils.LogError(logger, err, "failed to add unit test generation flags")
+ return nil
+ }
+
+ return cmd
+}
diff --git a/keploy/config/config.go b/keploy/config/config.go
new file mode 100644
index 0000000..7d00e0c
--- /dev/null
+++ b/keploy/config/config.go
@@ -0,0 +1,259 @@
+// Package config provides configuration structures for the application.
+package config
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+ "time"
+)
+
+type Config struct {
+ Path string `json:"path" yaml:"path" mapstructure:"path"`
+ AppID uint64 `json:"appId" yaml:"appId" mapstructure:"appId"`
+ AppName string `json:"appName" yaml:"appName" mapstructure:"appName"`
+ Command string `json:"command" yaml:"command" mapstructure:"command"`
+ Templatize Templatize `json:"templatize" yaml:"templatize" mapstructure:"templatize"`
+ Port uint32 `json:"port" yaml:"port" mapstructure:"port"`
+ E2E bool `json:"e2e" yaml:"e2e" mapstructure:"e2e"`
+ DNSPort uint32 `json:"dnsPort" yaml:"dnsPort" mapstructure:"dnsPort"`
+ ProxyPort uint32 `json:"proxyPort" yaml:"proxyPort" mapstructure:"proxyPort"`
+ Debug bool `json:"debug" yaml:"debug" mapstructure:"debug"`
+ DisableTele bool `json:"disableTele" yaml:"disableTele" mapstructure:"disableTele"`
+ DisableANSI bool `json:"disableANSI" yaml:"disableANSI" mapstructure:"disableANSI"`
+ InDocker bool `json:"inDocker" yaml:"-" mapstructure:"inDocker"`
+ ContainerName string `json:"containerName" yaml:"containerName" mapstructure:"containerName"`
+ NetworkName string `json:"networkName" yaml:"networkName" mapstructure:"networkName"`
+ BuildDelay uint64 `json:"buildDelay" yaml:"buildDelay" mapstructure:"buildDelay"`
+ Test Test `json:"test" yaml:"test" mapstructure:"test"`
+ Record Record `json:"record" yaml:"record" mapstructure:"record"`
+ Report Report `json:"report" yaml:"report" mapstructure:"report"`
+ Gen UtGen `json:"gen" yaml:"-" mapstructure:"gen"`
+ Normalize Normalize `json:"normalize" yaml:"-" mapstructure:"normalize"`
+ ReRecord ReRecord `json:"rerecord" yaml:"-" mapstructure:"rerecord"`
+ ConfigPath string `json:"configPath" yaml:"configPath" mapstructure:"configPath"`
+ BypassRules []BypassRule `json:"bypassRules" yaml:"bypassRules" mapstructure:"bypassRules"`
+ EnableTesting bool `json:"enableTesting" yaml:"-" mapstructure:"enableTesting"`
+ GenerateGithubActions bool `json:"generateGithubActions" yaml:"generateGithubActions" mapstructure:"generateGithubActions"`
+ KeployContainer string `json:"keployContainer" yaml:"keployContainer" mapstructure:"keployContainer"`
+ KeployNetwork string `json:"keployNetwork" yaml:"keployNetwork" mapstructure:"keployNetwork"`
+ CommandType string `json:"cmdType" yaml:"cmdType" mapstructure:"cmdType"`
+ Contract Contract `json:"contract" yaml:"contract" mapstructure:"contract"`
+ TestSuite TestSuite `json:"testSuite" yaml:"testSuite" mapstructure:"testSuite"`
+
+ InCi bool `json:"inCi" yaml:"inCi" mapstructure:"inCi"`
+ InstallationID string `json:"-" yaml:"-" mapstructure:"-"`
+ Version string `json:"-" yaml:"-" mapstructure:"-"`
+ APIServerURL string `json:"-" yaml:"-" mapstructure:"-"`
+ GitHubClientID string `json:"-" yaml:"-" mapstructure:"-"`
+}
+
+type TestSuite struct {
+ TSPath string `json:"tsPath" yaml:"tsPath" mapstructure:"tsPath"`
+ TSFile string `json:"tsFile" yaml:"tsFile" mapstructure:"tsFile"`
+ BaseURL string `json:"baseUrl" yaml:"baseUrl" mapstructure:"baseUrl"`
+}
+
+type UtGen struct {
+ SourceFilePath string `json:"sourceFilePath" yaml:"sourceFilePath" mapstructure:"sourceFilePath"`
+ TestFilePath string `json:"testFilePath" yaml:"testFilePath" mapstructure:"testFilePath"`
+ CoverageReportPath string `json:"coverageReportPath" yaml:"coverageReportPath" mapstructure:"coverageReportPath"`
+ TestCommand string `json:"testCommand" yaml:"testCommand" mapstructure:"testCommand"`
+ CoverageFormat string `json:"coverageFormat" yaml:"coverageFormat" mapstructure:"coverageFormat"`
+ DesiredCoverage float64 `json:"expectedCoverage" yaml:"expectedCoverage" mapstructure:"expectedCoverage"`
+ MaxIterations int `json:"maxIterations" yaml:"maxIterations" mapstructure:"maxIterations"`
+ TestDir string `json:"testDir" yaml:"testDir" mapstructure:"testDir"`
+ APIBaseURL string `json:"llmBaseUrl" yaml:"llmBaseUrl" mapstructure:"llmBaseUrl"`
+ Model string `json:"model" yaml:"model" mapstructure:"model"`
+ APIVersion string `json:"llmApiVersion" yaml:"llmApiVersion" mapstructure:"llmApiVersion"`
+ AdditionalPrompt string `json:"additionalPrompt" yaml:"additionalPrompt" mapstructure:"additionalPrompt"`
+ FunctionUnderTest string `json:"functionUnderTest" yaml:"-" mapstructure:"functionUnderTest"`
+ Flakiness bool `json:"flakiness" yaml:"flakiness" mapstructure:"flakiness"`
+}
+type Templatize struct {
+ TestSets []string `json:"testSets" yaml:"testSets" mapstructure:"testSets"`
+}
+
+type Record struct {
+ Filters []Filter `json:"filters" yaml:"filters" mapstructure:"filters"`
+ BasePath string `json:"basePath" yaml:"basePath" mapstructure:"basePath"`
+ RecordTimer time.Duration `json:"recordTimer" yaml:"recordTimer" mapstructure:"recordTimer"`
+ Metadata string `json:"metadata" yaml:"metadata" mapstructure:"metadata"`
+}
+
+type ReRecord struct {
+ SelectedTests []string `json:"selectedTests" yaml:"selectedTests" mapstructure:"selectedTests"`
+ Filters []Filter `json:"filters" yaml:"filters" mapstructure:"filters"`
+ Host string `json:"host" yaml:"host" mapstructure:"host"`
+ Port uint32 `json:"port" yaml:"port" mapstructure:"port"`
+}
+type Contract struct {
+ Services []string `json:"services" yaml:"services" mapstructure:"services"`
+ Tests []string `json:"tests" yaml:"tests" mapstructure:"tests"`
+ Path string `json:"path" yaml:"path" mapstructure:"path"`
+ Download bool `json:"download" yaml:"download" mapstructure:"download"`
+ Generate bool `json:"generate" yaml:"generate" mapstructure:"generate"`
+ Driven string `json:"driven" yaml:"driven" mapstructure:"driven"`
+ Mappings Mappings `json:"mappings" yaml:"mappings" mapstructure:"mappings"`
+}
+type Mappings struct {
+ ServicesMapping map[string][]string `json:"servicesMapping" yaml:"servicesMapping" mapstructure:"servicesMapping"`
+ Self string `json:"self" yaml:"self" mapstructure:"self"`
+}
+
+type Normalize struct {
+ SelectedTests []SelectedTests `json:"selectedTests" yaml:"selectedTests" mapstructure:"selectedTests"`
+ TestRun string `json:"testReport" yaml:"testReport" mapstructure:"testReport"`
+}
+
+type BypassRule struct {
+ Path string `json:"path" yaml:"path" mapstructure:"path"`
+ Host string `json:"host" yaml:"host" mapstructure:"host"`
+ Port uint `json:"port" yaml:"port" mapstructure:"port"`
+}
+type MatchType string
+
+const (
+ OR MatchType = "OR"
+ AND MatchType = "AND"
+)
+
+type Filter struct {
+ BypassRule `mapstructure:",squash"`
+ URLMethods []string `json:"urlMethods" yaml:"urlMethods" mapstructure:"urlMethods"`
+ Headers map[string]string `json:"headers" yaml:"headers" mapstructure:"headers"`
+ MatchType MatchType `json:"matchType"`
+}
+type Test struct {
+ SelectedTests map[string][]string `json:"selectedTests" yaml:"selectedTests" mapstructure:"selectedTests"`
+ GlobalNoise Globalnoise `json:"globalNoise" yaml:"globalNoise" mapstructure:"globalNoise"`
+ Delay uint64 `json:"delay" yaml:"delay" mapstructure:"delay"`
+ Host string `json:"host" yaml:"host" mapstructure:"host"`
+ Port uint32 `json:"port" yaml:"port" mapstructure:"port"`
+ APITimeout uint64 `json:"apiTimeout" yaml:"apiTimeout" mapstructure:"apiTimeout"`
+ SkipCoverage bool `json:"skipCoverage" yaml:"skipCoverage" mapstructure:"skipCoverage"` // boolean to capture the coverage in test
+ CoverageReportPath string `json:"coverageReportPath" yaml:"coverageReportPath" mapstructure:"coverageReportPath"` // directory path to store the coverage files
+ IgnoreOrdering bool `json:"ignoreOrdering" yaml:"ignoreOrdering" mapstructure:"ignoreOrdering"`
+ MongoPassword string `json:"mongoPassword" yaml:"mongoPassword" mapstructure:"mongoPassword"`
+ Language Language `json:"language" yaml:"language" mapstructure:"language"`
+ RemoveUnusedMocks bool `json:"removeUnusedMocks" yaml:"removeUnusedMocks" mapstructure:"removeUnusedMocks"`
+ FallBackOnMiss bool `json:"fallBackOnMiss" yaml:"fallBackOnMiss" mapstructure:"fallBackOnMiss"`
+ JacocoAgentPath string `json:"jacocoAgentPath" yaml:"jacocoAgentPath" mapstructure:"jacocoAgentPath"`
+ BasePath string `json:"basePath" yaml:"basePath" mapstructure:"basePath"`
+ Mocking bool `json:"mocking" yaml:"mocking" mapstructure:"mocking"`
+ IgnoredTests map[string][]string `json:"ignoredTests" yaml:"ignoredTests" mapstructure:"ignoredTests"`
+ DisableLineCoverage bool `json:"disableLineCoverage" yaml:"disableLineCoverage" mapstructure:"disableLineCoverage"`
+ DisableMockUpload bool `json:"disableMockUpload" yaml:"disableMockUpload" mapstructure:"disableMockUpload"`
+ UseLocalMock bool `json:"useLocalMock" yaml:"useLocalMock" mapstructure:"useLocalMock"`
+ UpdateTemplate bool `json:"updateTemplate" yaml:"updateTemplate" mapstructure:"updateTemplate"`
+ MustPass bool `json:"mustPass" yaml:"mustPass" mapstructure:"mustPass"`
+ MaxFailAttempts uint32 `json:"maxFailAttempts" yaml:"maxFailAttempts" mapstructure:"maxFailAttempts"`
+ MaxFlakyChecks uint32 `json:"maxFlakyChecks" yaml:"maxFlakyChecks" mapstructure:"maxFlakyChecks"`
+}
+
+type Report struct {
+ SelectedTestSets map[string][]string `json:"selectedTestSets" yaml:"selectedTestSets" mapstructure:"selectedTestSets"`
+}
+
+type Language string
+
+// String is used both by fmt.Print and by Cobra in help text
+func (e *Language) String() string {
+ return string(*e)
+}
+
+// Set must have pointer receiver so it doesn't change the value of a copy
+func (e *Language) Set(v string) error {
+ switch v {
+ case "go", "java", "python", "javascript":
+ *e = Language(v)
+ return nil
+ default:
+ return errors.New(`must be one of "go", "java", "python" or "javascript"`)
+ }
+}
+
+// Type is only used in help text
+func (e *Language) Type() string {
+ return "myEnum"
+}
+
+type Globalnoise struct {
+ Global GlobalNoise `json:"global" yaml:"global" mapstructure:"global"`
+ Testsets TestsetNoise `json:"test-sets" yaml:"test-sets" mapstructure:"test-sets"`
+}
+
+type SelectedTests struct {
+ TestSet string `json:"testSet" yaml:"testSet" mapstructure:"testSet"`
+ Tests []string `json:"tests" yaml:"tests" mapstructure:"tests"`
+}
+
+type (
+ Noise map[string][]string
+ GlobalNoise map[string]map[string][]string
+ TestsetNoise map[string]map[string]map[string][]string
+)
+
+func SetByPassPorts(conf *Config, ports []uint) {
+ for _, port := range ports {
+ conf.BypassRules = append(conf.BypassRules, BypassRule{
+ Path: "",
+ Host: "",
+ Port: port,
+ })
+ }
+}
+
+func GetByPassPorts(conf *Config) []uint {
+ var ports []uint
+ for _, rule := range conf.BypassRules {
+ ports = append(ports, rule.Port)
+ }
+ return ports
+}
+
+func SetSelectedTests(conf *Config, testSets []string) {
+ conf.Test.SelectedTests = make(map[string][]string)
+ for _, testSet := range testSets {
+ conf.Test.SelectedTests[testSet] = []string{}
+ }
+}
+func SetSelectedServices(conf *Config, services []string) {
+ // string is "s1,s2" so i want to get s1,s2
+ conf.Contract.Services = services
+}
+func SetSelectedContractTests(conf *Config, tests []string) {
+
+ conf.Contract.Tests = tests
+}
+
+func SetSelectedTestSets(conf *Config, testSets []string) {
+ conf.Report.SelectedTestSets = make(map[string][]string)
+ for _, testSet := range testSets {
+ conf.Report.SelectedTestSets[testSet] = []string{}
+ }
+}
+
+func SetSelectedTestsNormalize(conf *Config, value string) error {
+ testSets := strings.FieldsFunc(value, func(r rune) bool {
+ return r == ',' || r == ' '
+ })
+ var tests []SelectedTests
+ if len(testSets) == 0 {
+ conf.Normalize.SelectedTests = tests
+ return nil
+ }
+ for _, ts := range testSets {
+ parts := strings.Split(ts, ":")
+ if len(parts) != 2 {
+ return fmt.Errorf("invalid format: %s", ts)
+ }
+ testCases := strings.Split(parts[1], " ")
+ tests = append(tests, SelectedTests{
+ TestSet: parts[0],
+ Tests: testCases,
+ })
+ }
+ conf.Normalize.SelectedTests = tests
+ return nil
+}
diff --git a/keploy/config/default.go b/keploy/config/default.go
new file mode 100644
index 0000000..1d7b0e3
--- /dev/null
+++ b/keploy/config/default.go
@@ -0,0 +1,134 @@
+package config
+
+import (
+ yaml3 "gopkg.in/yaml.v3"
+ "sigs.k8s.io/kustomize/kyaml/yaml"
+ "sigs.k8s.io/kustomize/kyaml/yaml/merge2"
+ "sigs.k8s.io/kustomize/kyaml/yaml/walk"
+)
+
+// defaultConfig is a variable to store the default configuration of the Keploy CLI. It is not a constant because enterprise need update the default configuration.
+var defaultConfig = `
+path: ""
+appId: 0
+appName: ""
+command: ""
+templatize:
+ testSets: []
+port: 0
+proxyPort: 16789
+dnsPort: 26789
+debug: false
+disableANSI: false
+disableTele: false
+generateGithubActions: false
+containerName: ""
+networkName: ""
+buildDelay: 30
+test:
+ selectedTests: {}
+ ignoredTests: {}
+ globalNoise:
+ global: {}
+ test-sets: {}
+ delay: 5
+ host: ""
+ port: 0
+ apiTimeout: 5
+ skipCoverage: false
+ coverageReportPath: ""
+ ignoreOrdering: true
+ mongoPassword: "default@123"
+ language: ""
+ removeUnusedMocks: false
+ fallBackOnMiss: false
+ jacocoAgentPath: ""
+ basePath: ""
+ mocking: true
+ disableLineCoverage: false
+ disableMockUpload: true
+ useLocalMock: false
+ updateTemplate: false
+ mustPass: false
+ maxFailAttempts: 5
+ maxFlakyChecks: 1
+record:
+ recordTimer: 0s
+ filters: []
+configPath: ""
+bypassRules: []
+contract:
+ driven: "consumer"
+ mappings:
+ servicesMapping: {}
+ self: "s1"
+ services: []
+ tests: []
+ path: ""
+ download: false
+ generate: false
+inCi: false
+`
+
+func GetDefaultConfig() string {
+ return defaultConfig
+}
+
+func SetDefaultConfig(cfgStr string) {
+ defaultConfig = cfgStr
+}
+
+const InternalConfig = `
+enableTesting: false
+keployContainer: "keploy-v2"
+keployNetwork: "keploy-network"
+inDocker: false
+cmdType: "native"
+`
+
+var config = &Config{}
+
+func New() *Config {
+ // merge default config with internal config
+ mergedConfig, err := Merge(defaultConfig, InternalConfig)
+ if err != nil {
+ panic(err)
+ }
+ err = yaml3.Unmarshal([]byte(mergedConfig), config)
+ if err != nil {
+ panic(err)
+ }
+ return config
+}
+
+func Merge(srcStr, destStr string) (string, error) {
+ return mergeStrings(srcStr, destStr, false, yaml.MergeOptions{})
+}
+
+// Reference: https://github.com/kubernetes-sigs/kustomize/blob/537c4fa5c2bf3292b273876f50c62ce1c81714d7/kyaml/yaml/merge2/merge2.go#L24
+// VisitKeysAsScalars is set to true to enable merging comments.
+// inferAssociativeLists is set to fasle to disable merging associative lists.
+func mergeStrings(srcStr, destStr string, infer bool, mergeOptions yaml.MergeOptions) (string, error) {
+ src, err := yaml.Parse(srcStr)
+ if err != nil {
+ return "", err
+ }
+
+ dest, err := yaml.Parse(destStr)
+ if err != nil {
+ return "", err
+ }
+
+ result, err := walk.Walker{
+ Sources: []*yaml.RNode{dest, src},
+ Visitor: merge2.Merger{},
+ InferAssociativeLists: infer,
+ VisitKeysAsScalars: true,
+ MergeOptions: mergeOptions,
+ }.Walk()
+ if err != nil {
+ return "", err
+ }
+
+ return result.String()
+}
diff --git a/keploy/entrypoint.sh b/keploy/entrypoint.sh
new file mode 100755
index 0000000..532f13f
--- /dev/null
+++ b/keploy/entrypoint.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+if ! mountpoint -q /sys/kernel/debug; then
+ sudo mount -t debugfs debugfs /sys/kernel/debug
+fi
+
+sudo -E "$@"
diff --git a/keploy/go.mod b/keploy/go.mod
new file mode 100755
index 0000000..1e3898a
--- /dev/null
+++ b/keploy/go.mod
@@ -0,0 +1,159 @@
+module go.keploy.io/server/v2
+
+go 1.24.4
+
+toolchain go1.24.6
+
+replace github.com/jackc/pgproto3/v2 => github.com/keploy/pgproto3/v2 v2.0.5
+
+require (
+ github.com/Microsoft/go-winio v0.6.2 // indirect
+ github.com/cilium/ebpf v0.19.0
+ github.com/cloudflare/cfssl v1.6.4
+ github.com/docker/distribution v2.8.2+incompatible // indirect
+ github.com/docker/docker v24.0.4+incompatible
+ github.com/docker/go-connections v0.4.0 // indirect
+ github.com/docker/go-units v0.5.0 // indirect
+ github.com/fatih/color v1.18.0
+ github.com/k0kubun/pp/v3 v3.2.0
+ github.com/miekg/dns v1.1.57
+ github.com/morikuni/aec v1.0.0 // indirect
+ github.com/olekukonko/tablewriter v0.0.5
+ github.com/opencontainers/go-digest v1.0.0 // indirect
+ github.com/opencontainers/image-spec v1.1.1 // indirect
+ github.com/spf13/cobra v1.9.1
+ go.mongodb.org/mongo-driver v1.11.6
+ go.uber.org/zap v1.27.0
+ golang.org/x/crypto v0.39.0 // indirect
+ golang.org/x/sys v0.33.0
+ google.golang.org/protobuf v1.36.5
+)
+
+require (
+ github.com/containerd/log v0.1.0 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+ github.com/fsnotify/fsnotify v1.8.0 // indirect
+ github.com/getkin/kin-openapi v0.126.0
+ github.com/go-errors/errors v1.4.2 // indirect
+ github.com/go-ole/go-ole v1.2.6 // indirect
+ github.com/go-openapi/jsonpointer v0.21.0 // indirect
+ github.com/go-openapi/jsonreference v0.20.2 // indirect
+ github.com/go-openapi/swag v0.23.0 // indirect
+ github.com/google/certificate-transparency-go v1.2.1 // indirect
+ github.com/google/gnostic-models v0.6.9 // indirect
+ github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
+ github.com/invopop/yaml v0.3.1 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
+ github.com/magiconair/properties v1.8.9 // indirect
+ github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
+ github.com/pelletier/go-toml/v2 v2.2.3 // indirect
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
+ github.com/sagikazarmark/locafero v0.7.0 // indirect
+ github.com/sagikazarmark/slog-shim v0.1.0 // indirect
+ github.com/sirupsen/logrus v1.9.3 // indirect
+ github.com/sourcegraph/conc v0.3.0 // indirect
+ github.com/spf13/afero v1.12.0 // indirect
+ github.com/spf13/cast v1.7.1 // indirect
+ github.com/subosito/gotenv v1.6.0 // indirect
+ github.com/tidwall/gjson v1.18.0 // indirect
+ github.com/tidwall/match v1.1.1 // indirect
+ github.com/tidwall/pretty v1.2.1 // indirect
+ github.com/tidwall/sjson v1.2.5 // indirect
+ github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
+ github.com/yusufpapurcu/wmi v1.2.4 // indirect
+ gopkg.in/ini.v1 v1.67.0 // indirect
+ k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
+ sigs.k8s.io/yaml v1.4.0 // indirect
+)
+
+require (
+ gopkg.in/yaml.v3 v3.0.1
+ gotest.tools/v3 v3.5.0 // indirect
+)
+
+require (
+ github.com/go-logr/logr v1.4.2 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/golang/snappy v1.0.0 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/jackc/chunkreader/v2 v2.0.0 // indirect
+ github.com/jackc/pgio v1.0.0 // indirect
+ github.com/jmoiron/sqlx v1.4.0 // indirect
+ github.com/klauspost/compress v1.18.0 // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mattn/go-runewidth v0.0.16 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/protocolbuffers/protoscope v0.0.0-20221109213918-8e7a6aafa2c9
+ github.com/rivo/uniseg v0.4.7 // indirect
+ github.com/spf13/pflag v1.0.6
+ github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b // indirect
+ github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc // indirect
+ github.com/zmap/zlint/v3 v3.1.0 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ golang.org/x/mod v0.25.0 // indirect
+ golang.org/x/net v0.40.0
+ golang.org/x/text v0.26.0
+ golang.org/x/tools v0.33.0 // indirect
+ k8s.io/klog/v2 v2.130.1 // indirect
+)
+
+require (
+ facette.io/natsort v0.0.0-20181210072756-2cd4dd1e2dcb
+ github.com/7sDream/geko v0.1.1
+ github.com/agnivade/levenshtein v1.1.1
+ github.com/andybalholm/brotli v1.2.0
+ github.com/charmbracelet/glamour v0.6.0
+ github.com/denisbrodbeck/machineid v1.0.1
+ github.com/emirpasic/gods v1.18.1
+ github.com/getsentry/sentry-go v0.28.1
+ github.com/golang-jwt/jwt/v4 v4.5.0
+ github.com/google/uuid v1.6.0
+ github.com/jackc/pgproto3/v2 v2.3.2
+ github.com/keploy/jsonDiff v1.0.8
+ github.com/shirou/gopsutil/v3 v3.24.3
+ github.com/spf13/viper v1.19.0
+ github.com/stretchr/testify v1.10.0
+ github.com/wI2L/jsondiff v0.5.0
+ github.com/xdg-go/pbkdf2 v1.0.0
+ github.com/xdg-go/scram v1.1.1
+ github.com/xdg-go/stringprep v1.0.4
+ golang.org/x/sync v0.15.0
+ golang.org/x/term v0.32.0
+ google.golang.org/grpc v1.71.0
+ gopkg.in/yaml.v2 v2.4.0
+ helm.sh/helm/v3 v3.18.3
+ sigs.k8s.io/kustomize/kyaml v0.19.0
+ vitess.io/vitess v0.22.1
+)
+
+require github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
+
+require (
+ github.com/golang/glog v1.2.4 // indirect
+ github.com/gorilla/mux v1.8.1
+ github.com/perimeterx/marshmallow v1.1.5 // indirect
+ github.com/planetscale/vtprotobuf v0.6.1-0.20241121165744-79df5c4772f2 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/stretchr/objx v0.5.2 // indirect
+ golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
+ golang.org/x/time v0.12.0
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
+)
+
+require (
+ github.com/alecthomas/chroma v0.10.0 // indirect
+ github.com/aymanbagabas/go-osc52 v1.0.3 // indirect
+ github.com/aymerick/douceur v0.2.0 // indirect
+ github.com/dlclark/regexp2 v1.4.0 // indirect
+ github.com/gorilla/css v1.0.1 // indirect
+ github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
+ github.com/microcosm-cc/bluemonday v1.0.27 // indirect
+ github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect
+ github.com/moby/moby v26.0.2+incompatible
+ github.com/muesli/reflow v0.3.0 // indirect
+ github.com/muesli/termenv v0.13.0 // indirect
+ github.com/yuin/goldmark v1.5.2 // indirect
+ github.com/yuin/goldmark-emoji v1.0.1 // indirect
+)
diff --git a/keploy/go.sum b/keploy/go.sum
new file mode 100644
index 0000000..b0b510c
--- /dev/null
+++ b/keploy/go.sum
@@ -0,0 +1,470 @@
+facette.io/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:1pSweJFeR3Pqx7uoelppkzeegfUBXL6I2FFAbfXw570=
+facette.io/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:npRYmtaITVom7rcSo+pRURltHSG2r4TQM1cdqJ2dUB0=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/7sDream/geko v0.1.1 h1:Ms9RVcUJDPsUuRlk3T3sGmSGAMuttqh2/3okZ4yWUpU=
+github.com/7sDream/geko v0.1.1/go.mod h1:NcSQXSUpcoqNP8BkOLP2TsinxRVjnMkDYCKl9L1Czxk=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
+github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
+github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
+github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
+github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
+github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
+github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
+github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
+github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
+github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg=
+github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
+github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
+github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
+github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc=
+github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc=
+github.com/cilium/ebpf v0.19.0 h1:Ro/rE64RmFBeA9FGjcTc+KmCeY6jXmryu6FfnzPRIao=
+github.com/cilium/ebpf v0.19.0/go.mod h1:fLCgMo3l8tZmAdM3B2XqdFzXBpwkcSTroaVqN08OWVY=
+github.com/cloudflare/cfssl v1.6.4 h1:NMOvfrEjFfC63K3SGXgAnFdsgkmiq4kATme5BfcqrO8=
+github.com/cloudflare/cfssl v1.6.4/go.mod h1:8b3CQMxfWPAeom3zBnGJ6sd+G1NkL5TXqmDXacb+1J0=
+github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
+github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
+github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
+github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
+github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
+github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
+github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
+github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
+github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/docker v24.0.4+incompatible h1:s/LVDftw9hjblvqIeTiGYXBCD95nOEEl7qRsRrIOuQI=
+github.com/docker/docker v24.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
+github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
+github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
+github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
+github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
+github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/getkin/kin-openapi v0.126.0 h1:c2cSgLnAsS0xYfKsgt5oBV6MYRM/giU8/RtwUY4wyfY=
+github.com/getkin/kin-openapi v0.126.0/go.mod h1:7mONz8IwmSRg6RttPu6v8U/OJ+gr+J99qSFNjPGSQqw=
+github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k=
+github.com/getsentry/sentry-go v0.28.1/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg=
+github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
+github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
+github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
+github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
+github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s=
+github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
+github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
+github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
+github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc=
+github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
+github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/certificate-transparency-go v1.2.1 h1:4iW/NwzqOqYEEoCBEFP+jPbBXbLqMpq3CifMyOnDUME=
+github.com/google/certificate-transparency-go v1.2.1/go.mod h1:bvn/ytAccv+I6+DGkqpvSsEdiVGramgaSC6RD3tEmeE=
+github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
+github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
+github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
+github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
+github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
+github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I=
+github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
+github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
+github.com/jackc/chunkreader/v2 v2.0.0 h1:DUwgMQuuPnS0rhMXenUtZpqZqrR/30NWY+qQvTpSvEs=
+github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
+github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
+github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
+github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
+github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
+github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
+github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=
+github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=
+github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs=
+github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA=
+github.com/keploy/jsonDiff v1.0.8 h1:B/75cfLNZ7kayAjF1Jox70Iwa/nwCjBYNOuSt+RHH5A=
+github.com/keploy/jsonDiff v1.0.8/go.mod h1:wUuLbVs3Oe3mIQ61C7G88bppP//ArLtoDU0S9Awwv+s=
+github.com/keploy/pgproto3/v2 v2.0.5 h1:8spdNKZ+nOnHVxiimDsqulBRN6viPXPghkA7xppnzJ8=
+github.com/keploy/pgproto3/v2 v2.0.5/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
+github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
+github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
+github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
+github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
+github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
+github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
+github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
+github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
+github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
+github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
+github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
+github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
+github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
+github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE=
+github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/moby/moby v26.0.2+incompatible h1:t41TD3nRvK8E6bZFJdKrmNlH8Xe3epTmdNXf/mnfLKk=
+github.com/moby/moby v26.0.2+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
+github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
+github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
+github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
+github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
+github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
+github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
+github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
+github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
+github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
+github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
+github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
+github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
+github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
+github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
+github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
+github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
+github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
+github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/planetscale/vtprotobuf v0.6.1-0.20241121165744-79df5c4772f2 h1:1sLMdKq4gNANTj0dUibycTLzpIEKVnLnbaEkxws78nw=
+github.com/planetscale/vtprotobuf v0.6.1-0.20241121165744-79df5c4772f2/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/protocolbuffers/protoscope v0.0.0-20221109213918-8e7a6aafa2c9 h1:arwj11zP0yJIxIRiDn22E0H8PxfF7TsTrc2wIPFIsf4=
+github.com/protocolbuffers/protoscope v0.0.0-20221109213918-8e7a6aafa2c9/go.mod h1:SKZx6stCn03JN3BOWTwvVIO2ajMkb/zQdTceXYhKw/4=
+github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
+github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
+github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
+github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
+github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE=
+github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg=
+github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
+github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
+github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
+github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
+github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
+github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
+github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
+github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
+github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
+github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
+github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
+github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
+github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
+github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
+github.com/wI2L/jsondiff v0.5.0 h1:RRMTi/mH+R2aXcPe1VYyvGINJqQfC3R+KSEakuU1Ikw=
+github.com/wI2L/jsondiff v0.5.0/go.mod h1:qqG6hnK0Lsrz2BpIVCxWiK9ItsBCpIZQiv0izJjOZ9s=
+github.com/weppos/publicsuffix-go v0.13.1-0.20210123135404-5fd73613514e/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
+github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b h1:FsyNrX12e5BkplJq7wKOLk0+C6LZ+KGXvuEcKUYm5ss=
+github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
+github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
+github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
+github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
+github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
+github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
+github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
+github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU=
+github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
+github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
+github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is=
+github.com/zmap/zcrypto v0.0.0-20210123152837-9cf5beac6d91/go.mod h1:R/deQh6+tSWlgI9tb4jNmXxn8nSCabl5ZQsBX9//I/E=
+github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc h1:zkGwegkOW709y0oiAraH/3D8njopUR/pARHv4tZZ6pw=
+github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc/go.mod h1:FM4U1E3NzlNMRnSUTU3P1UdukWhYGifqEsjk9fn7BCk=
+github.com/zmap/zlint/v3 v3.1.0 h1:WjVytZo79m/L1+/Mlphl09WBob6YTGljN5IGWZFpAv0=
+github.com/zmap/zlint/v3 v3.1.0/go.mod h1:L7t8s3sEKkb0A2BxGy1IWrxt1ZATa1R4QfJZaQOD3zU=
+go.mongodb.org/mongo-driver v1.11.6 h1:XM7G6PjiGAO5betLF13BIa5TlLUUE3uJ/2Ox3Lz1K+o=
+go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
+go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
+go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
+go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
+go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
+go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
+go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
+go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
+go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
+go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
+golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
+golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
+golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
+golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
+golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
+golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
+golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
+golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
+golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
+golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
+golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
+golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
+golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
+golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
+google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
+google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
+gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
+helm.sh/helm/v3 v3.18.3 h1:+cvyGKgs7Jt7BN3Klmb4SsG4IkVpA7GAZVGvMz6VO4I=
+helm.sh/helm/v3 v3.18.3/go.mod h1:wUc4n3txYBocM7S9RjTeZBN9T/b5MjffpcSsWEjSIpw=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
+k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
+sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
+sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
+sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA=
+sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
+sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+vitess.io/vitess v0.22.1 h1:nCA0v6tt3YVPf8qWMQJktzxZlsLYwdPxplo2TbSEm9A=
+vitess.io/vitess v0.22.1/go.mod h1:JF7nQQ+XUP7og7ZNzOw0p+zKvUTwYd6TTKfFyzyfE9o=
diff --git a/keploy/gon.json b/keploy/gon.json
new file mode 100755
index 0000000..29d89bd
--- /dev/null
+++ b/keploy/gon.json
@@ -0,0 +1,13 @@
+{
+ "source" : ["./dist/keploy-mac-universal_darwin_all/keploy"],
+ "bundle_id" : "io.keploy.server",
+ "apple_id": {
+ "username" : "shubhamkjain@outlook.com"
+ },
+ "sign" :{
+ "application_identity" : "Developer ID Application: Shubham Jain"
+ },
+ "zip" :{
+ "output_path" : "keploy.zip"
+ }
+}
\ No newline at end of file
diff --git a/keploy/goreleaser.yaml b/keploy/goreleaser.yaml
new file mode 100755
index 0000000..869d254
--- /dev/null
+++ b/keploy/goreleaser.yaml
@@ -0,0 +1,60 @@
+# GoReleaser configuration
+archives:
+ -
+ name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
+
+builds:
+ - binary: keploy
+ id: keploy
+ main: ./main.go
+ ldflags:
+ - -s -w -X main.dsn={{.Env.SENTRY_DSN_BINARY}}
+ - -s -w -X main.version={{.Version}}
+ - -s -w -X main.apiServerURI={{.Env.SERVER_URL}}
+ - -s -w -X main.gitHubClientID={{.Env.GITTHUB_APP_CLIENT_ID}}
+ env:
+ - CGO_ENABLED=0
+ goos:
+ - linux
+ - darwin
+ - windows
+ goarch:
+ - amd64
+ - arm64
+ tags:
+ - viper_bind_struct
+
+ # - binary: keploy
+ # id: keploy-macos
+ # main: ./cli/server/main.go
+ # env:
+ # - CGO_ENABLED=0
+ # goos:
+ # - darwin
+ # goarch:
+ # - amd64
+ # - arm64
+
+universal_binaries:
+-
+ # ID of resulting universal binary.
+ #
+ # Defaults to the project name.
+ id: keploy-mac-universal
+
+ # IDs to use to filter the built binaries.
+ #
+ # Defaults to the `id` field.
+ # Since: v1.3.
+ ids:
+ - keploy
+
+ # Whether to remove the previous single-arch binaries from the artifact list.
+ # If left as false, your end release might have both several macOS archives:
+ # amd64, arm64 and all.
+ #
+ # Defaults to false.
+ replace: true
+ hooks:
+ post: gon -log-level=info -log-json ./gon.json
+
diff --git a/keploy/gsoc25_performance&security-testing.md b/keploy/gsoc25_performance&security-testing.md
new file mode 100644
index 0000000..b7145a3
--- /dev/null
+++ b/keploy/gsoc25_performance&security-testing.md
@@ -0,0 +1,558 @@
+# Keploy GSoC'25_Performance&Security Features Documentation
+
+This document provides a comprehensive overview of the three advanced features implemented in Keploy: **TestSuite Execution**, **Load Testing**, and **Security Checking**.
+
+## Table of Contents
+1. [Overview](#overview)
+2. [TestSuite Feature](#testsuite-feature)
+3. [Load Testing Feature](#load-testing-feature)
+4. [Security Checking Feature](#security-checking-feature)
+5. [Integration and Architecture](#integration-and-architecture)
+6. [Usage Examples](#usage-examples)
+7. [Configuration](#configuration)
+
+---
+
+## Overview
+
+These three features work together to provide a comprehensive testing solution:
+
+- **TestSuite**: Core execution engine for running API test suites defined in YAML
+- **Load Testing**: Performance testing capability that executes test suites under load
+- **Security Checking**: Security vulnerability scanning for API endpoints
+
+All features are integrated into the Keploy CLI and share common configuration patterns and data structures.
+
+---
+
+## TestSuite Feature
+
+### Purpose
+The TestSuite feature provides a declarative way to define and execute API test sequences using YAML configuration files. It serves as the foundation for both load testing and security checking.
+
+### Architecture
+
+#### Core Components
+
+1. **TSExecutor** (`pkg/service/testsuite/testsuite.go`)
+ - Main execution engine for test suites
+ - Handles HTTP requests, response validation, and variable extraction
+ - Supports rate limiting for controlled execution
+
+2. **TestSuite Data Structures**
+ ```go
+ type TestSuite struct {
+ Version string `yaml:"version"`
+ Kind string `yaml:"kind"`
+ Name string `yaml:"name"`
+ Spec TestSuiteSpec `yaml:"spec"`
+ }
+
+ type TestSuiteSpec struct {
+ Metadata TestSuiteMetadata `yaml:"metadata"`
+ Security Security `yaml:"security,omitempty"`
+ Load LoadOptions `yaml:"load,omitempty"`
+ Steps []TestStep `yaml:"steps"`
+ }
+ ```
+
+3. **TestStep Execution**
+ - Each step represents an HTTP request with assertions
+ - Supports variable extraction and interpolation
+ - Provides detailed execution results
+
+#### Key Features
+
+- **Variable Extraction**: Extract values from responses using JSON path notation
+- **Variable Interpolation**: Use extracted variables in subsequent requests
+- **Assertions**: Validate response status codes, headers, and body content
+- **Rate Limiting**: Control request rate for load testing scenarios
+- **Detailed Reporting**: Comprehensive execution reports with timing and error information
+
+### Implementation Details
+
+#### CLI Integration (`cli/testsuite.go`)
+```go
+func TestSuite(ctx context.Context, logger *zap.Logger, _ *config.Config,
+ serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "testsuite",
+ Short: "execute a testsuite against a given url (--base-url)",
+ Example: `keploy testsuite --base-url "http://localhost:8080/path/to/user/app"`,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ // Service factory pattern for dependency injection
+ svc, err := serviceFactory.GetService(ctx, cmd.Name())
+ if err != nil {
+ utils.LogError(logger, err, "failed to get service")
+ return nil
+ }
+
+ // Execute the test suite
+ _, err = tsSvc.Execute(ctx, nil)
+ return err
+ },
+ }
+}
+```
+
+#### Service Factory Integration (`cli/provider/service.go`)
+The service factory provides dependency injection and service instantiation:
+```go
+func (n *ServiceProvider) GetService(ctx context.Context, cmd string) (interface{}, error) {
+ switch cmd {
+ case "testsuite":
+ return testsuite.NewTSExecutor(n.cfg, n.logger, false)
+ // ... other services
+ }
+}
+```
+
+### Usage
+```bash
+# Execute a test suite
+keploy testsuite --base-url "http://localhost:8080" --ts-path ./keploy/testsuite --ts-file suite-0.yaml
+
+# Or simply passing the base URL keeping the defalut value.
+keploy testsuite --base-url "http://localhost:8080"
+```
+
+---
+
+## Load Testing Feature
+
+### Purpose
+The Load Testing feature enables performance testing by executing test suites under various load profiles. It provides comprehensive metrics collection and threshold-based pass/fail criteria.
+
+### Architecture
+
+#### Core Components
+
+1. **LoadTester** (`pkg/service/load/load.go`)
+ - Main coordinator for load test execution
+ - Manages test suite parsing and load configuration
+ - Generates comprehensive load test reports
+
+2. **Scheduler** (`pkg/service/load/scheduler.go`)
+ - Orchestrates virtual user (VU) execution
+ - Supports multiple load profiles (constant, ramping)
+ - Manages timing and synchronization
+
+3. **VUWorker** (`pkg/service/load/vu_worker.go`)
+ - Individual virtual user implementation
+ - Executes test suite repeatedly under load
+ - Collects per-VU metrics and timing data
+
+4. **MetricsCollector** (`pkg/service/load/metrics_collector.go`)
+ - Aggregates metrics from all virtual users
+ - Provides centralized data collection point
+ - Calculates aggregate statistics
+
+5. **ThresholdEvaluator** (`pkg/service/load/threshold_evaluator.go`)
+ - Evaluates performance thresholds
+ - Determines pass/fail status for load tests
+ - Supports various metric types and conditions
+
+#### Load Profiles
+
+1. **Constant VUs Profile**
+ - Maintains constant number of virtual users
+ - Consistent load throughout test duration
+ ```yaml
+ load:
+ profile: constant_vus
+ vus: 10
+ duration: 5m
+ rps: 50
+ ```
+
+2. **Ramping VUs Profile**
+ - Gradually increases/decreases virtual users
+ - Supports multiple stages with different targets
+ ```yaml
+ load:
+ profile: ramping_vus
+ vus: 30
+ duration: 3m
+ stages:
+ - duration: 30s
+ target: 10
+ - duration: 2m
+ target: 30
+ ```
+
+#### Metrics and Thresholds
+
+The system collects comprehensive metrics:
+- **Request metrics**: Count, failure rate, response times
+- **Performance metrics**: P90,95,99 latency, throughput
+- **Data transfer**: Bytes sent/received
+
+Thresholds provide pass/fail criteria:
+```yaml
+thresholds:
+ - metric: http_req_duration_p95
+ condition: "< 500ms"
+ severity: high
+ comment: "Ensure 95% of requests are below 500ms latency"
+ - metric: http_req_failed_rate
+ condition: "<= 1%"
+ severity: critical
+ comment: "Error rate must stay under 1%"
+```
+
+### Implementation Details
+
+#### CLI Integration (`cli/load.go`)
+```go
+func Load(ctx context.Context, logger *zap.Logger, _ *config.Config,
+ serviceFactory ServiceFactory, cmdConfigurator CmdConfigurator) *cobra.Command {
+ var cmd = &cobra.Command{
+ Use: "load",
+ Short: "load test a given testsuite.",
+ Example: `keploy load -f test_suite.yaml --out json > report.json`,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ // CLI parameter extraction
+ vus, _ := cmd.Flags().GetInt("vus")
+ duration, _ := cmd.Flags().GetString("duration")
+ rps, _ := cmd.Flags().GetInt("rps")
+
+ // Context with CLI overrides
+ ctx := context.WithValue(ctx, "vus", vus)
+ ctx = context.WithValue(ctx, "duration", duration)
+ ctx = context.WithValue(ctx, "rps", rps)
+
+ return ltSvc.Start(ctx)
+ },
+ }
+}
+```
+
+#### Integrated Dashboard
+- **Built-in Web Server**: Embedded dashboard server integrated directly into Keploy binary
+- **Auto-Browser Launch**: Automatically opens browser to dashboard URL when load test starts
+- **DashboardExposer** (`pkg/service/load/dashboard_exposer.go`):
+ - Serves embedded React dashboard from Go binary
+ - Provides real-time metrics API endpoints
+- **Exporter** (`pkg/service/load/exporter.go`): Real-time metrics streaming to dashboard
+- **Live Monitoring**: Real-time updates of VU count, RPS, response times, and threshold status
+- **Zero Configuration**: No external dependencies or separate dashboard setup required
+
+#### Dashboard Architecture
+
+The dashboard system is fully integrated into the Keploy binary using Go's embed feature:
+
+```go
+//go:embed out/*
+var content embed.FS
+
+func (de *DashboardExposer) fileSystem() http.FileSystem {
+ fsys, err := fs.Sub(content, "out")
+ return http.FS(fsys)
+}
+```
+
+**Key Features:**
+- **Embedded Static Files**: Frontend assets bundled into Go binary at compile time
+- **Smart Browser Detection**: Cross-platform browser launching with WSL support
+
+### Advanced Features
+
+1. **Rate Limiting**: Precise RPS control using token bucket algorithm
+2. **Integrated Dashboard**: Built-in web dashboard that automatically launches in browser
+ - **Embedded Server**: Dashboard server runs within Keploy binary (no external dependencies)
+ - **Auto-Launch**: Browser automatically opens to `http://localhost:3000` when load test starts
+ - **Real-time Monitoring**: Live metrics updates including VU count, RPS, response times
+ - **Threshold Visualization**: Real-time pass/fail status of performance thresholds
+3. **Report Generation**: JSON and CLI-formatted comprehensive reports
+4. **CLI Overrides**: Command-line parameters override YAML configuration
+5. **Threshold Evaluation**: Automated pass/fail determination with live status updates
+
+### Usage
+```bash
+# Basic load test (automatically opens dashboard in browser)
+keploy load -f ./keploy/suite-0.yaml --base-url "http://localhost:8080"
+
+# With CLI overrides (dashboard launches at http://localhost:3000)
+keploy load -f ./keploy/suite-0.yaml --base-url "http://localhost:8080" --vus 20 --duration 10m --rps 100
+```
+
+---
+
+## Security Checking Feature
+
+### Purpose
+The Security Checking feature provides automated security vulnerability scanning for API endpoints. It executes both built-in and custom security checks against API responses and requests.
+
+### Architecture
+
+#### Core Components
+
+1. **SecurityChecker** (`pkg/service/secure/secure.go`)
+ - Main security scanning engine
+ - Manages built-in and custom security checks
+ - Executes checks against test suite results
+
+2. **Security Checks System**
+ - Built-in checks for common vulnerabilities
+ - Custom check support via YAML configuration
+ - Severity-based filtering and reporting
+
+3. **AllowList System**
+ - Filter false positives
+ - Customizable per header, key, or pattern
+ - Integration with test suite configuration
+
+#### Security Check Types
+
+The system includes various built-in security checks:
+
+1. **Header Security Checks**
+ - Missing security headers (HSTS, CSP, X-Frame-Options)
+ - Insecure header values
+ - Information disclosure in headers
+
+2. **Response Body Checks**
+ - Sensitive data exposure
+ - Error message leakage
+ - Debug information disclosure
+
+3. **Cookie Security**
+ - Missing secure flags
+ - HttpOnly flag validation
+ - SameSite attribute checking
+
+4. **General Security**
+ - HTTP vs HTTPS enforcement
+ - Server information disclosure
+ - Version information exposure
+
+#### Custom Security Checks
+
+Users can define custom security checks:
+```yaml
+# custom-checks.yaml
+- id: custom_header_check
+ name: Custom Header Validation
+ description: Check for custom security header
+ severity: MEDIUM
+ type: header
+ target: response
+ key: X-Custom-Security
+ operation: exists
+ status: enabled
+```
+
+### Implementation Details
+
+#### CLI Integration (`cli/secure.go`)
+The security feature provides multiple subcommands:
+- `keploy secure` - Run security checks
+- `keploy secure add` - Add custom security check
+- `keploy secure remove` - Remove custom security check
+- `keploy secure update` - Update custom security check
+- `keploy secure list` - List available security checks
+
+#### Security Check Execution
+```go
+type SecurityCheck struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Severity string `json:"severity"` // "CRITICAL", "HIGH", "MEDIUM", "LOW"
+ Type string `json:"type"` // "header", "body", "cookie", "url"
+ Target string `json:"target"` // "request", "response"
+ Key string `json:"key"` // Header name, JSON path, etc.
+ Value string `json:"value,omitempty"` // Expected value or pattern
+ Operation string `json:"operation"` // "exists", "equals", "contains", "regex"
+ Status string `json:"status"` // "enabled", "disabled"
+}
+```
+
+#### Integration with TestSuite
+Security checks are executed against test suite results:
+1. Test suite executes API calls
+2. Security checker analyzes requests/responses
+3. Checks are applied based on configuration
+4. Results are filtered by severity and allowlists
+5. Comprehensive security report is generated
+
+### Usage
+```bash
+# Run security checks
+keploy secure --base-url "http://localhost:8080"
+
+# List available checks
+keploy secure list --rule-set basic
+
+# Add custom check
+keploy secure add
+
+# Run with custom severity threshold
+keploy secure --severity-threshold HIGH
+```
+
+---
+
+## Integration and Architecture
+
+### Service Factory Pattern
+
+All three features are integrated through a unified service factory pattern:
+
+```go
+// cli/provider/service.go
+func (n *ServiceProvider) GetService(ctx context.Context, cmd string) (interface{}, error) {
+ switch cmd {
+ case "secure":
+ return secure.NewSecurityChecker(n.cfg, n.logger)
+ case "load":
+ return load.NewLoadTester(n.cfg, n.logger)
+ case "testsuite":
+ return testsuite.NewTSExecutor(n.cfg, n.logger, false)
+ // ... other services
+ }
+}
+```
+
+### Shared Configuration
+
+All features share common configuration patterns:
+
+```yaml
+# keploy.yml
+testSuite:
+ tsPath: keploy/testsuite
+ tsFile: suite-0.yaml
+ baseUrl: ""
+
+load:
+ vus: 0
+ duration: ""
+ rps: 0
+ output: ""
+
+# Suite configuration (suite-0.yaml)
+spec:
+ security:
+ ruleset: basic
+ severity_threshold: MEDIUM
+ disable: []
+ allowlist:
+ headers: ["Server"]
+ keys: ["data.debug"]
+
+ load:
+ profile: constant_vus
+ vus: 10
+ duration: 5m
+ thresholds:
+ - metric: http_req_duration_p95
+ condition: "< 500ms"
+ severity: high
+```
+
+### Cross-Feature Integration
+
+1. **Load Testing + Security**: Load tests can include security checking
+2. **TestSuite Foundation**: Both load and security features build on testsuite execution
+3. **Shared Reporting**: Common report formats and output options
+4. **Configuration Inheritance**: Features inherit and extend base configurations
+
+---
+
+## Usage Examples
+
+### Complete Test Suite Example
+
+```yaml
+# suite-0.yaml
+version: api.keploy.io/v2beta1
+kind: TestSuite
+name: Todo_CRUD_Operations
+spec:
+ metadata:
+ description: Test CRUD operations for Todo API
+
+ security:
+ ruleset: basic
+ severity_threshold: MEDIUM
+ allowlist:
+ headers: ["Server"]
+ keys: ["data.debug"]
+
+ load:
+ profile: ramping_vus
+ vus: 30
+ duration: 3m
+ stages:
+ - duration: 30s
+ target: 10
+ - duration: 2m
+ target: 30
+ thresholds:
+ - metric: http_req_duration_p95
+ condition: "< 500ms"
+ severity: high
+
+ steps:
+ - name: Create_todo
+ method: POST
+ url: /todos
+ headers:
+ Content-Type: application/json
+ body: |
+ {
+ "title": "Test Todo",
+ "completed": false
+ }
+ extract:
+ todo_id: id
+ assert:
+ - type: status_code
+ expected_string: "201"
+
+ - name: Get_todo
+ method: GET
+ url: /todos/{{todo_id}}
+ assert:
+ - type: status_code
+ expected_string: "200"
+```
+
+### Command Usage Examples
+
+```bash
+# 1. Test functionality
+keploy testsuite --base-url "http://localhost:8080"
+
+# 2. Perform load testing (dashboard launches for real-time monitoring)
+keploy load --base-url "http://localhost:8080"
+
+# 3. Security scanning
+keploy secure --base-url "http://localhost:8080"
+```
+
+---
+
+## Configuration
+
+### Global Configuration (`keploy.yml`)
+
+```yaml
+testSuite:
+ tsPath: keploy/testsuite # Path to test suite directory
+ tsFile: suite-0.yaml # Test suite file name
+ baseUrl: "" # Base URL for API endpoints
+```
+
+### Feature-Specific Configuration
+
+Each feature supports extensive configuration through both YAML files and CLI flags, providing flexibility for different testing scenarios and environments.
+
+---
+
+## Conclusion
+
+These three features provide a comprehensive testing solution that covers functional testing, performance testing, and security scanning. The modular architecture and shared configuration patterns make them easy to use individually or in combination, while the integration with Keploy's existing ecosystem ensures seamless workflow integration.
+
+The features are designed with extensibility in mind, supporting custom security checks, flexible load profiles, and comprehensive reporting options to meet diverse testing requirements.
diff --git a/keploy/keploy.sh b/keploy/keploy.sh
new file mode 100644
index 0000000..2fe4f30
--- /dev/null
+++ b/keploy/keploy.sh
@@ -0,0 +1,295 @@
+#!/bin/bash
+
+installKeploy (){
+ version="latest"
+ IS_CI=false
+ NO_ROOT=false
+ PLATFORM="$(basename "$SHELL")"
+ for arg in "$@"
+ do
+ case $arg in
+ -isCI)
+ IS_CI=true
+ shift
+ ;;
+ -v)
+ if [[ "$2" =~ ^v[0-9]+.* ]]; then
+ version="$2"
+ shift 2
+ else
+ echo "Invalid version format. Please use '-v v'."
+ return 1
+ fi
+ ;;
+ -noRoot)
+ NO_ROOT=true
+ shift 1
+ ;;
+ -platform)
+ PLATFORM="$2"
+ shift 2
+ ;;
+ *)
+ ;;
+ esac
+ done
+ if [ "$version" != "latest" ]; then
+ echo "Installing Keploy version: $version......"
+ fi
+
+ move_keploy_binary() {
+ # Check if NO_ROOT is set to true
+ if [ "$NO_ROOT" = "true" ]; then
+ # Move without sudo
+ target_dir="$HOME/.keploy/bin"
+ source_dir="/tmp/keploy" # Default source directory
+
+ # Create the target directory in the user's home directory
+ mkdir -p "$target_dir"
+ if [ $? -ne 0 ]; then
+ echo "Error: Failed to create directory $target_dir"
+ exit 1
+ fi
+
+ # Check if the OS is macOS (Darwin) to set the correct source path
+ OS_NAME=$(uname) # Get the operating system name
+ if [ "$OS_NAME" = "Darwin" ]; then
+ source_dir="/tmp/keploy/keploy" # Set source directory to the binary inside the extracted folder
+ fi
+
+ # Move the keploy binary to the user's home directory bin
+ if [ -f "$source_dir" ]; then
+ mv "$source_dir" "$target_dir/keploy"
+ if [ $? -ne 0 ]; then
+ echo "Error: Failed to move the keploy binary from $source_dir to $target_dir"
+ exit 1
+ fi
+ else
+ echo "Error: $source_dir does not exist."
+ exit 1
+ fi
+
+ # Make sure the binary is executable
+ chmod +x "$target_dir/keploy"
+ if [ $? -ne 0 ]; then
+ echo "Error: Failed to make the keploy binary executable"
+ exit 1
+ fi
+ else
+ source_dir="/tmp/keploy"
+ OS_NAME=$(uname) # Get the operating system name
+ if [ "$OS_NAME" = "Darwin" ]; then
+ source_dir="/tmp/keploy/keploy" # Set source directory to the binary inside the extracted folder
+ fi
+ sudo mkdir -p /usr/local/bin && sudo mv "$source_dir" /usr/local/bin/keploy
+ fi
+ set_alias
+ }
+
+ install_keploy_darwin_all() {
+ if [ "$version" != "latest" ]; then
+ download_url="https://github.com/keploy/keploy/releases/download/$version/keploy_darwin_all.tar.gz"
+ else
+ download_url="https://github.com/keploy/keploy/releases/latest/download/keploy_darwin_all.tar.gz"
+ fi
+ # macOS tar does not support --overwrite option so we need to remove the directory first
+ # to avoid the "File exists" error
+ rm -rf /tmp/keploy
+ mkdir -p /tmp/keploy
+ curl --silent --location "$download_url" | tar xz -C /tmp/keploy/
+ move_keploy_binary
+ delete_keploy_alias
+ }
+
+ install_keploy_arm() {
+ if [ "$version" != "latest" ]; then
+ download_url="https://github.com/keploy/keploy/releases/download/$version/keploy_linux_arm64.tar.gz"
+ else
+ download_url="https://github.com/keploy/keploy/releases/latest/download/keploy_linux_arm64.tar.gz"
+ fi
+ curl --silent --location "$download_url" | tar xz --overwrite -C /tmp
+ move_keploy_binary
+ }
+
+
+ install_keploy_amd() {
+ if [ "$version" != "latest" ]; then
+ download_url="https://github.com/keploy/keploy/releases/download/$version/keploy_linux_amd64.tar.gz"
+ else
+ download_url="https://github.com/keploy/keploy/releases/latest/download/keploy_linux_amd64.tar.gz"
+ fi
+ curl --silent --location "$download_url" | tar xz --overwrite -C /tmp
+ move_keploy_binary
+ }
+
+ append_to_rc() {
+ last_byte=$(tail -c 1 $2)
+ if [[ "$last_byte" != "" ]]; then
+ echo -e "\n$1" >> $2
+ else
+ echo "$1" >> $2
+ fi
+ source $2
+ }
+
+ update_path() {
+ PATH_CMD="export PATH=\"\$HOME/.keploy/bin:\$PATH\""
+ rc_file="$1"
+ if [ -f "$rc_file" ]; then
+ if ! grep -q "$PATH_CMD" "$rc_file"; then
+ append_to_rc "$PATH_CMD" "$rc_file"
+ fi
+ else
+ export PATH="$PATH_CMD"
+ fi
+ }
+
+ # Get the alias to set and set it
+ set_alias() {
+ current_shell="$PLATFORM"
+ if [ "$NO_ROOT" = "true" ]; then
+ # Just update the PATH in .zshrc or .bashrc, no alias needed
+ if [[ "$current_shell" = "zsh" || "$current_shell" = "-zsh" ]]; then
+ update_path "$HOME/.zshrc"
+ elif [[ "$current_shell" = "bash" || "$current_shell" = "-bash" ]]; then
+ update_path "$HOME/.bashrc"
+ else
+ update_path "$HOME/.profile"
+ fi
+ else
+ ALIAS_CMD="alias keploy='sudo -E env PATH=\"\$PATH\" keploy'"
+ # Handle zsh or bash for non-macOS systems
+ if [[ "$current_shell" = "zsh" || "$current_shell" = "-zsh" ]]; then
+ if [ -f "$HOME/.zshrc" ]; then
+ if grep -q "alias keploy=" "$HOME/.zshrc"; then
+ sed -i '/alias keploy/d' "$HOME/.zshrc"
+ fi
+ append_to_rc "$ALIAS_CMD" ~/.zshrc
+ else
+ alias keploy="$ALIAS_CMD"
+ fi
+ elif [[ "$current_shell" = "bash" || "$current_shell" = "-bash" ]]; then
+ if [ -f "$HOME/.bashrc" ]; then
+ if grep -q "alias keploy=" "$HOME/.bashrc"; then
+ sed -i '/alias keploy/d' "$HOME/.bashrc"
+ fi
+ append_to_rc "$ALIAS_CMD" ~/.bashrc
+ else
+ alias keploy="$ALIAS_CMD"
+ fi
+ else
+ if [ -f "$HOME/.profile" ]; then
+ if grep -q "alias keploy=" "$HOME/.profile"; then
+ sed -i '/alias keploy/d' "$HOME/.profile"
+ fi
+ append_to_rc "$ALIAS_CMD" ~/.profile
+ else
+ alias keploy="$ALIAS_CMD"
+ fi
+ fi
+
+ fi
+
+ }
+
+ delete_keploy_alias() {
+ current_shell="$PLATFORM"
+ shell_rc_file=""
+ # Determine the shell configuration file based on the current shell
+ if [[ "$current_shell" = "zsh" || "$current_shell" = "-zsh" ]]; then
+ shell_rc_file="$HOME/.zshrc"
+ elif [[ "$current_shell" = "bash" || "$current_shell" = "-bash" ]]; then
+ shell_rc_file="$HOME/.bashrc"
+ else
+ echo "Unsupported shell: $current_shell"
+ return
+ fi
+ # Delete alias from the shell configuration file if it exists
+ if [ -f "$shell_rc_file" ]; then
+ if grep -q "alias keploy=" "$shell_rc_file"; then
+ if [[ "$(uname)" = "Darwin" ]]; then
+ sed -i '' '/alias keploy/d' "$shell_rc_file"
+ else
+ sed -i '/alias keploy/d' "$shell_rc_file"
+ fi
+ fi
+ fi
+ # Unset the alias in the current shell session if it exists
+ if alias keploy &>/dev/null; then
+ unalias keploy
+ fi
+ }
+
+ cleanup_tmp() {
+ # Remove extracted files /tmp directory
+ tmp_files=("LICENSE" "README.md" "READMEes-Es.md" "README-UnitGen.md")
+ for file in "${tmp_files[@]}"; do
+ if [ -f "/tmp/$file" ]; then
+ if [ "$NO_ROOT" = "true" ]; then
+ rm -rf "/tmp/$file"
+ else
+ sudo rm -rf "/tmp/$file"
+ fi
+
+ fi
+ done
+ }
+
+ ARCH=$(uname -m)
+
+ OS_NAME="$(uname -s)"
+ if [ "$OS_NAME" = "Darwin" ]; then
+ NO_ROOT=true
+ fi
+
+ if [ "$IS_CI" = false ]; then
+ OS_NAME="$(uname -s)"
+ if [ "$OS_NAME" = "Darwin" ]; then
+ cleanup_tmp
+ install_keploy_darwin_all
+ return
+ elif [ "$OS_NAME" = "Linux" ]; then
+ if [ "$NO_ROOT" = false ]; then
+ if ! mountpoint -q /sys/kernel/debug; then
+ sudo mount -t debugfs debugfs /sys/kernel/debug
+ fi
+ fi
+ if [ "$ARCH" = "x86_64" ]; then
+ cleanup_tmp
+ install_keploy_amd
+ elif [ "$ARCH" = "aarch64" ]; then
+ cleanup_tmp
+ install_keploy_arm
+ else
+ echo "Unsupported architecture: $ARCH"
+ return
+ fi
+ elif [[ "$OS_NAME" == MINGW32_NT* ]]; then
+ echo "\e]8;; https://pureinfotech.com/install-windows-subsystem-linux-2-windows-10\aWindows not supported please run on WSL2\e]8;;\a"
+ elif [[ "$OS_NAME" == MINGW64_NT* ]]; then
+ echo "\e]8;; https://pureinfotech.com/install-windows-subsystem-linux-2-windows-10\aWindows not supported please run on WSL2\e]8;;\a"
+ else
+ echo "Unknown OS, install Linux to run Keploy"
+ fi
+ else
+ if [ "$ARCH" = "x86_64" ]; then
+ cleanup_tmp
+ install_keploy_amd
+ elif [ "$ARCH" = "aarch64" ]; then
+ cleanup_tmp
+ install_keploy_arm
+ else
+ echo "Unsupported architecture: $ARCH"
+ return
+ fi
+ fi
+}
+
+installKeploy "$@"
+
+if command -v keploy &> /dev/null; then
+ keploy example
+ cleanup_tmp
+ rm -rf keploy.sh
+ rm -rf install.sh
+fi
\ No newline at end of file
diff --git a/keploy/main.go b/keploy/main.go
new file mode 100755
index 0000000..acea761
--- /dev/null
+++ b/keploy/main.go
@@ -0,0 +1,117 @@
+// Package main is the entry point for the keploy application.
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "strings"
+
+ "go.keploy.io/server/v2/cli"
+ "go.keploy.io/server/v2/cli/provider"
+ "go.keploy.io/server/v2/config"
+ "go.keploy.io/server/v2/pkg/platform/auth"
+ userDb "go.keploy.io/server/v2/pkg/platform/yaml/configdb/user"
+ "go.keploy.io/server/v2/utils"
+ "go.keploy.io/server/v2/utils/log"
+ //pprof for debugging
+ // "net/http"
+ // _ "net/http/pprof"
+)
+
+// version is the version of the server and will be injected during build by ldflags, same with dsn
+// see https://goreleaser.com/customization/build/
+
+var version string
+var dsn string
+var apiServerURI = "http://localhost:8083"
+var gitHubClientID = "Iv23liFBvIVhL29i9BAp"
+
+func main() {
+ // Uncomment the following code to enable pprof for debugging
+ // go func() {
+ // fmt.Println("Starting pprof server for debugging...")
+ // err := http.ListenAndServe("localhost:6060", nil)
+ // if err != nil {
+ // fmt.Println("Failed to start the pprof server for debugging", err)
+ // return
+ // }
+ // }()
+ setVersion()
+ ctx := utils.NewCtx()
+ start(ctx)
+ os.Exit(utils.ErrCode)
+}
+
+func setVersion() {
+ if version == "" {
+ version = "2-dev"
+ }
+ utils.Version = version
+ utils.VersionIdenitfier = "version"
+}
+
+func start(ctx context.Context) {
+ logger, logFile, err := log.New()
+ if err != nil {
+ fmt.Println("Failed to start the logger for the CLI", err)
+ return
+ }
+ utils.LogFile = logFile
+
+ defer func() {
+ inDocker := os.Getenv("KEPLOY_INDOCKER")
+ if inDocker != "true" {
+ if utils.LogFile != nil {
+ err := utils.LogFile.Close()
+ if err != nil {
+ utils.LogError(logger, err, "Failed to close Keploy Logs")
+ }
+ }
+ if err := utils.DeleteFileIfNotExists(logger, "keploy-logs.txt"); err != nil {
+ return
+ }
+ if err := utils.DeleteFileIfNotExists(logger, "docker-compose-tmp.yaml"); err != nil {
+ return
+ }
+ }
+ }()
+ defer utils.Recover(logger)
+
+ // The 'umask' command is commonly used in various operating systems to regulate the permissions of newly created files.
+ // These 'umask' values subtract from the permissions assigned by the process, effectively lowering the permissions.
+ // For example, if a file is created with permissions '777' and the 'umask' is '022', the resulting permissions will be '755',
+ // reducing certain permissions for security purposes.
+ // Setting 'umask' to '0' ensures that 'keploy' can precisely control the permissions of the files it creates.
+ // However, it's important to note that this approach may not work in scenarios involving mounted volumes,
+ // as the 'umask' is set by the host system, and cannot be overridden by 'keploy' or individual processes.
+ oldMask := utils.SetUmask()
+ defer utils.RestoreUmask(oldMask)
+
+ if dsn != "" {
+ utils.SentryInit(logger, dsn)
+ //logger = utils.ModifyToSentryLogger(ctx, logger, sentry.CurrentHub().Client(), configDb)
+ }
+ conf := config.New()
+ conf.APIServerURL = apiServerURI
+ conf.GitHubClientID = gitHubClientID
+ userDb := userDb.New(logger, conf)
+ conf.InstallationID, err = userDb.GetInstallationID(ctx)
+ if err != nil {
+ errMsg := "failed to get installation id"
+ utils.LogError(logger, err, errMsg)
+ os.Exit(1)
+ }
+ auth := auth.New(conf.APIServerURL, conf.InstallationID, logger, conf.GitHubClientID)
+
+ svcProvider := provider.NewServiceProvider(logger, conf, auth)
+ cmdConfigurator := provider.NewCmdConfigurator(logger, conf)
+ rootCmd := cli.Root(ctx, logger, svcProvider, cmdConfigurator)
+ if err := rootCmd.Execute(); err != nil {
+ if strings.HasPrefix(err.Error(), "unknown command") || strings.HasPrefix(err.Error(), "unknown shorthand") {
+ fmt.Println("Error: ", err.Error())
+ fmt.Println("Run 'keploy --help' for usage.")
+ os.Exit(1)
+ }
+ }
+}
diff --git a/keploy/oss-pledge.json b/keploy/oss-pledge.json
new file mode 100644
index 0000000..c3d34d1
--- /dev/null
+++ b/keploy/oss-pledge.json
@@ -0,0 +1,14 @@
+{
+ "name": "Keploy Inc",
+ "description": "Keploy is an open-source, zero-code testing tool that makes API and microservices testing simplify and helps developers boost code coverage. As an open-source project itself, we use a lot of open-source projects and we think it's fit to give it back to community.",
+ "urlLearnMore": "https://keploy.io/blog/community/introducing-the-keploy-oss-fund",
+ "urlSquareLogoWithBackground": "https://avatars.githubusercontent.com/u/92252339",
+ "annualReports": [
+ {
+ "url": "https://keploy.io/blog/community/introducing-the-keploy-oss-fund",
+ "dateYearEnding": "2024-12-31",
+ "averageNumberOfDevs": 12,
+ "payments": 12500
+ }
+ ]
+}
diff --git a/keploy/package-lock.json b/keploy/package-lock.json
new file mode 100644
index 0000000..1ef61ac
--- /dev/null
+++ b/keploy/package-lock.json
@@ -0,0 +1,6 @@
+{
+ "name": "keploy",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {}
+}
diff --git a/keploy/pkg/README.md b/keploy/pkg/README.md
new file mode 100755
index 0000000..7d0c1a5
--- /dev/null
+++ b/keploy/pkg/README.md
@@ -0,0 +1,6 @@
+# PKG Documentation
+
+This package comprises interface methods and Go structs,
+which can be utilized by external applications. It's
+employed to execute logic and store data for the CLI
+commands.
\ No newline at end of file
diff --git a/keploy/pkg/core/app/app.go b/keploy/pkg/core/app/app.go
new file mode 100644
index 0000000..de959c2
--- /dev/null
+++ b/keploy/pkg/core/app/app.go
@@ -0,0 +1,582 @@
+//go:build linux
+
+// Package app provides functionality for managing applications.
+package app
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "os/exec"
+ "syscall"
+ "time"
+
+ "golang.org/x/sync/errgroup"
+
+ "go.keploy.io/server/v2/pkg/models"
+
+ "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/events"
+ "github.com/docker/docker/api/types/filters"
+ "go.keploy.io/server/v2/pkg/platform/docker"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func NewApp(logger *zap.Logger, id uint64, cmd string, client docker.Client, opts Options) *App {
+ app := &App{
+ logger: logger,
+ id: id,
+ cmd: cmd,
+ docker: client,
+ kind: utils.FindDockerCmd(cmd),
+ keployContainer: "keploy-v2",
+ container: opts.Container,
+ containerDelay: opts.DockerDelay,
+ containerNetwork: opts.DockerNetwork,
+ }
+ return app
+}
+
+type App struct {
+ logger *zap.Logger
+ docker docker.Client
+ id uint64
+ cmd string
+ kind utils.CmdType
+ containerDelay uint64
+ container string
+ containerNetwork string
+ containerIPv4 chan string
+ keployNetwork string
+ keployContainer string
+ keployIPv4 string
+ inodeChan chan uint64
+ EnableTesting bool
+ Mode models.Mode
+}
+
+type Options struct {
+ // canExit disables any error returned if the app exits by itself.
+ //CanExit bool
+ Container string
+ DockerDelay uint64
+ DockerNetwork string
+}
+
+func (a *App) Setup(_ context.Context) error {
+
+ if utils.IsDockerCmd(a.kind) && isDetachMode(a.logger, a.cmd, a.kind) {
+ return fmt.Errorf("application could not be started in detached mode")
+ }
+
+ switch a.kind {
+ case utils.DockerRun, utils.DockerStart:
+ err := a.SetupDocker()
+ if err != nil {
+ return err
+ }
+ case utils.DockerCompose:
+ err := a.SetupCompose()
+ if err != nil {
+ return err
+ }
+ default:
+ // setup native binary
+ }
+ return nil
+}
+
+func (a *App) KeployIPv4Addr() string {
+ return a.keployIPv4
+}
+
+func (a *App) ContainerIPv4Addr() string {
+ return <-a.containerIPv4
+}
+func (a *App) SetContainerIPv4Addr(ipAddr string) {
+ a.logger.Debug("setting container IPv4 address", zap.String("ipAddr", ipAddr))
+ a.containerIPv4 <- ipAddr
+}
+
+func (a *App) SetupDocker() error {
+
+ if a.kind == utils.DockerStart {
+ running, err := a.docker.IsContainerRunning(a.container)
+ if err != nil {
+ return err
+ }
+ if running {
+ return fmt.Errorf("docker container is already in running state")
+ }
+ }
+
+ //injecting appNetwork to keploy.
+ err := a.injectNetwork(a.containerNetwork)
+ if err != nil {
+ utils.LogError(a.logger, err, fmt.Sprintf("failed to inject network:%v to the keploy container", a.containerNetwork))
+ return err
+ }
+ return nil
+}
+
+func (a *App) SetupCompose() error {
+ if a.container == "" {
+ utils.LogError(a.logger, nil, "container name not found", zap.String("AppCmd", a.cmd))
+ return errors.New("container name not found")
+ }
+ a.logger.Info("keploy requires docker compose containers to be run with external network")
+ //finding the user docker-compose file in the current directory.
+ // TODO currently we just return the first default docker-compose file found in the current directory
+ // we should add support for multiple docker-compose files by either parsing cmd for path
+ // or by asking the user to provide the path
+ // kdocker-compose.yaml file will be run instead of the user docker-compose.yaml file acc to below cases
+
+ path := findComposeFile(a.cmd)
+ if path == "" {
+ return errors.New("can't find the docker compose file of user. Are you in the right directory? ")
+ }
+
+ a.logger.Info(fmt.Sprintf("Found docker compose file path: %s", path))
+
+ newPath := "docker-compose-tmp.yaml"
+
+ compose, err := a.docker.ReadComposeFile(path)
+ if err != nil {
+ utils.LogError(a.logger, err, "failed to read the compose file")
+ return err
+ }
+ composeChanged := false
+
+ // Check if docker compose file uses relative file names for bind mounts
+ ok := a.docker.HasRelativePath(compose)
+ if ok {
+ err = a.docker.ForceAbsolutePath(compose, path)
+ if err != nil {
+ utils.LogError(a.logger, nil, "failed to convert relative paths to absolute paths in volume mounts in docker compose file")
+ return err
+ }
+ composeChanged = true
+ }
+
+ // Checking info about the network and whether its external:true
+ info := a.docker.GetNetworkInfo(compose)
+
+ if info == nil {
+ info, err = a.docker.SetKeployNetwork(compose)
+ if err != nil {
+ utils.LogError(a.logger, nil, "failed to set default network in the compose file", zap.String("network", a.keployNetwork))
+ return err
+ }
+ composeChanged = true
+ }
+
+ if !info.External {
+ err = a.docker.MakeNetworkExternal(compose)
+ if err != nil {
+ utils.LogError(a.logger, nil, "failed to make the network external in the compose file", zap.String("network", info.Name))
+ return fmt.Errorf("error while updating network to external: %v", err)
+ }
+ composeChanged = true
+ }
+
+ a.keployNetwork = info.Name
+
+ ok, err = a.docker.NetworkExists(a.keployNetwork)
+ if err != nil {
+ utils.LogError(a.logger, nil, "failed to find default network", zap.String("network", a.keployNetwork))
+ return err
+ }
+
+ //if keploy-network doesn't exist locally then create it
+ if !ok {
+ err = a.docker.CreateNetwork(a.keployNetwork)
+ if err != nil {
+ utils.LogError(a.logger, nil, "failed to create default network", zap.String("network", a.keployNetwork))
+ return err
+ }
+ }
+
+ if composeChanged {
+ err = a.docker.WriteComposeFile(compose, newPath)
+ if err != nil {
+ utils.LogError(a.logger, nil, "failed to write the compose file", zap.String("path", newPath))
+ }
+ a.logger.Info("Created new docker-compose for keploy internal use", zap.String("path", newPath))
+ //Now replace the running command to run the kdocker-compose.yaml file instead of user docker compose file.
+ a.cmd = modifyDockerComposeCommand(a.cmd, newPath)
+ }
+
+ if a.containerNetwork == "" {
+ a.containerNetwork = a.keployNetwork
+ }
+ err = a.injectNetwork(a.containerNetwork)
+ if err != nil {
+ utils.LogError(a.logger, err, fmt.Sprintf("failed to inject network:%v to the keploy container", a.containerNetwork))
+ return err
+ }
+ return nil
+}
+
+func (a *App) SetAppCommand(appCommand string) {
+ a.logger.Debug("Setting App Command", zap.String("cmd", appCommand))
+ a.cmd = appCommand
+}
+
+func (a *App) GetAppCommand() string {
+ return a.cmd
+}
+
+func (a *App) Kind(_ context.Context) utils.CmdType {
+ return a.kind
+}
+
+// injectNetwork attaches the given network to the keploy container
+// and also sends the keploy container ip of the new network interface to the kernel space
+func (a *App) injectNetwork(network string) error {
+ // inject the network to the keploy container
+ a.logger.Info(fmt.Sprintf("trying to inject network:%v to the keploy container", network))
+ err := a.docker.AttachNetwork(a.keployContainer, []string{network})
+ if err != nil {
+ utils.LogError(a.logger, nil, "failed to inject network to the keploy container")
+ return err
+ }
+
+ a.keployNetwork = network
+
+ //sending new proxy ip to kernel, since dynamically injected new network has different ip for keploy.
+ inspect, err := a.docker.ContainerInspect(context.Background(), a.keployContainer)
+ if err != nil {
+ utils.LogError(a.logger, nil, fmt.Sprintf("failed to get inspect keploy container:%v", inspect))
+ return err
+ }
+
+ keployNetworks := inspect.NetworkSettings.Networks
+ //Here we considering that the application would use only one custom network.
+ //TODO: handle for application having multiple custom networks
+ //TODO: check the logic for correctness
+ for n, settings := range keployNetworks {
+ if n == network {
+ a.keployIPv4 = settings.IPAddress
+ a.logger.Info("Successfully injected network to the keploy container", zap.Any("Keploy container", a.keployContainer), zap.Any("appNetwork", network), zap.String("keploy container ip", a.keployIPv4))
+ return nil
+ }
+ //if networkName != "bridge" {
+ // network = networkName
+ // newProxyIpString = networkSettings.IPAddress
+ // a.logger.Debug(fmt.Sprintf("Network Name: %s, New Proxy IP: %s\n", networkName, networkSettings.IPAddress))
+ //}
+ }
+ return fmt.Errorf("failed to find the network:%v in the keploy container", network)
+}
+func (a *App) extractMeta(ctx context.Context, e events.Message) (bool, error) {
+
+ if e.Action != "start" {
+ return false, nil
+ }
+ // Fetch container details by inspecting using container ID to check if container is created
+ info, err := a.docker.ContainerInspect(ctx, e.ID)
+ if err != nil {
+ a.logger.Debug("failed to inspect container by container Id", zap.Error(err))
+ return false, err
+ }
+
+ // Check if the container's name matches the desired name
+ if info.Name != "/"+a.container {
+ a.logger.Debug("ignoring container creation for unrelated container", zap.String("containerName", info.Name))
+ return false, nil
+ }
+
+ // Set Docker Container ID
+ a.docker.SetContainerID(e.ID)
+ a.logger.Debug("checking for container pid", zap.Any("containerDetails.State.Pid", info.State.Pid))
+ if info.State.Pid == 0 {
+ return false, errors.New("failed to get the pid of the container")
+ }
+ a.logger.Debug("", zap.Any("containerDetails.State.Pid", info.State.Pid), zap.String("containerName", a.container))
+ inode, err := getInode(info.State.Pid)
+ if err != nil {
+ return false, err
+ }
+
+ a.inodeChan <- inode
+ a.logger.Debug("container started and successfully extracted inode", zap.Any("inode", inode))
+ if info.NetworkSettings == nil || info.NetworkSettings.Networks == nil {
+ a.logger.Debug("container network settings not available", zap.Any("containerDetails.NetworkSettings", info.NetworkSettings))
+ return false, nil
+ }
+
+ n, ok := info.NetworkSettings.Networks[a.containerNetwork]
+ if !ok || n == nil {
+ a.logger.Debug("container network not found", zap.Any("containerDetails.NetworkSettings.Networks", info.NetworkSettings.Networks))
+ return false, fmt.Errorf("container network not found: %s", fmt.Sprintf("%+v", info.NetworkSettings.Networks))
+ }
+ a.SetContainerIPv4Addr(n.IPAddress)
+ return inode != 0 && n.IPAddress != "", nil
+}
+
+func (a *App) getDockerMeta(ctx context.Context) <-chan error {
+ // listen for the docker daemon events
+ defer a.logger.Debug("exiting from goroutine of docker daemon event listener")
+
+ errCh := make(chan error, 1)
+ timer := time.NewTimer(time.Duration(a.containerDelay) * time.Second)
+ logTicker := time.NewTicker(1 * time.Second)
+ defer logTicker.Stop()
+
+ eventFilter := filters.NewArgs(
+ filters.KeyValuePair{Key: "type", Value: "container"},
+ filters.KeyValuePair{Key: "type", Value: "network"},
+ filters.KeyValuePair{Key: "action", Value: "create"},
+ filters.KeyValuePair{Key: "action", Value: "connect"},
+ filters.KeyValuePair{Key: "action", Value: "start"},
+ )
+
+ messages, errCh2 := a.docker.Events(ctx, types.EventsOptions{
+ Filters: eventFilter,
+ })
+
+ g, ok := ctx.Value(models.ErrGroupKey).(*errgroup.Group)
+ if !ok {
+ errCh <- errors.New("failed to get the error group from the context")
+ return errCh
+ }
+
+ g.Go(func() error {
+ defer utils.Recover(a.logger)
+ // closing the channels in any case when returning.
+ defer func() {
+ a.logger.Debug("closing err, containerIPv4 and inode channels ")
+ close(errCh)
+ close(a.containerIPv4)
+ close(a.inodeChan)
+ }()
+ for {
+ select {
+ case <-timer.C:
+ errCh <- errors.New("timeout waiting for the container to start")
+ return nil
+ case <-ctx.Done():
+ a.logger.Debug("context cancelled, stopping the listener for container creation event.")
+ errCh <- ctx.Err()
+ return nil
+ case e := <-messages:
+ done, err := a.extractMeta(ctx, e)
+ if err != nil {
+ errCh <- err
+ return nil
+ }
+
+ if done {
+ return nil
+ }
+ // for debugging purposes
+ case <-logTicker.C:
+ a.logger.Debug("still waiting for the container to start.", zap.String("containerName", a.container))
+ return nil
+ case err := <-errCh2:
+ errCh <- err
+ return nil
+ }
+ }
+ })
+ return errCh
+}
+
+func (a *App) runDocker(ctx context.Context) models.AppError {
+ // if a.cmd is empty, it means the user wants to run the application manually,
+ // so we don't need to run the application in a goroutine
+ if a.cmd == "" {
+ return models.AppError{}
+ }
+
+ g, ctx := errgroup.WithContext(ctx)
+ ctx = context.WithValue(ctx, models.ErrGroupKey, g)
+
+ dockerMetaCtx, cancel := context.WithCancel(ctx)
+
+ defer func() {
+ cancel()
+ err := g.Wait()
+ if err != nil {
+ utils.LogError(a.logger, err, "failed to run dockerized app")
+ }
+ }()
+
+ errCh := make(chan error, 1)
+
+ // listen for the "create container" event in order to send the inode of the container to the kernel
+ errCh2 := a.getDockerMeta(dockerMetaCtx)
+
+ g.Go(func() error {
+ defer utils.Recover(a.logger)
+ defer close(errCh)
+ err := a.run(ctx)
+ if err.Err != nil {
+ utils.LogError(a.logger, err.Err, "Application stopped with the error")
+ errCh <- err.Err
+ }
+ return nil
+ })
+
+ select {
+ case err := <-errCh:
+ if err != nil && errors.Is(err, context.Canceled) {
+ return models.AppError{AppErrorType: models.ErrCtxCanceled, Err: ctx.Err()}
+ }
+ return models.AppError{AppErrorType: models.ErrInternal, Err: err}
+ case err := <-errCh2:
+ if err != nil && errors.Is(err, context.Canceled) {
+ return models.AppError{AppErrorType: models.ErrCtxCanceled, Err: ctx.Err()}
+ }
+ return models.AppError{AppErrorType: models.ErrInternal, Err: err}
+ case <-ctx.Done():
+ return models.AppError{AppErrorType: models.ErrCtxCanceled, Err: ctx.Err()}
+ }
+}
+
+func (a *App) Run(ctx context.Context, inodeChan chan uint64) models.AppError {
+ a.inodeChan = inodeChan
+ a.containerIPv4 = make(chan string, 1)
+ if utils.IsDockerCmd(a.kind) {
+ return a.runDocker(ctx)
+ }
+ return a.run(ctx)
+}
+func (a *App) waitTillExit() {
+ timeout := time.NewTimer(30 * time.Second)
+ logTicker := time.NewTicker(1 * time.Second)
+ defer logTicker.Stop()
+ defer timeout.Stop()
+
+ containerID := a.container
+ for {
+ select {
+ case <-logTicker.C:
+ // Inspect the container status
+ containerJSON, err := a.docker.ContainerInspect(context.Background(), containerID)
+ if err != nil {
+ a.logger.Debug("failed to inspect container", zap.String("containerID", containerID), zap.Error(err))
+ return
+ }
+
+ a.logger.Debug("container status", zap.String("status", containerJSON.State.Status), zap.String("containerName", a.container))
+ // Check if container is stopped or dead
+ if containerJSON.State.Status == "exited" || containerJSON.State.Status == "dead" {
+ return
+ }
+ case <-timeout.C:
+ a.logger.Warn("timeout waiting for the container to stop", zap.String("containerID", containerID))
+ return
+ }
+ }
+}
+
+func (a *App) run(ctx context.Context) models.AppError {
+ userCmd := a.cmd
+
+ if utils.FindDockerCmd(a.cmd) == utils.DockerRun {
+ userCmd = utils.EnsureRmBeforeName(userCmd)
+ }
+
+ // Define the function to cancel the command
+ cmdCancel := func(cmd *exec.Cmd) func() error {
+ return func() error {
+ if utils.IsDockerCmd(a.kind) {
+ a.logger.Debug("sending SIGINT to the container", zap.Any("cmd.Process.Pid", cmd.Process.Pid))
+ err := utils.SendSignal(a.logger, -cmd.Process.Pid, syscall.SIGINT)
+
+ return err
+ }
+ return utils.InterruptProcessTree(a.logger, cmd.Process.Pid, syscall.SIGINT)
+ }
+ }
+
+ var err error
+ cmdErr := utils.ExecuteCommand(ctx, a.logger, userCmd, cmdCancel, 25*time.Second)
+ if cmdErr.Err != nil {
+ switch cmdErr.Type {
+ case utils.Init:
+ return models.AppError{AppErrorType: models.ErrCommandError, Err: cmdErr.Err}
+ case utils.Runtime:
+ err = cmdErr.Err
+ }
+ }
+
+ if utils.IsDockerCmd(a.kind) {
+ a.waitTillExit()
+ }
+
+ select {
+ case <-ctx.Done():
+ a.logger.Debug("context cancelled, error while waiting for the app to exit", zap.Error(ctx.Err()))
+ return models.AppError{AppErrorType: models.ErrCtxCanceled, Err: ctx.Err()}
+ default:
+ if a.Mode == models.MODE_RECORD && a.EnableTesting {
+ a.logger.Info("waiting for some time before returning the error to allow recording of test cases when testing keploy with itself")
+ time.Sleep(3 * time.Second)
+ a.logger.Debug("test binary stopped", zap.Error(err))
+ return models.AppError{AppErrorType: models.ErrTestBinStopped, Err: context.Canceled}
+ }
+
+ if err != nil {
+ return models.AppError{AppErrorType: models.ErrUnExpected, Err: err}
+ }
+ return models.AppError{AppErrorType: models.ErrAppStopped, Err: nil}
+ }
+}
+
+//if a.docker.GetContainerID() == "" {
+// a.logger.Debug("still waiting for the container to start.", zap.String("containerName", a.container))
+// continue
+//}
+////Inspecting the application container again since the ip and pid takes some time to be linked to the container.
+//info, err := a.docker.ContainerInspect(ctx, a.container)
+//if err != nil {
+// return err
+//}
+//
+//a.logger.Debug("checking for container pid", zap.Any("containerDetails.State.Pid", info.State.Pid))
+//if info.State.Pid == 0 {
+// a.logger.Debug("container not yet started", zap.Any("containerDetails.State.Pid", info.State.Pid))
+// continue
+//}
+//a.logger.Debug("", zap.Any("containerDetails.State.Pid", info.State.Pid), zap.String("containerName", a.container))
+//a.inode,err = getInode(info.State.Pid)
+//if err != nil {
+// return err
+//}
+//if info.NetworkSettings == nil || info.NetworkSettings.Networks == nil {
+// a.logger.Debug("container network settings not available", zap.Any("containerDetails.NetworkSettings", info.NetworkSettings))
+// continue
+//}
+//
+//n, ok := info.NetworkSettings.Networks[a.containerNetwork]
+//if !ok || n == nil {
+// return errors.New("container network not found")
+//}
+//a.keployIPv4 = n.IPAddress
+//a.logger.Info("container started successfully", zap.Any("", info.NetworkSettings.Networks))
+//return
+
+//case e := <-messages:
+// if e.Type != events.ContainerEventType || e.Action != "start" {
+// continue
+// }
+//
+// // Fetch container details by inspecting using container ID to check if container is created
+// c, err := a.docker.ContainerInspect(ctx, e.ID)
+// if err != nil {
+// a.logger.Debug("failed to inspect container by container Id", zap.Error(err))
+// return err
+// }
+//
+// // Check if the container's name matches the desired name
+// if c.Name != "/"+a.container {
+// a.logger.Debug("ignoring container creation for unrelated container", zap.String("containerName", c.Name))
+// continue
+// }
+// // Set Docker Container ID
+// a.docker.SetContainerID(e.ID)
+//
+// a.logger.Debug("container created for desired app", zap.Any("ID", e.ID))
diff --git a/keploy/pkg/core/app/util.go b/keploy/pkg/core/app/util.go
new file mode 100644
index 0000000..6d374fe
--- /dev/null
+++ b/keploy/pkg/core/app/util.go
@@ -0,0 +1,103 @@
+//go:build linux
+
+package app
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "regexp"
+ "slices"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+func findComposeFile(cmd string) string {
+
+ cmdArgs := strings.Fields(cmd)
+
+ for i := 0; i < len(cmdArgs); i++ {
+ if cmdArgs[i] == "-f" && i+1 < len(cmdArgs) {
+ return cmdArgs[i+1]
+ }
+ }
+
+ filenames := []string{"docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"}
+
+ for _, filename := range filenames {
+ if _, err := os.Stat(filename); !os.IsNotExist(err) {
+ return filename
+ }
+ }
+
+ return ""
+}
+
+func modifyDockerComposeCommand(appCmd, newComposeFile string) string {
+ // Ensure newComposeFile starts with ./
+ if !strings.HasPrefix(newComposeFile, "./") {
+ newComposeFile = "./" + newComposeFile
+ }
+
+ // Define a regular expression pattern to match "-f "
+ pattern := `(-f\s+("[^"]+"|'[^']+'|\S+))`
+ re := regexp.MustCompile(pattern)
+
+ // Check if the "-f " pattern exists in the appCmd
+ if re.MatchString(appCmd) {
+ // Replace it with the new Compose file
+ return re.ReplaceAllString(appCmd, fmt.Sprintf("-f %s", newComposeFile))
+ }
+
+ // If the pattern doesn't exist, inject the new Compose file right after "docker-compose" or "docker compose"
+ upIdx := strings.Index(appCmd, " up")
+ if upIdx != -1 {
+ return fmt.Sprintf("%s -f %s%s", appCmd[:upIdx], newComposeFile, appCmd[upIdx:])
+ }
+
+ return fmt.Sprintf("%s -f %s", appCmd, newComposeFile)
+}
+
+func getInode(pid int) (uint64, error) {
+ path := filepath.Join("/proc", strconv.Itoa(pid), "ns", "pid")
+
+ f, err := os.Stat(path)
+ if err != nil {
+ return 0, err
+ }
+ // Dev := (f.Sys().(*syscall.Stat_t)).Dev
+ i := (f.Sys().(*syscall.Stat_t)).Ino
+ if i == 0 {
+ return 0, fmt.Errorf("failed to get the inode of the process")
+ }
+ return i, nil
+}
+
+func isDetachMode(logger *zap.Logger, command string, kind utils.CmdType) bool {
+ args := strings.Fields(command)
+
+ if kind == utils.DockerStart {
+ flags := []string{"-a", "--attach", "-i", "--interactive"}
+
+ for _, arg := range args {
+ if slices.Contains(flags, arg) {
+ return false
+ }
+ }
+ utils.LogError(logger, fmt.Errorf("docker start require --attach/-a or --interactive/-i flag"), "failed to start command")
+ return true
+ }
+
+ for _, arg := range args {
+ if arg == "-d" || arg == "--detach" {
+ utils.LogError(logger, fmt.Errorf("detach mode is not allowed in Keploy command"), "failed to start command")
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/keploy/pkg/core/core_linux.go b/keploy/pkg/core/core_linux.go
new file mode 100644
index 0000000..3ac1296
--- /dev/null
+++ b/keploy/pkg/core/core_linux.go
@@ -0,0 +1,279 @@
+//go:build linux
+
+// Package core provides functionality for managing core functionalities in Keploy.
+package core
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "sync"
+
+ "golang.org/x/sync/errgroup"
+
+ "go.keploy.io/server/v2/pkg/core/app"
+ "go.keploy.io/server/v2/pkg/core/hooks/structs"
+ "go.keploy.io/server/v2/pkg/models"
+ "go.keploy.io/server/v2/pkg/platform/docker"
+ "go.keploy.io/server/v2/utils"
+ "go.uber.org/zap"
+)
+
+type Core struct {
+ Proxy // embedding the Proxy interface to transfer the proxy methods to the core object
+ Hooks // embedding the Hooks interface to transfer the hooks methods to the core object
+ Tester // embedding the Tester interface to transfer the tester methods to the core object
+ dockerClient docker.Client //embedding the docker client to transfer the docker client methods to the core object
+ logger *zap.Logger
+ id utils.AutoInc
+ apps sync.Map
+ proxyStarted bool
+}
+
+func New(logger *zap.Logger, hook Hooks, proxy Proxy, tester Tester, client docker.Client) *Core {
+ return &Core{
+ logger: logger,
+ Hooks: hook,
+ Proxy: proxy,
+ Tester: tester,
+ dockerClient: client,
+ }
+}
+
+func (c *Core) Setup(ctx context.Context, cmd string, opts models.SetupOptions) (uint64, error) {
+ // create a new app and store it in the map
+ id := uint64(c.id.Next())
+ a := app.NewApp(c.logger, id, cmd, c.dockerClient, app.Options{
+ DockerNetwork: opts.DockerNetwork,
+ Container: opts.Container,
+ DockerDelay: opts.DockerDelay,
+ })
+ c.apps.Store(id, a)
+
+ err := a.Setup(ctx)
+ if err != nil {
+ utils.LogError(c.logger, err, "failed to setup app")
+ return 0, err
+ }
+ return id, nil
+}
+
+func (c *Core) getApp(id uint64) (*app.App, error) {
+ a, ok := c.apps.Load(id)
+ if !ok {
+ return nil, fmt.Errorf("app with id:%v not found", id)
+ }
+
+ // type assertion on the app
+ h, ok := a.(*app.App)
+ if !ok {
+ return nil, fmt.Errorf("failed to type assert app with id:%v", id)
+ }
+
+ return h, nil
+}
+
+func (c *Core) Hook(ctx context.Context, id uint64, opts models.HookOptions) error {
+ hookErr := errors.New("failed to hook into the app")
+
+ a, err := c.getApp(id)
+ if err != nil {
+ utils.LogError(c.logger, err, "failed to get app")
+ return hookErr
+ }
+
+ isDocker := utils.IsDockerCmd(a.Kind(ctx))
+
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ }
+
+ g, ok := ctx.Value(models.ErrGroupKey).(*errgroup.Group)
+ if !ok {
+ return errors.New("failed to get the error group from the context")
+ }
+
+ // Create a new error group for the hooks (Always required)
+ hookErrGrp, _ := errgroup.WithContext(ctx)
+ hookCtx := context.WithoutCancel(ctx) //so that main context doesn't cancel the hookCtx to control the lifecycle of the hooks
+ hookCtx, hookCtxCancel := context.WithCancel(hookCtx)
+ hookCtx = context.WithValue(hookCtx, models.ErrGroupKey, hookErrGrp)
+
+ // create a new error group for the proxy
+ proxyErrGrp, _ := errgroup.WithContext(ctx)
+ proxyCtx := context.WithoutCancel(ctx) //so that main context doesn't cancel the proxyCtx to control the lifecycle of the proxy
+ proxyCtx, proxyCtxCancel := context.WithCancel(proxyCtx)
+ proxyCtx = context.WithValue(proxyCtx, models.ErrGroupKey, proxyErrGrp)
+
+ g.Go(func() error {
+ <-ctx.Done()
+ proxyCtxCancel()
+ err = proxyErrGrp.Wait()
+ if err != nil {
+ utils.LogError(c.logger, err, "failed to stop the proxy")
+ }
+
+ hookCtxCancel()
+ err := hookErrGrp.Wait()
+ if err != nil {
+ utils.LogError(c.logger, err, "failed to unload the hooks")
+ }
+
+ //deleting in order to free the memory in case of rerecord. otherwise different app id will be created for the same app.
+ c.apps.Delete(id)
+ c.id.Reset()
+
+ return nil
+ })
+
+ // Load hooks
+ err = c.Load(hookCtx, id, HookCfg{
+ AppID: id,
+ Pid: 0,
+ IsDocker: isDocker,
+ KeployIPV4: a.KeployIPv4Addr(),
+ Mode: opts.Mode,
+ Rules: opts.Rules,
+ E2E: opts.E2E,
+ Port: opts.Port,
+ })
+ if err != nil {
+ utils.LogError(c.logger, err, "failed to load hooks")
+ return hookErr
+ }
+
+ if c.proxyStarted {
+ c.logger.Debug("Proxy already started")
+ // return nil
+ }
+
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ }
+
+ // TODO: Hooks can be loaded multiple times but proxy should be started only once
+ // if there is another containerized app, then we need to pass new (ip:port) of proxy to the eBPF
+ // as the network namespace is different for each container and so is the keploy/proxy IP to communicate with the app.
+ // start proxy
+ err = c.StartProxy(proxyCtx, ProxyOptions{
+ DNSIPv4Addr: a.KeployIPv4Addr(),
+ //DnsIPv6Addr: ""
+ })
+ if err != nil {
+ utils.LogError(c.logger, err, "failed to start proxy")
+ return hookErr
+ }
+
+ c.proxyStarted = true
+
+ // For keploy test bench
+ if opts.EnableTesting {
+
+ // enable testing in the app
+ a.EnableTesting = true
+ a.Mode = opts.Mode
+
+ // Setting up the test bench
+ err := c.Tester.Setup(ctx, models.TestingOptions{Mode: opts.Mode})
+ if err != nil {
+ utils.LogError(c.logger, err, "error while setting up the test bench environment")
+ return errors.New("failed to setup the test bench")
+ }
+ }
+
+ return nil
+}
+
+// GetHookUnloadDone returns a channel that signals when hooks are completely unloaded
+func (c *Core) GetHookUnloadDone(id uint64) <-chan struct{} {
+ return c.GetUnloadDone()
+}
+
+func (c *Core) Run(ctx context.Context, id uint64, opts models.RunOptions) models.AppError {
+ a, err := c.getApp(id)
+ if err != nil {
+ utils.LogError(c.logger, err, "failed to get app")
+ return models.AppError{AppErrorType: models.ErrInternal, Err: err}
+ }
+
+ runAppErrGrp, runAppCtx := errgroup.WithContext(ctx)
+
+ inodeErrCh := make(chan error, 1)
+ appErrCh := make(chan models.AppError, 1)
+ inodeChan := make(chan uint64, 1) //send inode to the hook
+
+ defer func() {
+ err := runAppErrGrp.Wait()
+ defer close(inodeErrCh)
+ if err != nil {
+ utils.LogError(c.logger, err, "failed to stop the app")
+ }
+ }()
+
+ runAppErrGrp.Go(func() error {
+ defer utils.Recover(c.logger)
+ if a.Kind(ctx) == utils.Native {
+ close(inodeChan) // since we are not using inode in native mode
+ return nil
+ }
+ select {
+ case inode := <-inodeChan:
+ err := c.SendDockerAppInfo(id, structs.DockerAppInfo{AppInode: inode, ClientID: id})
+ if err != nil {
+ utils.LogError(c.logger, err, "")
+
+ inodeErrCh <- errors.New("failed to send inode to the kernel")
+ }
+ case <-ctx.Done():
+ return nil
+ }
+ return nil
+ })
+
+ originalApp := a.GetAppCommand()
+ runAppErrGrp.Go(func() error {
+ defer utils.Recover(c.logger)
+ defer close(appErrCh)
+ defer a.SetAppCommand(originalApp)
+ if opts.AppCommand != "" {
+ a.SetAppCommand(opts.AppCommand)
+ }
+ appErr := a.Run(runAppCtx, inodeChan)
+ if appErr.Err != nil {
+ utils.LogError(c.logger, appErr.Err, "error while running the app")
+ appErrCh <- appErr
+ }
+ return nil
+ })
+
+ select {
+ case <-runAppCtx.Done():
+ c.logger.Debug("Run context cancelled, stopping the app and reverting the app command")
+ return models.AppError{AppErrorType: models.ErrCtxCanceled, Err: nil}
+ case appErr := <-appErrCh:
+ return appErr
+ case inodeErr := <-inodeErrCh:
+ return models.AppError{AppErrorType: models.ErrInternal, Err: inodeErr}
+ }
+}
+
+func (c *Core) GetContainerIP(_ context.Context, id uint64) (string, error) {
+
+ a, err := c.getApp(id)
+ if err != nil {
+ utils.LogError(c.logger, err, "failed to get app")
+ return "", err
+ }
+
+ ip := a.ContainerIPv4Addr()
+ c.logger.Debug("ip address of the target app container", zap.Any("ip", ip))
+ if ip == "" {
+ return "", fmt.Errorf("failed to get the IP address of the app container. Try increasing --delay (in seconds)")
+ }
+
+ return ip, nil
+}
diff --git a/keploy/pkg/core/core_others.go b/keploy/pkg/core/core_others.go
new file mode 100644
index 0000000..bc205ca
--- /dev/null
+++ b/keploy/pkg/core/core_others.go
@@ -0,0 +1,61 @@
+//go:build !linux
+
+// Package core provides functionality for managing core functionalities in Keploy.
+package core
+
+import (
+ "context"
+ "errors"
+ "runtime"
+
+ "go.keploy.io/server/v2/pkg/models"
+ "go.uber.org/zap"
+)
+
+type Core struct {
+ logger *zap.Logger
+}
+
+var errUnsupported = errors.New("instrumentation only supported on linux. Detected OS: " + runtime.GOOS)
+
+func New(logger *zap.Logger) *Core {
+ return &Core{
+ logger: logger,
+ }
+}
+
+func (c *Core) Setup(ctx context.Context, cmd string, opts models.SetupOptions) (uint64, error) {
+ return 0, errUnsupported
+}
+
+func (c *Core) Hook(ctx context.Context, id uint64, opts models.HookOptions) error {
+ return errUnsupported
+}
+
+func (c *Core) GetHookUnloadDone(id uint64) <-chan struct{} {
+ ch := make(chan struct{})
+ close(ch) // Immediately close since no actual hooks are loaded
+ return ch
+}
+
+func (c *Core) MockOutgoing(ctx context.Context, id uint64, opts models.OutgoingOptions) error {
+ return errUnsupported
+}
+
+func (c *Core) SetMocks(ctx context.Context, id uint64, filtered []*models.Mock, unFiltered []*models.Mock) error {
+ return errUnsupported
+}
+
+func (c *Core) GetConsumedMocks(ctx context.Context, id uint64) ([]models.MockState, error) {
+ return nil, errUnsupported
+}
+
+func (c *Core) Run(ctx context.Context, id uint64, _ models.RunOptions) models.AppError {
+ return models.AppError{
+ Err: errUnsupported,
+ }
+}
+
+func (c *Core) GetContainerIP(_ context.Context, id uint64) (string, error) {
+ return "", errUnsupported
+}
diff --git a/keploy/pkg/core/core_others_test.go b/keploy/pkg/core/core_others_test.go
new file mode 100644
index 0000000..b8aaeea
--- /dev/null
+++ b/keploy/pkg/core/core_others_test.go
@@ -0,0 +1,92 @@
+//go:build !linux
+
+package core
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "go.uber.org/zap"
+)
+
+func TestCore_GetHookUnloadDone_NonLinux(t *testing.T) {
+ logger := zap.NewNop()
+
+ // Create a new Core instance using the non-Linux constructor
+ core := New(logger)
+
+ appID := uint64(12345)
+
+ // Test that GetHookUnloadDone returns a channel
+ ch := core.GetHookUnloadDone(appID)
+ assert.NotNil(t, ch, "GetHookUnloadDone should return a channel")
+
+ // For non-Linux platforms, the channel should be immediately closed
+ // since no actual hooks are loaded
+ select {
+ case <-ch:
+ // Expected behavior for non-Linux - channel is immediately closed
+ case <-time.After(100 * time.Millisecond):
+ t.Error("Channel should be immediately closed on non-Linux platforms")
+ }
+}
+
+func TestCore_GetHookUnloadDone_NonLinux_MultipleCalls(t *testing.T) {
+ logger := zap.NewNop()
+ core := New(logger)
+
+ appID := uint64(12345)
+
+ // Get multiple channels - each should be immediately closed
+ ch1 := core.GetHookUnloadDone(appID)
+ ch2 := core.GetHookUnloadDone(appID)
+
+ assert.NotNil(t, ch1, "First call should return a channel")
+ assert.NotNil(t, ch2, "Second call should return a channel")
+
+ // Both channels should be immediately closed
+ select {
+ case <-ch1:
+ // Expected behavior
+ case <-time.After(100 * time.Millisecond):
+ t.Error("First channel should be immediately closed")
+ }
+
+ select {
+ case <-ch2:
+ // Expected behavior
+ case <-time.After(100 * time.Millisecond):
+ t.Error("Second channel should be immediately closed")
+ }
+}
+
+func TestCore_GetHookUnloadDone_NonLinux_DifferentApps(t *testing.T) {
+ logger := zap.NewNop()
+ core := New(logger)
+
+ appID1 := uint64(12345)
+ appID2 := uint64(67890)
+
+ // Get channels for different app IDs
+ ch1 := core.GetHookUnloadDone(appID1)
+ ch2 := core.GetHookUnloadDone(appID2)
+
+ assert.NotNil(t, ch1, "First app should return a channel")
+ assert.NotNil(t, ch2, "Second app should return a channel")
+
+ // Both should be immediately closed
+ select {
+ case <-ch1:
+ // Expected behavior
+ case <-time.After(100 * time.Millisecond):
+ t.Error("Channel for app1 should be immediately closed")
+ }
+
+ select {
+ case <-ch2:
+ // Expected behavior
+ case <-time.After(100 * time.Millisecond):
+ t.Error("Channel for app2 should be immediately closed")
+ }
+}
diff --git a/keploy/pkg/core/hooks/README.md b/keploy/pkg/core/hooks/README.md
new file mode 100755
index 0000000..24c2075
--- /dev/null
+++ b/keploy/pkg/core/hooks/README.md
@@ -0,0 +1,6 @@
+# Hooks Package Documentation
+
+The `hooks` package contains the user-space Go code responsible for
+loading eBPF hooks and eBPF maps, which are used to instrument the user
+API. This package is utilized by the CLI commands. Additionally, it
+launches proxy on a defined port to capture egress calls.
\ No newline at end of file
diff --git a/keploy/pkg/core/hooks/bpf_arm64_bpfel.go b/keploy/pkg/core/hooks/bpf_arm64_bpfel.go
new file mode 100644
index 0000000..bca2d35
--- /dev/null
+++ b/keploy/pkg/core/hooks/bpf_arm64_bpfel.go
@@ -0,0 +1,289 @@
+// Code generated by bpf2go; DO NOT EDIT.
+//go:build arm64
+
+package hooks
+
+import (
+ "bytes"
+ _ "embed"
+ "fmt"
+ "io"
+
+ "github.com/cilium/ebpf"
+)
+
+// loadBpf returns the embedded CollectionSpec for bpf.
+func loadBpf() (*ebpf.CollectionSpec, error) {
+ reader := bytes.NewReader(_BpfBytes)
+ spec, err := ebpf.LoadCollectionSpecFromReader(reader)
+ if err != nil {
+ return nil, fmt.Errorf("can't load bpf: %w", err)
+ }
+
+ return spec, err
+}
+
+// loadBpfObjects loads bpf and converts it into a struct.
+//
+// The following types are suitable as obj argument:
+//
+// *bpfObjects
+// *bpfPrograms
+// *bpfMaps
+//
+// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
+func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
+ spec, err := loadBpf()
+ if err != nil {
+ return err
+ }
+
+ return spec.LoadAndAssign(obj, opts)
+}
+
+// bpfSpecs contains maps and programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type bpfSpecs struct {
+ bpfProgramSpecs
+ bpfMapSpecs
+ bpfVariableSpecs
+}
+
+// bpfProgramSpecs contains programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type bpfProgramSpecs struct {
+ K_connect4 *ebpf.ProgramSpec `ebpf:"k_connect4"`
+ K_connect6 *ebpf.ProgramSpec `ebpf:"k_connect6"`
+ K_getpeername4 *ebpf.ProgramSpec `ebpf:"k_getpeername4"`
+ K_getpeername6 *ebpf.ProgramSpec `ebpf:"k_getpeername6"`
+ SyscallProbeEntryAccept *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_accept"`
+ SyscallProbeEntryAccept4 *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_accept4"`
+ SyscallProbeEntryClose *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_close"`
+ SyscallProbeEntryRead *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_read"`
+ SyscallProbeEntryReadv *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_readv"`
+ SyscallProbeEntryRecvfrom *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_recvfrom"`
+ SyscallProbeEntrySendto *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_sendto"`
+ SyscallProbeEntryTcpV4Connect *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_tcp_v4_connect"`
+ SyscallProbeEntryTcpV4PreConnect *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_tcp_v4_pre_connect"`
+ SyscallProbeEntryTcpV6Connect *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_tcp_v6_connect"`
+ SyscallProbeEntryTcpV6PreConnect *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_tcp_v6_pre_connect"`
+ SyscallProbeEntryUdpPreConnect *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_udp_pre_connect"`
+ SyscallProbeEntryWrite *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_write"`
+ SyscallProbeEntryWritev *ebpf.ProgramSpec `ebpf:"syscall__probe_entry_writev"`
+ SyscallProbeRetAccept *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_accept"`
+ SyscallProbeRetAccept4 *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_accept4"`
+ SyscallProbeRetClose *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_close"`
+ SyscallProbeRetConnect *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_connect"`
+ SyscallProbeRetRead *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_read"`
+ SyscallProbeRetReadv *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_readv"`
+ SyscallProbeRetRecvfrom *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_recvfrom"`
+ SyscallProbeRetSendto *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_sendto"`
+ SyscallProbeRetTcpV4Connect *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_tcp_v4_connect"`
+ SyscallProbeRetTcpV6Connect *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_tcp_v6_connect"`
+ SyscallProbeRetWrite *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_write"`
+ SyscallProbeRetWritev *ebpf.ProgramSpec `ebpf:"syscall__probe_ret_writev"`
+ SyscallProbeEntryConnect *ebpf.ProgramSpec `ebpf:"syscall_probe_entry_connect"`
+ SyscallProbeEntrySocket *ebpf.ProgramSpec `ebpf:"syscall_probe_entry_socket"`
+}
+
+// bpfMapSpecs contains maps before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type bpfMapSpecs struct {
+ ActiveAcceptArgsMap *ebpf.MapSpec `ebpf:"active_accept_args_map"`
+ ActiveCloseArgsMap *ebpf.MapSpec `ebpf:"active_close_args_map"`
+ ActiveReadArgsMap *ebpf.MapSpec `ebpf:"active_read_args_map"`
+ ActiveWriteArgsMap *ebpf.MapSpec `ebpf:"active_write_args_map"`
+ AppChildKernelPidMap *ebpf.MapSpec `ebpf:"app_child_kernel_pid_map"`
+ ConnInfoMap *ebpf.MapSpec `ebpf:"conn_info_map"`
+ CurrentSockMap *ebpf.MapSpec `ebpf:"current_sock_map"`
+ DestInfoMap *ebpf.MapSpec `ebpf:"dest_info_map"`
+ DockerAppRegistrationMap *ebpf.MapSpec `ebpf:"docker_app_registration_map"`
+ E2eInfoMap *ebpf.MapSpec `ebpf:"e2e_info_map"`
+ KeployAgentKernelPidMap *ebpf.MapSpec `ebpf:"keploy_agent_kernel_pid_map"`
+ KeployAgentRegistrationMap *ebpf.MapSpec `ebpf:"keploy_agent_registration_map"`
+ KeployClientKernelPidMap *ebpf.MapSpec `ebpf:"keploy_client_kernel_pid_map"`
+ KeployClientRegistrationMap *ebpf.MapSpec `ebpf:"keploy_client_registration_map"`
+ OutgoingConnCheckMap *ebpf.MapSpec `ebpf:"outgoing_conn_check_map"`
+ OutgoingConnectArgsMap *ebpf.MapSpec `ebpf:"outgoing_connect_args_map"`
+ RedirectProxyMap *ebpf.MapSpec `ebpf:"redirect_proxy_map"`
+ SocketCloseEvents *ebpf.MapSpec `ebpf:"socket_close_events"`
+ SocketDataEventBufferHeap *ebpf.MapSpec `ebpf:"socket_data_event_buffer_heap"`
+ SocketDataEvents *ebpf.MapSpec `ebpf:"socket_data_events"`
+ SocketOpenEvents *ebpf.MapSpec `ebpf:"socket_open_events"`
+ TaskStructMap *ebpf.MapSpec `ebpf:"task_struct_map"`
+}
+
+// bpfVariableSpecs contains global variables before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type bpfVariableSpecs struct {
+}
+
+// bpfObjects contains all objects after they have been loaded into the kernel.
+//
+// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
+type bpfObjects struct {
+ bpfPrograms
+ bpfMaps
+ bpfVariables
+}
+
+func (o *bpfObjects) Close() error {
+ return _BpfClose(
+ &o.bpfPrograms,
+ &o.bpfMaps,
+ )
+}
+
+// bpfMaps contains all maps after they have been loaded into the kernel.
+//
+// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
+type bpfMaps struct {
+ ActiveAcceptArgsMap *ebpf.Map `ebpf:"active_accept_args_map"`
+ ActiveCloseArgsMap *ebpf.Map `ebpf:"active_close_args_map"`
+ ActiveReadArgsMap *ebpf.Map `ebpf:"active_read_args_map"`
+ ActiveWriteArgsMap *ebpf.Map `ebpf:"active_write_args_map"`
+ AppChildKernelPidMap *ebpf.Map `ebpf:"app_child_kernel_pid_map"`
+ ConnInfoMap *ebpf.Map `ebpf:"conn_info_map"`
+ CurrentSockMap *ebpf.Map `ebpf:"current_sock_map"`
+ DestInfoMap *ebpf.Map `ebpf:"dest_info_map"`
+ DockerAppRegistrationMap *ebpf.Map `ebpf:"docker_app_registration_map"`
+ E2eInfoMap *ebpf.Map `ebpf:"e2e_info_map"`
+ KeployAgentKernelPidMap *ebpf.Map `ebpf:"keploy_agent_kernel_pid_map"`
+ KeployAgentRegistrationMap *ebpf.Map `ebpf:"keploy_agent_registration_map"`
+ KeployClientKernelPidMap *ebpf.Map `ebpf:"keploy_client_kernel_pid_map"`
+ KeployClientRegistrationMap *ebpf.Map `ebpf:"keploy_client_registration_map"`
+ OutgoingConnCheckMap *ebpf.Map `ebpf:"outgoing_conn_check_map"`
+ OutgoingConnectArgsMap *ebpf.Map `ebpf:"outgoing_connect_args_map"`
+ RedirectProxyMap *ebpf.Map `ebpf:"redirect_proxy_map"`
+ SocketCloseEvents *ebpf.Map `ebpf:"socket_close_events"`
+ SocketDataEventBufferHeap *ebpf.Map `ebpf:"socket_data_event_buffer_heap"`
+ SocketDataEvents *ebpf.Map `ebpf:"socket_data_events"`
+ SocketOpenEvents *ebpf.Map `ebpf:"socket_open_events"`
+ TaskStructMap *ebpf.Map `ebpf:"task_struct_map"`
+}
+
+func (m *bpfMaps) Close() error {
+ return _BpfClose(
+ m.ActiveAcceptArgsMap,
+ m.ActiveCloseArgsMap,
+ m.ActiveReadArgsMap,
+ m.ActiveWriteArgsMap,
+ m.AppChildKernelPidMap,
+ m.ConnInfoMap,
+ m.CurrentSockMap,
+ m.DestInfoMap,
+ m.DockerAppRegistrationMap,
+ m.E2eInfoMap,
+ m.KeployAgentKernelPidMap,
+ m.KeployAgentRegistrationMap,
+ m.KeployClientKernelPidMap,
+ m.KeployClientRegistrationMap,
+ m.OutgoingConnCheckMap,
+ m.OutgoingConnectArgsMap,
+ m.RedirectProxyMap,
+ m.SocketCloseEvents,
+ m.SocketDataEventBufferHeap,
+ m.SocketDataEvents,
+ m.SocketOpenEvents,
+ m.TaskStructMap,
+ )
+}
+
+// bpfVariables contains all global variables after they have been loaded into the kernel.
+//
+// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
+type bpfVariables struct {
+}
+
+// bpfPrograms contains all programs after they have been loaded into the kernel.
+//
+// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
+type bpfPrograms struct {
+ K_connect4 *ebpf.Program `ebpf:"k_connect4"`
+ K_connect6 *ebpf.Program `ebpf:"k_connect6"`
+ K_getpeername4 *ebpf.Program `ebpf:"k_getpeername4"`
+ K_getpeername6 *ebpf.Program `ebpf:"k_getpeername6"`
+ SyscallProbeEntryAccept *ebpf.Program `ebpf:"syscall__probe_entry_accept"`
+ SyscallProbeEntryAccept4 *ebpf.Program `ebpf:"syscall__probe_entry_accept4"`
+ SyscallProbeEntryClose *ebpf.Program `ebpf:"syscall__probe_entry_close"`
+ SyscallProbeEntryRead *ebpf.Program `ebpf:"syscall__probe_entry_read"`
+ SyscallProbeEntryReadv *ebpf.Program `ebpf:"syscall__probe_entry_readv"`
+ SyscallProbeEntryRecvfrom *ebpf.Program `ebpf:"syscall__probe_entry_recvfrom"`
+ SyscallProbeEntrySendto *ebpf.Program `ebpf:"syscall__probe_entry_sendto"`
+ SyscallProbeEntryTcpV4Connect *ebpf.Program `ebpf:"syscall__probe_entry_tcp_v4_connect"`
+ SyscallProbeEntryTcpV4PreConnect *ebpf.Program `ebpf:"syscall__probe_entry_tcp_v4_pre_connect"`
+ SyscallProbeEntryTcpV6Connect *ebpf.Program `ebpf:"syscall__probe_entry_tcp_v6_connect"`
+ SyscallProbeEntryTcpV6PreConnect *ebpf.Program `ebpf:"syscall__probe_entry_tcp_v6_pre_connect"`
+ SyscallProbeEntryUdpPreConnect *ebpf.Program `ebpf:"syscall__probe_entry_udp_pre_connect"`
+ SyscallProbeEntryWrite *ebpf.Program `ebpf:"syscall__probe_entry_write"`
+ SyscallProbeEntryWritev *ebpf.Program `ebpf:"syscall__probe_entry_writev"`
+ SyscallProbeRetAccept *ebpf.Program `ebpf:"syscall__probe_ret_accept"`
+ SyscallProbeRetAccept4 *ebpf.Program `ebpf:"syscall__probe_ret_accept4"`
+ SyscallProbeRetClose *ebpf.Program `ebpf:"syscall__probe_ret_close"`
+ SyscallProbeRetConnect *ebpf.Program `ebpf:"syscall__probe_ret_connect"`
+ SyscallProbeRetRead *ebpf.Program `ebpf:"syscall__probe_ret_read"`
+ SyscallProbeRetReadv *ebpf.Program `ebpf:"syscall__probe_ret_readv"`
+ SyscallProbeRetRecvfrom *ebpf.Program `ebpf:"syscall__probe_ret_recvfrom"`
+ SyscallProbeRetSendto *ebpf.Program `ebpf:"syscall__probe_ret_sendto"`
+ SyscallProbeRetTcpV4Connect *ebpf.Program `ebpf:"syscall__probe_ret_tcp_v4_connect"`
+ SyscallProbeRetTcpV6Connect *ebpf.Program `ebpf:"syscall__probe_ret_tcp_v6_connect"`
+ SyscallProbeRetWrite *ebpf.Program `ebpf:"syscall__probe_ret_write"`
+ SyscallProbeRetWritev *ebpf.Program `ebpf:"syscall__probe_ret_writev"`
+ SyscallProbeEntryConnect *ebpf.Program `ebpf:"syscall_probe_entry_connect"`
+ SyscallProbeEntrySocket *ebpf.Program `ebpf:"syscall_probe_entry_socket"`
+}
+
+func (p *bpfPrograms) Close() error {
+ return _BpfClose(
+ p.K_connect4,
+ p.K_connect6,
+ p.K_getpeername4,
+ p.K_getpeername6,
+ p.SyscallProbeEntryAccept,
+ p.SyscallProbeEntryAccept4,
+ p.SyscallProbeEntryClose,
+ p.SyscallProbeEntryRead,
+ p.SyscallProbeEntryReadv,
+ p.SyscallProbeEntryRecvfrom,
+ p.SyscallProbeEntrySendto,
+ p.SyscallProbeEntryTcpV4Connect,
+ p.SyscallProbeEntryTcpV4PreConnect,
+ p.SyscallProbeEntryTcpV6Connect,
+ p.SyscallProbeEntryTcpV6PreConnect,
+ p.SyscallProbeEntryUdpPreConnect,
+ p.SyscallProbeEntryWrite,
+ p.SyscallProbeEntryWritev,
+ p.SyscallProbeRetAccept,
+ p.SyscallProbeRetAccept4,
+ p.SyscallProbeRetClose,
+ p.SyscallProbeRetConnect,
+ p.SyscallProbeRetRead,
+ p.SyscallProbeRetReadv,
+ p.SyscallProbeRetRecvfrom,
+ p.SyscallProbeRetSendto,
+ p.SyscallProbeRetTcpV4Connect,
+ p.SyscallProbeRetTcpV6Connect,
+ p.SyscallProbeRetWrite,
+ p.SyscallProbeRetWritev,
+ p.SyscallProbeEntryConnect,
+ p.SyscallProbeEntrySocket,
+ )
+}
+
+func _BpfClose(closers ...io.Closer) error {
+ for _, closer := range closers {
+ if err := closer.Close(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Do not access this directly.
+//
+//go:embed bpf_arm64_bpfel.o
+var _BpfBytes []byte
diff --git a/keploy/pkg/core/hooks/bpf_arm64_bpfel.o b/keploy/pkg/core/hooks/bpf_arm64_bpfel.o
new file mode 100644
index 0000000000000000000000000000000000000000..9f31745df889223e01702d9fdd27821d58253d5d
GIT binary patch
literal 381808
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