Run the Live Tests Against a Real Shop¶
This guide explains how to set up a disposable shop — PrestaShop or WooCommerce — and run tests/Live against it. It also covers the live tiers that need no disposable environment at all: the Positive User (user.com) tiers, which run against a real workspace — see Positive User below.
Live tests exist because a mock can only confirm what the test already believed. They call a real, reachable target instead of Http::fake(), and they are the only tests that can catch an integration encoding a wrong assumption about a third-party API. See Code Quality — Live Smoke Tests for the rules a live test has to follow; this page is about getting a target into a state where they can run at all.
They are never part of composer test or CI, and they skip themselves when unconfigured. Nobody is required to run them — but the person changing PrestaShopIntegration, PrestaShopOutboundWriter, WooCommerceIntegration, WooCommerceOutboundWriter, UserPlatformClient, UserPlatformIntegration, UserPlatformOutboundWriter or UserPlatformConsentMapper should.
tests/Live/SourceLocaleResolutionLiveTest.php needs no setup beyond what's below: it reuses the exact same PRESTASHOP_LIVE_*/WOOCOMMERCE_LIVE_* variables the platform-specific tiers use, skips itself per platform depending on what's configured, and never writes anything — see Source Locale Resolution below.
Prerequisites¶
- The Conflux Docker environment running (
docker compose up -d) - The shop environment for the platform you are testing, checked out next to
conflux/and installed — each repository's README covers its own install:
| Platform | Repository | Install | Reachable at |
|---|---|---|---|
| PrestaShop | sg-prestashop |
./install.sh <version> |
https://www.prestashop.test |
| WooCommerce | sg-woocommerce |
bash ./install.sh <wp-version> <wc-version> |
https://www.woocommerce.test |
Only the platform you are testing needs to be running. The other platform's tests skip themselves.
What Both Platforms Need¶
Three things hold regardless of which shop you point at:
APP_ENV=localis not optional for a shop on amkcertcertificate.PrestaShopIntegration::client()andWooCommerceIntegration::client()only skip TLS verification whenapp()->isLocal()is true, andphpunit.xmlsetsAPP_ENV=testing. Without it every request fails with a cURL 60.- Pass the credentials as
-eflags rather than writing them into.env— a key in.envoutlives the session that needed it. - Leaving any of the variables unset makes the tests skip rather than fail, as does a shop that does not answer, so an accidental run costs nothing.
composer test:live always runs the whole tests/Live directory. Configuring one platform runs that platform's files and skips the other's; set both sets of variables to run everything.
PrestaShop¶
1. Start the shop¶
The shop's containers are usually stopped between sessions. From the sg-prestashop checkout:
It takes roughly half a minute before nginx stops answering 502. Wait for a 200:
2. Find a webservice key¶
PrestaShop's webservice keys live in ps_webservice_account. An installed shop that has been connected to Conflux before already has one:
docker exec prestashop-mysql-1 sh -c \
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" prestashop \
-e "SELECT id_webservice_account, \`key\`, description, active FROM ps_webservice_account;"'
The key needs GET on configurations and both GET and PATCH on customers. Check what it has:
docker exec prestashop-mysql-1 sh -c \
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" prestashop \
-e "SELECT resource, method FROM ps_webservice_permission
WHERE id_webservice_account = <id> AND resource IN (\"customers\",\"configurations\");"'
If no key exists, or the permissions are missing, create one in the back office under Advanced Parameters → Webservice. PS_WEBSERVICE must be 1 in ps_configuration for any of this to answer.
3. Find a customer id¶
docker exec prestashop-mysql-1 sh -c \
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" prestashop \
-e "SELECT id_customer, email, newsletter FROM ps_customer WHERE deleted = 0 LIMIT 5;"'
A stock PrestaShop install ships id_customer = 2 (pub@prestashop.com). Any customer works — the tests read its current state and restore what they change.
4. Run the tests¶
From the conflux checkout:
docker compose exec \
-e APP_ENV=local \
-e PRESTASHOP_LIVE_BASE_URL=https://www.prestashop.test \
-e PRESTASHOP_LIVE_API_KEY=<key from step 2> \
-e PRESTASHOP_LIVE_CUSTOMER_ID=<id from step 3> \
php composer test:live
WooCommerce¶
1. Start the store¶
From the sg-woocommerce checkout:
2. Let the Conflux container reach the store¶
www.woocommerce.test is a network alias on the store's own compose network (sg-woocommerce_default), and nothing joins the Conflux containers to it. PrestaShop needs no equivalent step because sg-prestashop ships a socat sidecar that publishes www.prestashop.test on conflux_default; sg-woocommerce has none, so attach the Conflux PHP container by hand:
An already-attached container answers Error response from daemon: endpoint with name conflux-php-1 already exists in network sg-woocommerce_default — harmless. The attachment is not declared in either compose project, so it has to be redone whenever the Conflux PHP container is recreated. Confirm from the conflux checkout that the store answers:
3. Create a REST API key¶
WooCommerce stores the consumer key as a hash and only the secret in plain text, so an existing key cannot be recovered — you can see that keys exist, not reuse them:
docker compose exec -T mysql sh -c \
'mysql -uroot -proot wordpress \
-e "SELECT key_id, description, permissions, truncated_key FROM wp_woocommerce_api_keys;"'
Create a fresh one in the back office under WooCommerce → Settings → Advanced → REST API keys → Add key (direct link: /wp-admin/admin.php?page=wc-settings&tab=advanced§ion=keys&create-key=1). Permissions must be Read/Write — the write tests PUT to customers/<id>. The ck_…/cs_… pair is shown once, on creation only.
4. Find a customer carrying the newsletter meta¶
The consent tests read the connector plugin's conflux_newsletter user meta off the WC REST customer payload, and skip when the customer carries none. List the customers that have it:
docker compose exec -T mysql sh -c \
'mysql -uroot -proot wordpress \
-e "SELECT user_id, meta_value FROM wp_usermeta WHERE meta_key = \"conflux_newsletter\";"'
If none do, pick a customer and seed it. Until the connector plugin renders the newsletter checkbox, this is the only way the meta gets onto a customer:
docker compose exec -T php wp --allow-root user list --role=customer --fields=ID,user_email
docker compose exec -T php wp --allow-root user meta update <id> conflux_newsletter 1
Both run from the sg-woocommerce checkout. WP-CLI runs as root in that container, hence --allow-root.
5. Run the tests¶
From the conflux checkout:
docker compose exec \
-e APP_ENV=local \
-e WOOCOMMERCE_LIVE_BASE_URL=https://www.woocommerce.test \
-e WOOCOMMERCE_LIVE_CONSUMER_KEY=ck_... \
-e WOOCOMMERCE_LIVE_CONSUMER_SECRET=cs_... \
-e WOOCOMMERCE_LIVE_CUSTOMER_ID=<id from step 4> \
php composer test:live
Positive User¶
Unlike PrestaShop and WooCommerce, this tier needs no disposable environment: it calls a real, already-existing Positive User (user.com) workspace directly — there is no local install to stand up. It does not need APP_ENV=local either — a real workspace runs on a real certificate, and UserPlatformClient makes no TLS accommodation to opt out of.
Four files run against that workspace, and they differ in what they do to it:
| File | What it does |
|---|---|
UserPlatformLiveVerificationTest |
Read-only. GET /api/public/attributes/ reads the workspace's own custom-attribute schema and creates nothing |
UserPlatformLiveProductWriteTest |
Writes. Creates real products and hard-deletes each one again in afterEach(). Every key it uses starts with conflux-live-, so a product left behind by a crashed run stays findable. A single product is read off the product list, because GET products/{id}/ answers with the product's events |
UserPlatformLiveCustomerWriteTest |
Writes. Creates real contacts and hard-deletes each one again in afterEach(). Needs conflux_source to exist as a custom attribute in the workspace first, and every contact it creates carries conflux_source = conflux-live-test so one left behind by a crashed run stays findable |
UserPlatformLiveConsentTest |
Writes, to its own contact only. Creates a contact through the outbound writer and records it in the delivery ledger (the consent pull refuses any contact Conflux did not create), sets email_opt_in on it with a raw People API call, reads the consent back through pullConsent(), and hard-deletes the contact in afterEach() |
UserPlatformLiveConsentWriteTest |
Writes, to its own contact only. Same setup, then writes consent through UserPlatformOutboundWriter's customer_consents update (which fires consent_granted events, see below), reads it back, and hard-deletes the contact in afterEach(). It checks the three assumptions the consent write rests on: that email_opt_in is kept, that consent_granted answers 2xx on the numeric id, and that the mass update moves unsubscribed |
1. Get a workspace subdomain and API token¶
Use any Positive User workspace you have access to and a token issued for it. The subdomain is the part before .user.com in the workspace's own URL; the token is a 64-character user.com API token.
2. Run the tests¶
From the conflux checkout:
docker compose exec \
-e USER_PLATFORM_API_HOST=staginguser.com \
-e USER_PLATFORM_LIVE_SUBDOMAIN=<workspace subdomain, without .user.com> \
-e USER_PLATFORM_LIVE_API_TOKEN=<64-char token> \
php composer test:live -- --filter UserPlatformLive
All four take the same pair, because it is the same credential: the token is workspace-bound and the workspace is named in the host. Narrow the filter to UserPlatformLiveVerification to run only the read-only half.
USER_PLATFORM_API_HOST has to be set in the container's environment too — the apex completing the host has no default, deliberately, so that no environment can address production's apex by forgetting a setting. staginguser.com is the staging estate, user.com production.
Leaving either variable unset skips the tests rather than failing them, same as the shop tiers. The two consent tiers skip without USER_PLATFORM_API_HOST as well.
What deleting the contact does not undo
Every contact the consent tiers create is deleted again, but two effects of the write tier are permanent:
- Events. Each write fires a
consent_grantedevent into the contact's stream. It cannot be withdrawn, and any automation triggering on it will have run. Point the tier at a workspace whose automations you know. - A double opt-in mail, if the confirmed-only rule is relaxed. An unconfirmed subscription makes the writer fire
consent_requested, which asks Positive User to send the contact a real email. No live test fires it:UserPlatformLiveConsentWriteTestonly ever writes confirmed subscriptions and withdrawals, and its projection helper throws for anything else.
Source Locale Resolution (read-only, both platforms)¶
tests/Live/SourceLocaleResolutionLiveTest.php checks that PrestaShopIntegration::resolveSourceLocale() and WooCommerceIntegration::resolveSourceLocale() actually find a locale where each reads one — the field a mock can only assume exists, per the reasoning at the top of this page. It needs no setup beyond the PrestaShop and WooCommerce steps above: it builds its own connections directly from the same PRESTASHOP_LIVE_BASE_URL/PRESTASHOP_LIVE_API_KEY and WOOCOMMERCE_LIVE_BASE_URL/WOOCOMMERCE_LIVE_CONSUMER_KEY/WOOCOMMERCE_LIVE_CONSUMER_SECRET variables, no customer id or key of its own.
Each of its tests skips itself independently per platform, so configuring only one still runs that platform's half:
docker compose exec \
-e APP_ENV=local \
-e PRESTASHOP_LIVE_BASE_URL=https://www.prestashop.test \
-e PRESTASHOP_LIVE_API_KEY=<key from PrestaShop step 2> \
-e WOOCOMMERCE_LIVE_BASE_URL=https://www.woocommerce.test \
-e WOOCOMMERCE_LIVE_CONSUMER_KEY=ck_... \
-e WOOCOMMERCE_LIVE_CONSUMER_SECRET=cs_... \
php composer test:live
It only reads: it resolves the locale against a SystemConnection built in memory rather than a persisted one, so resolveSourceLocale()'s own write-back — which only ever touches a persisted connection — never fires, and there is nothing to leave behind or restore.
What a Run Leaves Behind¶
The write tests really do write to the shop. Both restore the newsletter flag they found, in a finally, so a failing run does not leave the target in the state that failed — but a restore is not a full undo, because each platform writes fields of its own accord.
PrestaShop¶
tests/Live/PrestaShopLiveConsentWriteTest.php leaves two columns behind:
| Column | What PrestaShop does |
|---|---|
newsletter_date_add |
Stamped with the current time when the flag goes to 1; flipping the flag back to 0 leaves the stamp standing |
ip_registration_newsletter |
An empty string is rewritten to NULL on the same save |
Both are the shop's behaviour rather than the writer's, and both are deliberately excluded from the test's before/after comparison. To put a customer back exactly as it was:
docker exec prestashop-mysql-1 sh -c \
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" prestashop -e "
SET SESSION sql_mode=\"\";
UPDATE ps_customer
SET newsletter = 0,
newsletter_date_add = \"0000-00-00 00:00:00\",
ip_registration_newsletter = \"\"
WHERE id_customer = <id>;"'
SET SESSION sql_mode="" is needed because 0000-00-00 00:00:00 is PrestaShop's own null-date sentinel and strict mode refuses it.
WooCommerce¶
tests/Live/WooCommerceLiveConsentWriteTest.php leaves date_modified/date_modified_gmt advanced: WooCommerce touches them on any save, so a run moves them twice — once for the write under test, once for the restore. Nothing else the store holds is touched, and there is nothing to put back by hand.
A Green Run Is Not Always a Confirmation¶
The timezone test says nothing about a shop on UTC
writes the opt-in instant as shop-local wall-clock time exists because the consent writer once put the opt-in into newsletter_date_add in the app's timezone instead of the shop's. It catches that by comparing the stored wall-clock value against the zone the shop reports in PS_TIMEZONE.
Conflux runs on UTC (config/app.php). If the shop is also on UTC, both zones agree, the conversion is the identity, and the test passes no matter how broken the conversion is.
Check before trusting it:
docker exec prestashop-mysql-1 sh -c \
'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" prestashop -N \
-e "SELECT value FROM ps_configuration WHERE name = \"PS_TIMEZONE\";"'
Anything other than UTC gives the test teeth. A stock install picks up the timezone chosen during setup, so a shop installed with German or French defaults (Europe/Berlin, Europe/Paris) is already fine. If yours reports UTC, change it in the back office under Shop Parameters → General → Localization, or the test is decoration.
The same caution generalises: a live test is worth exactly what the live target can distinguish. When adding one, ask what the target would have to answer for the test to fail, and whether your shop can answer that way.
Stopping the Shop¶
Both shops are independent of the Conflux environment, so leave them running or shut them down as you like:
See Also¶
- Code Quality — Live Smoke Tests — the rules a live test has to follow, including the extra obligations of one that writes
- PrestaShop Integration reference — the endpoints and mappings the PrestaShop tests exercise
- WooCommerce Integration reference — the endpoints and mappings the WooCommerce tests exercise
- User Platform Integration reference — the endpoints the Positive User tiers exercise, including the consent tiers