Skip to content

Orders — acceptance criteria

Placing an order

11 scenarios. These are acceptance criteria for what the service must do, not a description of what it does.

binding spec features/place-order.feature @ 2.3.0 is the spec of record. It states what must be; implementations converge on it, never the reverse.

Enforced by
  • any change must bump the artifact and service versions (check:version); a silent edit fails CI
  • bound by implementations in strict mode, where an unbound scenario fails their build
  • every schema element added to a contract must be named here (check:intent)
  • gherkin-lint
Version history
VersionFirst releasedService version
2.3.02026-09-153.4.0
2.2.02026-09-093.3.0
2.1.02026-08-293.1.0
2.0.02026-08-273.0.0
1.3.02026-08-242.1.0
1.2.02026-08-232.0.0
1.1.02026-08-231.0.0
Consume it

As a customer I want to place an order So that it is fulfilled once payment settles These scenarios are acceptance criteria, not suggestions. An implementation that fails any of them is wrong. If a scenario is itself wrong, that is a versioned change to this file.

A valid order is accepted and its event is guaranteed

  • Whenthe customer places an order for 2 units of SKU-RED at 1250 pence
  • Thenthe response status is 201
  • Andthe order status is placed
  • Andthe order total is 2500 pence
  • Andan OrderPlaced event is guaranteed to be published for the order

The order and its event succeed or fail together

  • Givenevent publication is unavailable
  • Whenthe customer places an order for 1 unit of SKU-RED at 1250 pence
  • Thenthe response status is 201
  • Andthe OrderPlaced event is published once publication recovers
  • Andno order ever exists without its OrderPlaced event

Replaying an idempotency key returns the original order

  • Giventhe customer placed an order with Idempotency-Key header idem-99
  • Whenthe same request is retried with Idempotency-Key header idem-99
  • Thenthe response status is 201
  • Andthe same order_id is returned
  • Andno second OrderPlaced event is published

A conflicting body on a used idempotency key is rejected

  • Giventhe customer placed an order with Idempotency-Key header idem-99
  • Whena different order body is sent with Idempotency-Key header idem-99
  • Thenthe response status is 409
  • Andthe problem body carries an error_code and message

Order events are CloudEvents envelopes

  • Whenthe customer places an order for 2 units of SKU-RED at 1250 pence
  • Thenan OrderPlaced event is published on orders.placed.v2
  • Andthe envelope carries specversion 1.0, a unique id and a time
  • Andthe envelope source is /orders and its type is com.hungovercoders.orders.placed.v2
  • Andthe envelope subject is the order_id, with datacontenttype application/json
  • Andthe data carries the order_id, customer_id, placed_at and total_pence
  • Andhandlers dedupe on the envelope id, not the natural key

Cancellation is a CloudEvents envelope too

  • Giventhe customer placed an order
  • Whenthe order is cancelled because out_of_stock
  • Thenan OrderCancelled event is published on orders.cancelled.v2
  • Andits data carries the order_id, cancelled_at and reason

An order is only cancelled for a declared reason

  • Giventhe customer placed an order
  • Whenthe order is cancelled because <reason>
  • Thenthe cancellation is accepted and records that reason
  • Anda reason outside this list is rejected
  • Examples
  • reasonmeaning
    customer_requestthe customer asked for the order to be cancelled
    payment_failedno payment settled inside the window
    out_of_stockthe order cannot be fulfilled from stock

A settled payment marks the order paid

  • Giventhe customer placed an order
  • Whena PaymentSettled event arrives on payments.settled.v2 for that order_id
  • Thenthe order status is paid
  • Andreplaying the same envelope id does not change the order again

An order can be fetched by its id

  • Giventhe customer placed an order via placeOrder
  • Whenthe order is fetched by order_id via getOrder
  • Thenthe response status is 200
  • Andthe order carries the order_id, customer_id, status and total_pence

Fetching an unknown order returns 404

  • Whenan unknown order_id is fetched via getOrder
  • Thenthe response status is 404
  • Andthe problem body carries an error_code and message

