Overview
Activepieces tests live in four distinct layers. Each layer owns a different failure mode — picking the wrong layer produces slow, redundant, or silently ineffective tests.The Four Layers
Unit
A single module or function in isolation. Mocks are allowed for collaborators. No real I/O, no real infrastructure. Runs in-process under Vitest.Integration
Multiple modules (and sometimes multiple packages) wired together against real infrastructure — a real database, queue, filesystem, iptables rule set, HTTP server, or V8 isolate. The scope still sits inside a single package, but the collaborators are real. Runs under Vitest, in-process plus local services.E2E
The full product stack, exercised through a public interface — either a browser UI or the public HTTP API. Runs under Playwright against docker-compose or a deployed environment.Smoke
Minimal post-deploy liveness checks against a real running environment. Implemented as bash pluscurl. Runs against production-like stacks after deploy.
Which package owns which layer
Picking the right layer
1
Does the test touch only one module?
It is a Unit test. Keep mocks for collaborators and run it in-process.
2
Does it need real infra (DB, queue, filesystem, iptables, V8 isolate)?
It is an Integration test. Stay within one package, but let the collaborators be real.
3
Does it exercise the full product through a public interface?
It is an E2E test. Write it with Playwright under
packages/tests-e2e/.4
Is it a post-deploy liveness check?
It is a Smoke test. Drop it under
smoke-test/ as a bash + curl script.Common pitfalls
Classification debt
packages/server/api/test/unit/app/core/canary/canary-proxy.integration.test.ts— already carries an.integration.suffix but lives undertest/unit/. Spins up two real Fastify instances listening on real TCP ports. Target: move topackages/server/api/test/integration/ce/core/canary/.packages/server/api/test/integration/ce/authentication/password-hasher.test.ts— pure bcrypt/scrypt logic, nosetupTestEnvironment, no DB, no Fastify. Target: move topackages/server/api/test/unit/app/authentication/.packages/server/engine/test/handler/*.test.ts(13 files) — load real@activepieces/piece-http,@activepieces/piece-data-mapper, etc. and make real outbound HTTP calls from the executor. Target: relabel as integration (either move to a newtest/integration/subtree, or document in the engine test README that the flat folder mixes unit and integration).packages/server/engine/test/core/code/v8-isolate-code-sandbox.test.ts— runs a real V8 isolate. Target: same as above (integration).packages/server/worker/test/piece-installer.test.ts— does real filesystem work against temp dirs; borderline integration. Target: decide on a threshold (temp-dir I/O alone = unit, vs. temp-dir I/O + subprocess = integration) and relabel accordingly.- Duplication:
api/test/integration/ce/flows/flow/flow.test.tsvsapi/test/integration/cloud/flow/flow.test.ts— both assert identical “Create flow” shapes; only role-permission checks differ. Target: extract the shared CRUD assertions into CE and keep only the cloud-specific role permutations undercloud/.
What is NOT duplication (defense-in-depth)
- Flow execution is tested at engine (pure logic), API integration (queue + worker dispatch), and smoke (post-deploy liveness). Each owns different failure modes.
- Sandbox isolation is tested at engine (V8 code sandbox) and worker (process orchestration). Distinct concerns.
Related documentation
- E2E Tests playbook — full-stack browser E2E deep dive
- Per-package READMEs live at
packages/server/api/test/README.md,packages/server/engine/test/README.md,packages/server/worker/test/README.md,packages/tests-e2e/README.md, andsmoke-test/README.md.