From 20af1ebae5800b82b63e0cdfafae2a43f263c99d Mon Sep 17 00:00:00 2001 From: Charan Kamarapu Date: Tue, 28 Apr 2026 22:28:22 +0530 Subject: [PATCH] Add keploy sandbox CI workflow and fixed-width timestamp marshalling - Stable Order JSON timestamps via fixedNanoLayout so response byte width is consistent across runs (deterministic for keploy mock matching). - New .github/workflows/keploy-sandbox.yml replays linked sandbox suites on pull requests against the producer service. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/keploy-sandbox.yml | 69 ++++++++++++++++++++++++++++ producer/models/order.go | 22 ++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/keploy-sandbox.yml diff --git a/.github/workflows/keploy-sandbox.yml b/.github/workflows/keploy-sandbox.yml new file mode 100644 index 0000000..07f7d2c --- /dev/null +++ b/.github/workflows/keploy-sandbox.yml @@ -0,0 +1,69 @@ +# Auto-generated by keploy scaffold_pipeline_workflow — edit freely. +# Replays linked sandbox test suites for orderflowproducer on every pull request. + +name: Keploy Sandbox Tests +on: + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + keploy-sandbox: + runs-on: ubuntu-latest + # Single source of truth for per-app values. Change any of these + # without hunting through the steps below. + env: + COMPOSE_FILE: docker-compose.yml + APP_SERVICE: producer + CONTAINER_NAME: orderflow-producer + APP_ID: 69f0c0c5b5416c13d84e5c2c + APP_PORT: "8080" + steps: + - uses: actions/checkout@v4 + + - name: Install keploy enterprise + run: | + curl --silent -O -L https://keploy.io/ent/install.sh + sudo bash install.sh + keploy --version + + - name: Bring up app dependencies + run: | + docker compose -f "$COMPOSE_FILE" up -d --wait + docker compose -f "$COMPOSE_FILE" stop "$APP_SERVICE" + docker compose -f "$COMPOSE_FILE" rm -f "$APP_SERVICE" + + - name: Run keploy sandbox tests + env: + KEPLOY_API_KEY: ${{ secrets.KEPLOY_API_KEY }} + run: | + # --create-branch uses find-or-create semantics — first run on + # a PR creates the Keploy branch named after the git branch; + # subsequent runs (force-pushes, retries) reuse the existing + # Keploy branch instead of erroring on the name conflict. + # The Keploy branch name is auto-stamped onto the run's + # CIMetadata.Branch for dashboard display, so there's no + # separate --branch flag any more (dropped to disambiguate + # from the new Keploy-branch flags). + keploy test sandbox \ + -c "docker compose -f $COMPOSE_FILE up $APP_SERVICE" \ + --container-name "$CONTAINER_NAME" \ + --cloud-app-id "$APP_ID" \ + --app-url "http://localhost:$APP_PORT" \ + --create-branch "${{ github.head_ref }}" + + - name: Upload keploy reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: keploy-reports + path: keploy/reports + if-no-files-found: ignore + + - name: Dump compose logs on failure + if: failure() + run: docker compose -f "$COMPOSE_FILE" logs --tail=200 + + - name: Tear down + if: always() + run: docker compose -f "$COMPOSE_FILE" down -v diff --git a/producer/models/order.go b/producer/models/order.go index 471b280..4be1e0e 100644 --- a/producer/models/order.go +++ b/producer/models/order.go @@ -1,6 +1,13 @@ package models -import "time" +import ( + "encoding/json" + "time" +) + +// fixedNanoLayout always emits 9 fractional-second digits so the JSON byte +// width of timestamps is constant across runs. +const fixedNanoLayout = "2006-01-02T15:04:05.000000000Z" type Order struct { ID string `json:"id"` @@ -14,6 +21,19 @@ type Order struct { UpdatedAt time.Time `json:"updated_at"` } +func (o Order) MarshalJSON() ([]byte, error) { + type alias Order + return json.Marshal(&struct { + alias + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + }{ + alias: alias(o), + CreatedAt: o.CreatedAt.UTC().Format(fixedNanoLayout), + UpdatedAt: o.UpdatedAt.UTC().Format(fixedNanoLayout), + }) +} + type CreateOrderRequest struct { UserID string `json:"user_id"` ProductName string `json:"product_name"`