Orders must have at least one valid line

  • Whenthe customer places an order with <lines>
  • Thenthe response status is 400
  • Andthe problem body carries an error_code and message
  • Examples
  • lines
    no lines
    a line quantity of 0
    a negative unit_price_pence
    a line missing its sku
Raw Gherkin
Feature: Placing an order
  As a customer
  I want to place an order
  So that it is fulfilled once payment settles

  These scenarios are acceptance criteria, not suggestions. An
  implementation that fails any of them is wrong. If a scenario is
  itself wrong, that is a versioned change to this file.

  Background:
    Given the customer "c-1001" exists

  Scenario: A valid order is accepted and its event is guaranteed
    When the customer places an order for 2 units of "SKU-RED" at 1250 pence
    Then the response status is 201
    And the order status is "placed"
    And the order total is 2500 pence
    And an "OrderPlaced" event is guaranteed to be published for the order

  Scenario: The order and its event succeed or fail together
    Given event publication is unavailable
    When the customer places an order for 1 unit of "SKU-RED" at 1250 pence
    Then the response status is 201
    And the "OrderPlaced" event is published once publication recovers
    And no order ever exists without its "OrderPlaced" event

  Scenario: Replaying an idempotency key returns the original order
    Given the customer placed an order with "Idempotency-Key" header "idem-99"
    When the same request is retried with "Idempotency-Key" header "idem-99"
    Then the response status is 201
    And the same order_id is returned
    And no second "OrderPlaced" event is published

  Scenario: A conflicting body on a used idempotency key is rejected
    Given the customer placed an order with "Idempotency-Key" header "idem-99"
    When a different order body is sent with "Idempotency-Key" header "idem-99"
    Then the response status is 409
    And the problem body carries an error_code and message

  Scenario: Order events are CloudEvents envelopes
    When the customer places an order for 2 units of "SKU-RED" at 1250 pence
    Then an "OrderPlaced" event is published on "orders.placed.v2"
    And the envelope carries specversion "1.0", a unique id and a time
    And the envelope source is /orders and its type is com.hungovercoders.orders.placed.v2
    And the envelope subject is the order_id, with datacontenttype "application/json"
    And the data carries the order_id, customer_id, placed_at and total_pence
    And handlers dedupe on the envelope id, not the natural key

  Scenario: Cancellation is a CloudEvents envelope too
    Given the customer placed an order
    When the order is cancelled because "out_of_stock"
    Then an "OrderCancelled" event is published on "orders.cancelled.v2"
    And its data carries the order_id, cancelled_at and reason

  Scenario Outline: An order is only cancelled for a declared reason
    Given the customer placed an order
    When the order is cancelled because "<reason>"
    Then the cancellation is accepted and records that reason
    And a reason outside this list is rejected

    Examples:
      | reason           | meaning                                          |
      | customer_request | the customer asked for the order to be cancelled |
      | payment_failed   | no payment settled inside the window             |
      | out_of_stock     | the order cannot be fulfilled from stock         |

  Scenario: A settled payment marks the order paid
    Given the customer placed an order
    When a "PaymentSettled" event arrives on "payments.settled.v2" for that order_id
    Then the order status is "paid"
    And replaying the same envelope id does not change the order again

  Scenario: An order can be fetched by its id
    Given the customer placed an order via placeOrder
    When the order is fetched by order_id via getOrder
    Then the response status is 200
    And the order carries the order_id, customer_id, status and total_pence

  Scenario: Fetching an unknown order returns 404
    When an unknown order_id is fetched via getOrder
    Then the response status is 404
    And the problem body carries an error_code and message

  Scenario Outline: Orders must have at least one valid line
    When the customer places an order with <lines>
    Then the response status is 400
    And the problem body carries an error_code and message

    Examples:
      | lines                       |
      | no lines                    |
      | a line quantity of 0        |
      | a negative unit_price_pence |
      | a line missing its sku      |