DEV Community

Amaan Khan
Amaan Khan

Posted on

One Test Case, Every Layer: REST, OpenAI, AWS, Parameters, and Variables Together

Every step in this walkthrough was generated by describing the scenario in plain English to AutomationCodeless's AI Test Step Assistant — not written by hand. The AI drafted the seven steps, chained the references between them, and picked reasonable field values; from there it was just a matter of reviewing the output and adjusting the parameter IDs and bucket names to match a real environment. Want to see it for yourself? Sign in at automationcodeless.com and you can execute these exact steps.

Most integration bugs live at the boundary between two systems — the place where a unit test can't see, because unit tests mock the boundary away instead of testing it. Add an AI step into the pipeline and you get a second kind of boundary: not just "did the two systems talk to each other correctly," but "did the model's output actually make sense, in a form the rest of the pipeline can use." This walkthrough builds one test case that exercises both — REST, AI classification, and AWS — end-to-end, using Parameters and a Variable to keep it from turning into a hardcoded mess.

The scenario

A support ticket comes in through a REST API. An OpenAI step classifies its severity. If the classification is critical, a downstream Lambda (outside the test — already deployed) writes an alert record to S3. We'll test that this entire chain actually works, not just that each piece works in isolation.

Step 1 — fetch a stored credential instead of hardcoding it

The ticketing API needs a bearer token. Rather than pasting it into every step that needs it, it's stored once as a Parameter and pulled in at runtime:

{
  "step_id": 1,
  "description": "Fetch the internal service token stored as a Parameter",
  "step_type": "core",
  "step_name": "get_parameter",
  "parameter_id": 21
}
Enter fullscreen mode Exit fullscreen mode

Step 2 — create the ticket

{
  "step_id": 2,
  "description": "Create a new support ticket",
  "step_type": "rest",
  "step_name": "post",
  "value": {
    "url": "https://api.example.com/tickets",
    "headers": {
      "Content-Type": "application/json",
      "Authorization": "Bearer ${step[1].result_value}"
    },
    "requestBody": {
      "customerId": 8842,
      "subject": "Payment charged twice for order #5521",
      "description": "I was charged $129.00 twice for the same order. Please refund the duplicate charge immediately, this is affecting my business."
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Typical response:

{
  "id": "TCK-90142",
  "status": "open",
  "createdAt": "2026-08-02T10:12:04Z"
}
Enter fullscreen mode Exit fullscreen mode

Step 3 — store the ticket ID as a variable

The ticket ID gets referenced by several later steps. Rather than repeating ${step[2].result_value.id} everywhere, save it once as a named Variable:

{
  "step_id": 3,
  "description": "Store the new ticket ID for reuse in later steps",
  "step_type": "core",
  "step_name": "set_variable",
  "variable_name": "ticket_id",
  "value": "${step[2].result_value.id}",
  "data_type": "string"
}
Enter fullscreen mode Exit fullscreen mode

Step 4 — classify severity with OpenAI

{
  "step_id": 4,
  "description": "Classify the ticket's severity from its description",
  "step_type": "open_ai",
  "step_name": "gpt-4-turbo",
  "parameter_id": 22,
  "value": "Classify this support ticket's severity as exactly one word — low, medium, high, or critical: ${step[2].result_value.description}"
}
Enter fullscreen mode Exit fullscreen mode

For this ticket, the model returns critical.

Step 5 — assert the AI actually returned something usable

AI output isn't strongly typed the way a database column is. Before anything downstream trusts it, assert it's actually one of the values the rest of the pipeline knows how to handle:

{
  "step_id": 5,
  "description": "Assert the AI returned a recognized severity level",
  "step_type": "assert",
  "step_name": "oneOf",
  "value": "${step[4].result_value}",
  "expected_value": ["low", "medium", "high", "critical"]
}
Enter fullscreen mode Exit fullscreen mode

Step 6 — confirm the downstream alert actually fired

A critical classification should trigger a Lambda (already deployed, outside this test) that writes an alert record to S3. Check that it actually did:

{
  "step_id": 6,
  "description": "Confirm a critical-alert record was written for this ticket",
  "step_type": "aws",
  "step_name": "s3_get_object",
  "value": {
    "bucket": "support-critical-alerts",
    "key": "${ticket_id}.json",
    "region": "us-east-1",
    "wait_for_seconds": 10
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 7 — assert the alert points back to the right ticket

{
  "step_id": 7,
  "description": "Assert the alert record references the same ticket we created",
  "step_type": "assert",
  "step_name": "equal",
  "value": "${step[6].result_value.ticketId}",
  "expected_value": "${ticket_id}"
}
Enter fullscreen mode Exit fullscreen mode

Why chain all of this into one test case

Each of these pieces could be tested in isolation — REST endpoint returns 200, OpenAI returns a string, S3 object exists. None of that proves the pipeline works. The bugs that actually reach production live in the connections: the Lambda trigger that silently stopped firing after a permissions change, the AI prompt that used to return "Critical" (capitalized) until a model update changed its formatting and broke every downstream string match, the ticket ID that gets truncated somewhere between systems. A single chained test case is what catches those — each step only passes if the step before it actually produced something usable.

Common pitfalls

  • Don't skip validating AI output. Treat a model's response as untrusted input, the same as anything from the network — assert its shape and allowed values before anything downstream depends on it.
  • Use variables for anything referenced more than twice. ${step[2].result_value.id} works fine once; repeating it across five steps makes the test fragile to reorder and harder to read. Save it once as a variable instead.
  • Add a wait for asynchronous side effects. The S3 alert in step 6 is written by a Lambda triggered asynchronously — checking too early is a false failure, not a real one.
  • Never hardcode credentials into a step. Pull them from Parameters, the same way step 1 does — it's the difference between updating one value when a token rotates and hunting through every test case that uses it.

Top comments (0)