Test Environment Guide
Scope
This guide describes the minimum isolated test setup for AgentifUI when development and production already use existing PostgreSQL, Redis, and MinIO services.
The goal is data isolation, not full infrastructure isolation.
Files and Profiles
Use:
.env.testfor isolated test runs.env.devfor development.env.prodfor production
Start from the public template:
cp .env.test.example .env.test
You can load it with the shared wrapper:
AGENTIF_ENV_FILE=.env.test bash scripts/with-env-local.sh <command>
The M3 managed CRUD integration test uses an explicitly supplied DATABASE_URL (or PGURL), then .env.test.local / .env.test if neither is set. It never falls back to .env.dev or a built-in database address, and fails before CRUD operations when test configuration is missing. These tests write data: verify that the selected connection is an isolated test database, especially when inheriting environment variables from a shell.
Minimum Isolated Values
Use a dedicated namespace for every shared service:
NODE_ENV=test
NEXT_PUBLIC_APP_URL=http://127.0.0.1:3000
BETTER_AUTH_URL=http://127.0.0.1:3000
DATABASE_URL=postgresql://agentif:agentif@127.0.0.1:5432/agentifui_test
MIGRATOR_DATABASE_URL=postgresql://agentif:agentif@127.0.0.1:5432/agentifui_test
REDIS_URL=redis://127.0.0.1:6379/2
REDIS_PREFIX=agentifui-test
CACHE_L2_KEY_PREFIX=agentifui-test:cache:l2
CACHE_L2_INVALIDATION_CHANNEL=agentifui-test:cache:l2:invalidate
REALTIME_REDIS_CHANNEL=agentifui-test:realtime:events
REALTIME_REDIS_STREAM_KEY=agentifui-test:realtime:events:stream
S3_ENDPOINT=http://127.0.0.1:9000
S3_BUCKET=agentifui-test
S3_ACCESS_KEY_ID=minioadmin
S3_SECRET_ACCESS_KEY=minioadmin
S3_REGION=us-east-1
S3_ENABLE_PATH_STYLE=1
BETTER_AUTH_ENABLED=true
AUTH_BACKEND=better-auth
BETTER_AUTH_SECRET=test-only-better-auth-secret-change-me
API_ENCRYPTION_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
If you run tests inside a dev container, replace 127.0.0.1 with the host gateway that reaches the shared data services.
Why Redis Needs More Than a Different DB
A separate Redis DB such as /2 is necessary but not sufficient.
This project also uses named cache prefixes, invalidation channels, and realtime stream keys. Keep test values explicit so test traffic does not collide with dev or prod.
Relevant variables:
REDIS_PREFIXCACHE_L2_KEY_PREFIXCACHE_L2_INVALIDATION_CHANNELREALTIME_REDIS_CHANNELREALTIME_REDIS_STREAM_KEY
Bootstrap the Test Namespaces
PostgreSQL
Create the test database once:
psql "postgresql://agentif:agentif@127.0.0.1:5432/postgres" -v ON_ERROR_STOP=1 <<'SQL'
CREATE DATABASE agentifui_test;
SQL
Then apply the public SQL migrations:
AGENTIF_ENV_FILE=.env.test bash scripts/with-env-local.sh bash -lc '
shopt -s nullglob
for migration in database/migrations/*.sql; do
psql "$MIGRATOR_DATABASE_URL" -v ON_ERROR_STOP=1 -f "$migration"
done
'
Redis
No extra bootstrap step is required beyond choosing a dedicated DB and test-only prefixes.
MinIO
Create a dedicated bucket once:
mc alias set local http://127.0.0.1:9000 minioadmin minioadmin
mc mb --ignore-existing local/agentifui-test
Running Commands with the Test Profile
Examples:
pnpm build:shared
AGENTIF_ENV_FILE=.env.test bash scripts/with-env-local.sh pnpm test
AGENTIF_ENV_FILE=.env.test bash scripts/with-env-local.sh pnpm build:all
Use targeted commands when possible, but keep the test profile loaded so runtime assumptions match the isolated namespace.
build:all builds shared, API and Web. Environment isolation does not isolate
build output: if a running service uses that output, validate from a separate
source directory instead of overwriting its artifacts.
Authentication email automation should mock or capture the SMTP transport and
must not load real SMTP credentials. The default dev-log mode does not send
mail, but logs test links and tokens; it is not a substitute for testing the SMTP
callback. Use isolated Redis keys when testing recipient limits. Real inbox and
link verification is a separate controlled acceptance step, not part of ordinary
test runs. See email configuration.
CI Parity
__tests__/db/dify-agui-integration.test.ts uses the isolated migrated database,
a non-superuser RLS role, two API instances and a synthetic HTTP/SSE Dify service.
It covers negotiated chatbot identity, single-writer history, quota/idempotency,
replacements, unknown outcomes, failed persistence and client disconnects. It does
not read real backend credentials or establish real-model or deployment acceptance.
Apply all migrations, including the trusted Dify binding constraint increment,
before running it. The existing LangChain and legacy Dify suites remain required
for affected-path compatibility.
GitHub Actions uses the same variable model and applies the same SQL migrations, but it starts temporary PostgreSQL, Redis, and MinIO services on the GitHub runner itself.
The test/build job installs application dependencies once, runs tests, then builds all workspace packages with NODE_ENV=production. MinIO and its mc client are compiled from pinned official upstream commits on a tool-cache miss; the binaries are cached by operating system, architecture, Go version, and both source commits. This avoids relying on unavailable community images or legacy binary downloads. A cold cache takes longer; subsequent runs reuse the binaries. Object data and client configuration remain temporary and are not cached or published.
That means CI does not talk to your existing dev or prod services unless you explicitly point it there.
Isolation Boundary
With the setup above:
- tests do not write into the dev PostgreSQL database
- tests do not write into the prod PostgreSQL database
- tests do not write into dev or prod MinIO buckets
- tests do not share Redis prefixes, invalidation channels, or realtime stream keys with dev or prod
What is still shared in the minimum scheme:
- PostgreSQL server process and port
- Redis server process and port
- MinIO server process and port
- machine resources such as CPU, memory, network, and disk
So this is namespace isolation, not full infrastructure isolation.