Run PostgreSQL and Cassandra integration tests and deploy Compose stack
Docker test deployment / test-build-deploy (push) Failing after 8m35s
Docker test deployment / test-build-deploy (push) Failing after 8m35s
This commit is contained in:
@@ -27,7 +27,7 @@ jobs:
|
||||
SOURCE_REPOSITORY: ${{ gitea.repository }}
|
||||
SOURCE_REVISION: ${{ gitea.sha }}
|
||||
DEPLOY_PORT: ${{ inputs.deploy_port || vars.ADCLOUD_DEPLOY_PORT || '18080' }}
|
||||
PUBLIC_BASE_URL: ${{ inputs.public_base_url || vars.ADCLOUD_PUBLIC_BASE_URL || '' }}
|
||||
PUBLIC_BASE_URL: ${{ inputs.public_base_url || vars.ADCLOUD_PUBLIC_BASE_URL || 'http://47.76.92.159:18080' }}
|
||||
steps:
|
||||
- name: Check Docker daemon and tools
|
||||
run: |
|
||||
@@ -68,12 +68,18 @@ jobs:
|
||||
- name: Build the runtime image
|
||||
run: |
|
||||
docker build --target runtime --tag "adcloud-server-ci:$SOURCE_REVISION" --label "org.opencontainers.image.revision=$SOURCE_REVISION" .
|
||||
- name: Test PostgreSQL and Cassandra integration
|
||||
run: |
|
||||
export DEPLOY_IMAGE="adcloud-server-ci:$SOURCE_REVISION"
|
||||
export TEST_IMAGE="adcloud-server-ci:$SOURCE_REVISION-test"
|
||||
export CI_STACK_NAME="adcloud-ci-$(printf '%s' "$SOURCE_REVISION" | cut -c1-12)-${{ gitea.run_id }}"
|
||||
bash scripts/compose_stack.sh ci
|
||||
- name: Deploy and verify published host port
|
||||
run: |
|
||||
export DEPLOY_IMAGE="adcloud-server-ci:$SOURCE_REVISION"
|
||||
export DEPLOY_REPOSITORY="$SOURCE_REPOSITORY"
|
||||
export DEPLOY_REVISION="$SOURCE_REVISION"
|
||||
bash scripts/deploy_docker.sh
|
||||
bash scripts/compose_stack.sh deploy
|
||||
- name: Probe configured public URL
|
||||
run: |
|
||||
set -eu
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@ RUN groupadd --gid 10001 adcloud && useradd --uid 10001 --gid adcloud --no-creat
|
||||
&& mkdir -p /app/scripts /data /config \
|
||||
&& chown -R adcloud:adcloud /app /data /config && chmod 700 /data /config
|
||||
COPY --from=build /opt/venv /opt/venv
|
||||
COPY scripts/smoke_api.py /app/scripts/smoke_api.py
|
||||
COPY scripts/smoke_api.py scripts/init_cassandra.py /app/scripts/
|
||||
COPY deploy/cassandra.cql /app/deploy/cassandra.cql
|
||||
WORKDIR /app
|
||||
USER 10001:10001
|
||||
EXPOSE 8000
|
||||
|
||||
@@ -80,3 +80,5 @@ uv sync --locked --extra postgres --extra cassandra
|
||||
|
||||
Dockerfile 提供 test、runtime 两个目标;Gitea Actions 在 main 推送后构建、测试并部署到 runner 的 Docker daemon,默认映射宿主机 18080 到容器 8000。
|
||||
详细配置、runner 要求、公网端口、持久化和回滚见 [Docker 部署说明](docs/docker-deployment.md)。
|
||||
|
||||
测试部署使用 `compose.yml` 编排 PostgreSQL、Cassandra、API 与画像 worker;CI 运行全部数据库集成测试,公网 API 默认 http://47.76.92.159:18080/docs。详见 Docker 部署文档。
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
# Test stack only. Only the API publishes a host port.
|
||||
x-app: &app
|
||||
image: ${DEPLOY_IMAGE:?Set DEPLOY_IMAGE}
|
||||
read_only: true
|
||||
cap_drop: [ALL]
|
||||
security_opt: [no-new-privileges:true]
|
||||
tmpfs: [/tmp:rw,noexec,nosuid,size=64m]
|
||||
mem_limit: 512m
|
||||
pids_limit: 128
|
||||
volumes: [config:/config]
|
||||
environment: &app-env
|
||||
DATABASE_URL: postgresql+psycopg://adcloud:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}@postgres:5432/adcloud
|
||||
PROFILE_BACKEND: cassandra
|
||||
CASSANDRA_HOSTS: cassandra
|
||||
CASSANDRA_TLS: 'false'
|
||||
COOKIE_SECURE: 'false'
|
||||
CORS_ORIGINS: ${DEPLOY_CORS_ORIGINS:-http://localhost:3000,http://localhost:5173,http://localhost:5174}
|
||||
depends_on: &app-deps
|
||||
postgres: {condition: service_healthy}
|
||||
cassandra-init: {condition: service_completed_successfully}
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-bookworm
|
||||
restart: unless-stopped
|
||||
mem_limit: 512m
|
||||
environment:
|
||||
POSTGRES_DB: adcloud
|
||||
POSTGRES_USER: adcloud
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}
|
||||
volumes: [postgres-data:/var/lib/postgresql/data]
|
||||
healthcheck:
|
||||
test: [CMD-SHELL, 'pg_isready -U adcloud -d adcloud']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
cassandra:
|
||||
image: cassandra:4.1
|
||||
restart: unless-stopped
|
||||
mem_limit: 2g
|
||||
environment:
|
||||
CASSANDRA_CLUSTER_NAME: AdcloudTest
|
||||
CASSANDRA_DC: datacenter1
|
||||
CASSANDRA_ENDPOINT_SNITCH: GossipingPropertyFileSnitch
|
||||
MAX_HEAP_SIZE: 768M
|
||||
HEAP_NEWSIZE: 256M
|
||||
volumes: [cassandra-data:/var/lib/cassandra]
|
||||
healthcheck:
|
||||
test: [CMD-SHELL, "cqlsh -e 'SELECT release_version FROM system.local' 127.0.0.1"]
|
||||
start_period: 60s
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
cassandra-init:
|
||||
image: ${DEPLOY_IMAGE:?Set DEPLOY_IMAGE}
|
||||
entrypoint: [python, /app/scripts/init_cassandra.py]
|
||||
mem_limit: 256m
|
||||
depends_on:
|
||||
cassandra: {condition: service_healthy}
|
||||
api:
|
||||
<<: *app
|
||||
restart: unless-stopped
|
||||
ports: ['${DEPLOY_BIND_IP:-0.0.0.0}:${DEPLOY_PORT:-18080}:8000']
|
||||
worker:
|
||||
<<: *app
|
||||
restart: unless-stopped
|
||||
command: [adcloud-profiles]
|
||||
healthcheck:
|
||||
disable: true
|
||||
integration:
|
||||
image: ${TEST_IMAGE:?Set TEST_IMAGE}
|
||||
profiles: [ci]
|
||||
depends_on: *app-deps
|
||||
environment:
|
||||
ADCLOUD_TEST_POSTGRES_URL: postgresql+psycopg://adcloud:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}@postgres:5432/adcloud
|
||||
ADCLOUD_TEST_CASSANDRA_HOSTS: cassandra
|
||||
ADCLOUD_TEST_CASSANDRA_KEYSPACE: adcloud
|
||||
ADCLOUD_TEST_CASSANDRA_TLS: 'false'
|
||||
smoke:
|
||||
<<: *app
|
||||
profiles: [checks]
|
||||
entrypoint: [python, /app/scripts/smoke_api.py]
|
||||
command: [--base-url, 'http://api:8000', --require-projection]
|
||||
volumes: [config:/config:ro]
|
||||
depends_on:
|
||||
api: {condition: service_healthy}
|
||||
worker: {condition: service_started}
|
||||
healthcheck:
|
||||
disable: true
|
||||
|
||||
volumes:
|
||||
postgres-data: {}
|
||||
cassandra-data: {}
|
||||
config:
|
||||
name: ${CONFIG_VOLUME:-${COMPOSE_PROJECT_NAME:-adcloud-test}-config}
|
||||
external: ${CONFIG_EXTERNAL:-false}
|
||||
+19
-13
@@ -8,8 +8,8 @@
|
||||
2. 使用 Gitea 自动签发的短期作业 token 下载精确提交的源码,不将认证信息放入 URL、命令行或日志。
|
||||
3. 用 uv 和 uv.lock 构建 test 镜像,执行 Ruff 与 pytest。外部数据库测试未配置环境时跳过。
|
||||
4. 构建不含开发依赖的非 root runtime 镜像,包含 PostgreSQL / Cassandra Python 驱动,允许以后配置存储;不运行数据库服务。
|
||||
5. 在一次性 candidate 容器上验证真实 HTTP 流程。
|
||||
6. 替换测试容器,持久化数据和密钥,映射宿主机端口,再从 Docker daemon 的 host 网络验证请求。
|
||||
5. 通过独立 Compose 项目启动临时 PostgreSQL / Cassandra,执行全部 54 个测试(包含 6 个真实数据库测试);结束后清理该 CI 项目的容器和数据卷。
|
||||
6. 用 Compose 部署持久化 PostgreSQL / Cassandra、API 和画像 worker,检查 JSONB 存储、画像投影与宿主机 HTTP 端口。
|
||||
7. 如提供公网 URL,再探测公网 `/health`。后续可从外部执行匿名和埋点测试。
|
||||
|
||||
Docker 镜像直接保存在该 daemon 上,本版不依赖镜像仓库登录。
|
||||
@@ -65,27 +65,33 @@ Docker socket 具有管理 daemon 的能力,因此部署工作流仅对 main
|
||||
|
||||
```sh
|
||||
docker build --target runtime --tag adcloud-server:test .
|
||||
DEPLOY_IMAGE=adcloud-server:test bash scripts/deploy_docker.sh
|
||||
DEPLOY_IMAGE=adcloud-server:test bash scripts/compose_stack.sh deploy
|
||||
```
|
||||
|
||||
当前固定测试部署配置为 SQLite / relational。未来连接数据库时需给容器传入 DATABASE_URL 等配置,并按需要启动画像 worker;本工作流不创建 PostgreSQL 或 Cassandra。
|
||||
当前公网环境使用 PostgreSQL 16 保存身份图谱和 JSONB 权威画像,Cassandra 4.1 保存画像投影,worker 持续消费 outbox。数据库不发布宿主机端口,只能通过项目 Docker 内网连接。
|
||||
Cassandra 在此合成测试环境内关闭 TLS / 认证,单节点 RF=1;生产环境需另外配置认证、TLS、备份和多节点副本。
|
||||
数据库和应用合计内存限制约 3.5 GiB,服务器应留出至少 4 GiB 可用内存,首次启动 Cassandra 可能需要几分钟。
|
||||
`compose.yml` 使用健康检查和一次性 CQL 初始化任务控制启动顺序。
|
||||
|
||||
临时 CI 使用 `adcloud-ci-<commit>-<run>` 独立项目;PostgreSQL 密码随机生成。Cassandra 测试同时连接 CI PostgreSQL,验证双存储组合。失败时也会清理该项目。
|
||||
|
||||
手动 Compose 操作需通过脚本加载 config 卷中持久化的 PostgreSQL 密码;不要直接输出 `docker compose config`(其中含展开的连接凭据)。
|
||||
|
||||
## 持久化、权限和回滚
|
||||
|
||||
- `adcloud-server-test-data`:SQLite 数据,挂到 `/data`。
|
||||
- `adcloud-server-test-config`:运行密钥,挂到 `/config`。
|
||||
- 首次启动生成随机 IDENTITY_SECRET、SERVICE_TOKEN,原子写入 mode 0600 的配置文件;后续重建复用它们,不输出到流水线日志。
|
||||
- runtime 使用 UID/GID 10001,根文件系统只读,去除 capabilities,启用 no-new-privileges,并限制 CPU、内存和进程数。
|
||||
- 正式测试容器使用 `restart=unless-stopped`,Actions 作业结束后继续运行。
|
||||
- 只替换具有本项目 managed/repository 标签的容器和 volume,不删除其他服务或数据卷。
|
||||
- named lock 防止多个部署同时操作测试容器。取消任务可能遗留 lock;先确认无部署仍在运行,再由服务器管理员检查并清理 `adcloud-server-test-deploy-lock`。
|
||||
- 新容器健康检查或 host-port HTTP 验证失败时,删除失败的新容器并尽可能恢复旧容器。端口替换期间有短暂停机;不保证数据库 schema 迁移可以回滚。
|
||||
- 本版不自动删除旧 Docker 镜像,避免影响其他构建;后续需按保留策略清理本项目镜像。
|
||||
- Compose 项目为 `adcloud-test`;PostgreSQL 和 Cassandra 分别使用项目专属命名数据卷。
|
||||
- `adcloud-server-test-config` 复用原身份密钥卷;首次补充随机 PostgreSQL 密码,重部署复用,配置文件权限 0600,不输出到日志。
|
||||
- API / worker 使用 UID/GID 10001、只读根文件系统、去除 capabilities、限制内存和进程;均使用 `restart=unless-stopped`。
|
||||
- named lock 防止并发部署。确认无部署仍在运行后才能清理遗留的 `adcloud-server-test-deploy-lock`。
|
||||
- 首次切换前先等待数据库就绪;旧 SQLite 容器仅停止,旧数据卷保留,无自动数据迁移。
|
||||
- 首次 Compose 切换失败时尽可能恢复旧 SQLite 容器;后续 Compose 更新不是原子部署,不保证 schema 迁移可回滚。公网 API 切换有短暂停机。
|
||||
- 测试部署不自动清除持久化数据卷或旧镜像。原 `deploy_docker.sh` 保留用于独立 SQLite 部署,不与 Compose 同时运行。
|
||||
|
||||
## HTTP 验证
|
||||
|
||||
`smoke_api.py` 检查系统/模块路由、匿名 Cookie、埋点、未授权拒绝、可信 Stitch、画像继承和历史事件回溯。
|
||||
使用合成邮箱与独立匿名身份,会向测试数据库写入少量演示数据。
|
||||
`--require-projection` 还会验证 PostgreSQL / JSONB 配置,并等待画像状态变为 Cassandra `current`,避免降级到关系型读取掩盖 worker 故障。
|
||||
验证宿主机端口时,临时探测容器挂载配置卷为只读,读取凭据在进程内发送,不将凭据放到命令参数或日志中。
|
||||
|
||||
公网健康检查:
|
||||
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
: "${DEPLOY_IMAGE:?DEPLOY_IMAGE is required}"
|
||||
export TEST_IMAGE=${TEST_IMAGE:-$DEPLOY_IMAGE}
|
||||
mode=${1:-deploy}
|
||||
docker compose version
|
||||
if [[ "$mode" == ci ]]; then
|
||||
: "${CI_STACK_NAME:?CI_STACK_NAME is required}"
|
||||
[[ "$CI_STACK_NAME" =~ ^adcloud-ci-[a-z0-9-]+$ ]] || exit 2
|
||||
export COMPOSE_PROJECT_NAME=$CI_STACK_NAME
|
||||
export POSTGRES_PASSWORD
|
||||
POSTGRES_PASSWORD=$(python3 -c 'import secrets; print(secrets.token_urlsafe(48))')
|
||||
unset CONFIG_VOLUME CONFIG_EXTERNAL
|
||||
# Only this unique disposable CI project is removed, including its volumes.
|
||||
trap 'docker compose -f compose.yml --profile ci down --volumes --remove-orphans >/dev/null' EXIT
|
||||
docker compose -f compose.yml --profile ci run --rm integration
|
||||
exit
|
||||
fi
|
||||
[[ "$mode" == deploy ]] || { echo 'Use ci or deploy'; exit 2; }
|
||||
export DEPLOY_PORT=${DEPLOY_PORT:-18080}
|
||||
[[ "$DEPLOY_PORT" =~ ^[0-9]{1,5}$ ]] && ((10#$DEPLOY_PORT >= 1024 && 10#$DEPLOY_PORT <= 65535)) || exit 2
|
||||
export COMPOSE_PROJECT_NAME=adcloud-test
|
||||
export CONFIG_VOLUME=adcloud-server-test-config CONFIG_EXTERNAL=true
|
||||
lock=adcloud-server-test-deploy-lock
|
||||
lock_acquired=false
|
||||
legacy_stopped=false
|
||||
successful=false
|
||||
cleanup() {
|
||||
local result=$?
|
||||
trap - EXIT
|
||||
if ! $successful && $legacy_stopped; then
|
||||
docker compose -f compose.yml stop api worker >/dev/null || true
|
||||
docker start adcloud-server-test >/dev/null || true
|
||||
echo 'Compose deployment failed; legacy SQLite service restored where possible.'
|
||||
fi
|
||||
if $lock_acquired; then docker rm "$lock" >/dev/null || true; fi
|
||||
exit "$result"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
for attempt in $(seq 1 60); do
|
||||
if docker create --name "$lock" --label io.entroia.adcloud.managed=true --label io.entroia.adcloud.repository=Zayden-Jung/adcloud-server --entrypoint /bin/true "$DEPLOY_IMAGE" >/dev/null 2>&1; then
|
||||
lock_acquired=true; break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
$lock_acquired || { echo 'Deployment lock unavailable'; exit 1; }
|
||||
if ! docker volume inspect "$CONFIG_VOLUME" >/dev/null 2>&1; then
|
||||
docker volume create --label io.entroia.adcloud.repository=Zayden-Jung/adcloud-server "$CONFIG_VOLUME" >/dev/null
|
||||
fi
|
||||
[[ "$(docker volume inspect --format '{{index .Labels "io.entroia.adcloud.repository"}}' "$CONFIG_VOLUME")" == Zayden-Jung/adcloud-server ]] || { echo 'Refusing unmanaged config volume'; exit 1; }
|
||||
# Capture a persistent random password in memory; never echo it or use shell tracing.
|
||||
export POSTGRES_PASSWORD
|
||||
POSTGRES_PASSWORD=$(docker run --rm --volume "$CONFIG_VOLUME:/config" --entrypoint python "$DEPLOY_IMAGE" -c 'from pathlib import Path; from adcloud_server.container_entrypoint import bootstrap; print(bootstrap(Path("/config/identity.env"))["POSTGRES_PASSWORD"])')
|
||||
# Initialize databases before switching the public API port.
|
||||
docker compose -f compose.yml up -d postgres cassandra
|
||||
docker compose -f compose.yml run --rm cassandra-init
|
||||
if docker container inspect adcloud-server-test >/dev/null 2>&1; then
|
||||
[[ "$(docker inspect --format '{{index .Config.Labels "io.entroia.adcloud.repository"}}' adcloud-server-test)" == Zayden-Jung/adcloud-server ]] || { echo 'Refusing unmanaged legacy container'; exit 1; }
|
||||
docker stop --time 20 adcloud-server-test >/dev/null
|
||||
legacy_stopped=true
|
||||
fi
|
||||
docker compose -f compose.yml up -d --wait --wait-timeout 180 api worker
|
||||
docker compose -f compose.yml --profile checks run --rm smoke
|
||||
# Verify the published host port too; config stays read-only in this probe.
|
||||
docker run --rm --network host --volume "$CONFIG_VOLUME:/config:ro" --entrypoint python "$DEPLOY_IMAGE" /app/scripts/smoke_api.py --base-url "http://127.0.0.1:$DEPLOY_PORT" --require-projection
|
||||
successful=true
|
||||
echo "PostgreSQL + Cassandra + worker deployed; public API port=$DEPLOY_PORT"
|
||||
echo 'Legacy SQLite container remains stopped; its data volume is retained (no automatic data migration).'
|
||||
@@ -0,0 +1,20 @@
|
||||
"""Initialize the single-datacenter test cluster using the checked-in CQL schema."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from cassandra.cluster import Cluster
|
||||
|
||||
cluster = Cluster([os.getenv("CASSANDRA_HOST", "cassandra")], connect_timeout=10)
|
||||
try:
|
||||
session = cluster.connect()
|
||||
schema = Path("/app/deploy/cassandra.cql").read_text()
|
||||
schema = "\n".join(
|
||||
line for line in schema.splitlines() if not line.startswith("--")
|
||||
)
|
||||
for statement in schema.split(";"):
|
||||
if statement.strip():
|
||||
session.execute(statement, timeout=30)
|
||||
print("Cassandra test schema initialized")
|
||||
finally:
|
||||
cluster.shutdown()
|
||||
+20
-2
@@ -4,6 +4,7 @@ import argparse
|
||||
import http.cookiejar
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
@@ -12,7 +13,7 @@ from uuid import uuid4
|
||||
from dotenv import dotenv_values
|
||||
|
||||
|
||||
def check(base_url: str, service_token: str):
|
||||
def check(base_url: str, service_token: str, require_projection=False):
|
||||
jar = http.cookiejar.CookieJar()
|
||||
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar))
|
||||
|
||||
@@ -78,6 +79,20 @@ def check(base_url: str, service_token: str):
|
||||
assert history["items"][0]["id"] == event["event_id"]
|
||||
graph = api("/v1/identity/" + linked["master_id"] + "/graph", trusted=True)
|
||||
assert len(graph["identifiers"]) == 1
|
||||
if require_projection:
|
||||
storage = api("/v1/identity/storage", trusted=True)
|
||||
assert storage["graph_backend"] == "postgresql"
|
||||
assert storage["canonical_profile_format"] == "jsonb"
|
||||
assert storage["profile_backend"] == "cassandra"
|
||||
for _ in range(60):
|
||||
profile = api("/v1/users/" + linked["master_id"], trusted=True)
|
||||
if profile["projection_status"] == "current":
|
||||
break
|
||||
time.sleep(1)
|
||||
assert profile["projection_status"] == "current", (
|
||||
"Worker projection not current"
|
||||
)
|
||||
assert profile["attributes"]["smoke"] is True
|
||||
print(
|
||||
"HTTP smoke passed: routes, anonymous cookie, events"
|
||||
+ (", auth, stitching, profile and history" if service_token else "")
|
||||
@@ -92,6 +107,7 @@ def main():
|
||||
action="store_true",
|
||||
help="Do not read or send the service token",
|
||||
)
|
||||
parser.add_argument("--require-projection", action="store_true")
|
||||
args = parser.parse_args()
|
||||
token = ""
|
||||
if not args.public_only:
|
||||
@@ -103,7 +119,9 @@ def main():
|
||||
raise SystemExit(
|
||||
"Configure SERVICE_TOKEN or mount the deployment config volume"
|
||||
)
|
||||
check(args.base_url, token)
|
||||
if args.require_projection and args.public_only:
|
||||
parser.error("Projection checks require a service token")
|
||||
check(args.base_url, token, args.require_projection)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -10,7 +10,7 @@ from pathlib import Path
|
||||
|
||||
from dotenv import dotenv_values
|
||||
|
||||
SECRET_NAMES = ("IDENTITY_SECRET", "SERVICE_TOKEN")
|
||||
SECRET_NAMES = ("IDENTITY_SECRET", "SERVICE_TOKEN", "POSTGRES_PASSWORD")
|
||||
|
||||
|
||||
def bootstrap(path: Path):
|
||||
|
||||
@@ -21,7 +21,9 @@ pytestmark = pytest.mark.skipif(
|
||||
@pytest.fixture
|
||||
def components(tmp_path):
|
||||
settings = Settings(
|
||||
database_url="sqlite:///" + str(tmp_path / "graph.db"),
|
||||
database_url=os.getenv(
|
||||
"ADCLOUD_TEST_POSTGRES_URL", "sqlite:///" + str(tmp_path / "graph.db")
|
||||
),
|
||||
profile_backend="cassandra",
|
||||
tenant_id="test-" + str(uuid4()),
|
||||
identity_secret="test-cassandra-" + "x" * 48,
|
||||
|
||||
Reference in New Issue
Block a user