diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..85b7177
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,19 @@
+# Example environment for the local test infrastructure (see compose.yaml).
+# Copy to .env (gitignored, never commit real secrets):
+# cp .env.example .env
+
+# --- Postgres ---
+POSTGRES_DB=pks_dev
+POSTGRES_USER=pks_user
+POSTGRES_PASSWORD=changeme
+
+# --- Backend app settings (used by the backend, not by compose) ---
+APP_PORT=8080
+REDIS_PASSWORD=changeme
+
+# --- MinIO (only needed with: docker compose --profile files up -d) ---
+MINIO_ROOT_USER=minioadmin
+MINIO_ROOT_PASSWORD=changeme-minio-root
+MINIO_BUCKET_NAME=local-bucket
+MINIO_ACCESS_TOKEN=changeme-access
+MINIO_SECRET_KEY=changeme-secret
diff --git a/.gitignore b/.gitignore
index 16714c0..b7bbdd2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,9 @@
# Log file
*.log
+# Local environment (see .env.example, never commit real secrets)
+.env
+
# BlueJ files
*.ctxt
diff --git a/README.md b/README.md
index f8e7199..62b3569 100644
--- a/README.md
+++ b/README.md
@@ -11,3 +11,25 @@ git pull --rebase
```plain
Пользователь (USER) <- Модератор (MODER) <- Администратор (ADMIN)
```
+
+## Тесты
+
+Два вида тестов:
+
+- **Unit** (`mvn test`): API мокается, внешнего бэкенда не нужно.
+- **Integration** (классы с `@Tag("integration")`): ходят в живой API без моков,
+ им нужен поднятый бэкенд. Из дефолтной сборки и из CI они исключены
+ (surefire `excludedGroups` в `pom.xml`), чтобы джобы не падали без бэкенда.
+
+Локальный запуск интеграционных тестов:
+
+```bash
+cp .env.example .env
+docker compose up -d
+# для файловых/экспортных тестов: docker compose --profile files up -d
+
+# запустить бэкенд из соседнего репозитория (порт 8080 из APP_PORT):
+# cd ../pks-java && ./mvnw spring-boot:run
+
+mvn test -Dgroups=integration -DexcludedGroups=
+```
diff --git a/compose.yaml b/compose.yaml
new file mode 100644
index 0000000..4d689b3
--- /dev/null
+++ b/compose.yaml
@@ -0,0 +1,82 @@
+# Test infrastructure for DesktopApplicationClientJava integration tests.
+#
+# Mirrors the contract of the backend (pks-java) compose file: same services,
+# same ports, same environment variable names (see .env.example).
+# Run only ONE stack at a time (this one OR the backend one) — ports overlap.
+#
+# Default: postgres + redis (enough for auth/resource/user/admin ITs):
+# docker compose up -d
+# With object storage (for file/export ITs):
+# docker compose --profile files up -d
+
+services:
+ postgres:
+ image: postgres:16-alpine
+ environment:
+ - POSTGRES_DB=${POSTGRES_DB}
+ - POSTGRES_USER=${POSTGRES_USER}
+ - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
+ ports:
+ - '5432:5432'
+ volumes:
+ - postgres_data:/var/lib/postgresql/data
+ restart: unless-stopped
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ redis:
+ image: redis:7-alpine
+ ports:
+ - '6379:6379'
+ volumes:
+ - redis_basic_data:/data
+ restart: unless-stopped
+ healthcheck:
+ test: ["CMD", "redis-cli", "ping"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ minio:
+ image: quay.io/minio/minio
+ profiles: [files]
+ command: server /data --console-address ":9001"
+ environment:
+ - MINIO_ROOT_USER=${MINIO_ROOT_USER}
+ - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
+ ports:
+ - '9000:9000' # S3 API
+ - '9001:9001' # Web Console
+ volumes:
+ - minio_data:/data
+ restart: unless-stopped
+ healthcheck:
+ test: ["CMD-SHELL", "curl -f http://localhost:9000/minio/health/live || exit 1"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ minio-init:
+ image: minio/mc:latest
+ profiles: [files]
+ depends_on:
+ minio:
+ condition: service_healthy
+ entrypoint: >
+ /bin/sh -c "
+ mc alias set myminio http://minio:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD};
+ mc mb myminio/${MINIO_BUCKET_NAME} --ignore-existing;
+ mc admin user add myminio ${MINIO_ACCESS_TOKEN} ${MINIO_SECRET_KEY};
+ mc admin policy attach myminio readwrite --user ${MINIO_ACCESS_TOKEN};
+ exit 0;
+ "
+
+volumes:
+ redis_basic_data:
+ postgres_data:
+ driver: local
+ minio_data:
+ driver: local
diff --git a/pom.xml b/pom.xml
index a5bc40b..3ea95ac 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,6 +18,7 @@
UTF-8
${encoding}
${encoding}
+ integration
@@ -134,6 +135,11 @@
-Djava.awt.headless=true
--enable-native-access=ALL-UNNAMED
+
+ ${excludedGroups}