Test cloud services
Services often do their work through the cloud: a file lands in a bucket and a job picks it up, results go to a warehouse, and events go to a topic. The cloud packs let a scenario do what the outside world does, such as uploading the file or publishing the event. The scenario then checks what your service did: the row, the object, the message.
Scenario: A claim within the limit is settled when its photo arrives When the evidence/crushed-box.png file is uploaded to the claim-evidence s3 bucket as claims/CLM-4101/crushed-box.png Then within 30s the claims dynamodb table has an item where: | id | CLM-4101 | | status | APPROVED | And the refund-requests sqs queue has a message where: | claim | CLM-4101 | | attribute reason | DAMAGED |The packs
Section titled “The packs”| Cloud | Packs |
|---|---|
| AWS | aws-s3, aws-sqs, aws-sns, aws-eventbridge, aws-dynamodb, on aws-core |
| Google Cloud | gcp-storage, gcp-pubsub, gcp-bigquery, gcp-firestore, on gcp-core |
| Azure | azure-blob, azure-servicebus |
Each pack talks to its service through the cloud’s official SDK. The pack reference has a page for each one, with its steps and settings, grouped by cloud. List the packs you use in axx-packs.yaml (Choose packs). An AWS or Google Cloud pack brings its core with it.
Connect as your service does
Section titled “Connect as your service does”Register the account, project or resource once, usually in the Background. A scenario then uses it everywhere:
Background: Given the parcels aws account with the following properties: | region | eu-west-1 | | endpoint | http://${sys:local.host}:4566 | | access key id | ${env:AWS_ACCESS_KEY_ID:-local} | | secret access key | ${env:AWS_SECRET_ACCESS_KEY:-local} |- AWS:
the {word} aws accounttakes aregion, and optionally anendpoint, aprofileor static keys. Without keys, the SDK’s default credential chain applies. - Google Cloud:
the {word} gcp projecttakes aproject, and optionally anendpointor acredentialskey file. Without them, Application Default Credentials apply. - Azure: each service connects to its own resource.
the {word} azure storage accounttakes aconnection stringor aurl.the {word} service bus namespacetakes aconnection stringor anamespace, plus amanagement endpointfor emulators.
An asyncapi row on an AWS account, a Google Cloud project or a Service Bus namespace names the AsyncAPI document its queues’ and topics’ messages follow (Validate against AsyncAPI).
An endpoint points every service of the account or project at an emulator. Leave it out, and the same features run against the cloud. Axx’s AWS clients also honor AWS_ENDPOINT_URL, but only the account’s endpoint addresses S3 by path, as an S3 emulator needs.
Run the clouds locally
Section titled “Run the clouds locally”Emulators such as floci run AWS, Google Cloud and Azure on your machine: one container per cloud, free, with no account needed. Start one next to your service in the Compose file your service definition runs, and point your service at it the way its SDK expects:
AWS_ENDPOINT_URLfor AWS;STORAGE_EMULATOR_HOST,PUBSUB_EMULATOR_HOSTandFIRESTORE_EMULATOR_HOSTfor Google Cloud;- connection strings for Azure.
services: aws: image: floci/floci:latest ports: ['4566:4566'] environment: FLOCI_HOSTNAME: aws claims: build: ../app environment: AWS_REGION: eu-west-1 AWS_ENDPOINT_URL: http://aws:4566Create the buckets, queues, topics and tables the way your infrastructure code does in a real account. Scenarios bring the data: files, seeds and messages. The examples run a one-shot provision command before the service starts.
For a service that consumes Service Bus messages, use Microsoft’s Service Bus emulator rather than floci’s. With floci-az, a message that arrives while the service is busy with another can stay locked until its lock expires, a minute by default, so the service sees it too late for a check. The customs-clearance example runs Service Bus in Microsoft’s emulator and Blob Storage in floci. Its management API is on its own port: give axx’s namespace management endpoint http://localhost:5300.
Seed data
Section titled “Seed data”A seed is a YAML or JSON file that gives a scenario’s data: the items of DynamoDB tables, the rows of BigQuery tables, or the documents of Firestore collections, by ID.
Given a seeds/shipments.yaml firestore seedFirestore stores dates as timestamps, and a Firestore seed keeps YAML’s dates:
shipments: PX-5101: carrier: KESTREL weightKg: 2.5 weighedAt: 2026-09-01T08:12:00Z # a timestamp manifest: "2026-09-01" # a string- An unquoted date is a timestamp. A date alone (
2026-09-01) is midnight UTC. A date and time can have fractional seconds and an offset (2026-09-01T10:12:00.25+02:00); without an offset, it is in UTC. A value tagged!!timestampis a timestamp too. - A quoted date is a string. Quote a value to keep it a string, such as a manifest named by its date.
- JSON seeds hold strings. JSON has no date type, so the dates of a JSON seed are strings.
Checks read timestamps back in RFC 3339 and UTC, so | weighedAt | 2026-09-01T08:12:00Z | checks the document above.
Send a service messages
Section titled “Send a service messages”A service that consumes messages gets them the way it does in the cloud: publish to the topic it reads, and let the subscription deliver them.
When the disputes/kestrel-overcharge.json message is published to the carrier-disputes pubsub topicA service that Pub/Sub pushes to gets what a push subscription sends: an envelope whose message.data is the message, base64-encoded, with its attributes and ID. Create the push subscription, with the service’s address as its endpoint, where your other emulator resources are created, and publish to its topic: an emulator with push subscriptions, such as the gcloud CLI’s, pushes the message to the service as Pub/Sub does. The scenario neither builds the envelope nor encodes the data.
What the checks do
Section titled “What the checks do”-
They wait. Services act asynchronously, so every check waits: 10 seconds, or the time
within {duration}gives. -
They see the scenario’s messages only. A message check counts only what arrived since its scenario started. Scenarios run in parallel, so match on data unique to the scenario, such as a claim or an invoice ID.
-
Topics and buses cost nobody a message. For the topics and buses a run checks, Axx subscribes a listener of its own before the scenarios start, and removes it when the run ends:
- an SQS queue for an SNS topic;
- a subscription for a Pub/Sub topic;
- a rule and a queue for an EventBridge bus;
- a subscription for a Service Bus topic.
-
Checking a queue takes its messages. On an SQS or Service Bus queue, Axx consumes each message like any other consumer would. So check the queues your service writes to. Check a queue it consumes by what the service does with the messages.
-
Conditions compare text. A
| field | value |row compares the value as text, with a dotted path into nested data.nullmeans null andundefinedmeans absent. On messages,attribute <name>orproperty <name>rows check the values sent with the message. -
Objects are read by their type. Besides its exact content and its JSON properties, a check can look for text in an object, in a PDF, a Word document or a workbook too, or for a row of a CSV file or a workbook (Check files):
Then within 30s the invoices/kestrel-2026-09.pdf object in the carrier-invoices gcs bucket contains "Total due: 1284.50 EUR"And the disputes/kestrel-2026-09.csv object in the carrier-invoices gcs bucket has a row where:| parcel | PX-KES-1001 || reason | weight differs |
Examples
Section titled “Examples”Each of these examples tests a service built on one cloud, with that cloud running in floci:
parcel-claims, on AWS. Shops claim for damaged or lost parcels. Evidence photos in S3 trigger the decision; refunds, decisions and events go out over SQS, SNS and EventBridge.carrier-billing, on Google Cloud. Carriers’ invoices uploaded to Cloud Storage are priced from BigQuery and Firestore, and disputes are published on Pub/Sub.customs-clearance, on Azure. Declarations filed on Service Bus are cleared from invoices in Blob Storage.