docs: 文档/配置/测试同步 NAS :8124 生产现状,清除 Node.js 与甲骨文残留

背景:文档停在 Node.js 时代或甲骨文 8123 部署,与生产(NAS :8124 + Flask +
auth-hub + ai-gateway)严重脱节,曾导致凭旧记忆误判'无线上环境'。

- CLAUDE.md 重写:技术栈/结构/命令/部署事实/关键坑(F7 button、UTC 日期、
  429 退避以 DB 为准、迁移幂等、AI 生成耗时)
- docs/ARCHITECTURE.md 重写为 Flask 蓝图+services+可插拔数据层 + NAS 部署
- docs/DEVELOPMENT.md 重写为 Flask/CRA 开发指南 + push.sh 部署流程
- docs/REQUIREMENTS.md:部署条目改 NAS 8124;补 auth-hub/AI 教练/新修复
- docs/AUTH_HUB_INTEGRATION.md 新增(补 .env.example 悬空引用)
- README.md:技术栈/DB/auth-hub/API 清单/部署节修正
- backend/config.py 与 .env.example:AUTH_HUB_REDIRECT_URI 默认 8123→8124,
  MariaDB 注释 Oracle→NAS
- tests:GatewayCourtesy 并发测试对齐 MAX_CONCURRENT(AI_JOB_CONCURRENCY=2);
  conftest 禁用 create_app 后台队列线程,修整库测试 flaky(585 passed)
This commit is contained in:
ericwyuan
2026-09-02 19:34:32 +08:00
parent 68363957ec
commit 2d9a2be185
12 changed files with 609 additions and 496 deletions

View File

@@ -52,6 +52,19 @@ def _isolate_ai_env(monkeypatch):
monkeypatch.delenv(var, raising=False)
@pytest.fixture(autouse=True)
def _no_ai_jobs_background_thread(monkeypatch):
"""create_app() starts a daemon queue-consumer thread that outlives the
test that launched it and races the *next* test for jobs on that test's
fresh database (with whatever _runner the previous stub left behind) —
which made queue tests flaky. Tests drive the queue themselves through
jobs.run_once(), so the thread is disabled here.
"""
from services import jobs
monkeypatch.setattr(jobs, "ENABLED", False)
@pytest.fixture(autouse=True)
def _clear_garmin_client_cache():
"""Drop cached Garmin sessions between tests.

View File

@@ -1076,15 +1076,20 @@ class TestHighlightsReadAlone:
class TestGatewayCourtesy:
"""The gateway runs one worker with four threads and is shared with two
other projects. This consumer must not be able to saturate it."""
"""The gateway is shared with two other projects, so this consumer must
not saturate it. The cap is MAX_CONCURRENT (AI_JOB_CONCURRENCY; default 2
since 2026-09-01) and is counted across the whole deployment, not per
Gunicorn worker."""
def test_only_one_job_runs_at_a_time_across_the_deployment(self, db, user):
jobs.enqueue(user["id"], "health", "a")
jobs.enqueue(user["id"], "sleep", "b")
assert jobs._claim_next() is not None
assert jobs._claim_next() is None, \
"a second Gunicorn worker must not start a second gateway call"
def test_concurrency_is_capped_at_MAX_CONCURRENT(self, db, user):
"""More than the cap may queue, but only MAX_CONCURRENT run at once."""
limit = jobs.MAX_CONCURRENT
for i in range(limit + 2):
jobs.enqueue(user["id"], "health", f"job-{i}")
claimed = [jobs._claim_next() for _ in range(limit + 1)]
assert sum(1 for c in claimed if c is not None) == limit
assert claimed[limit] is None, \
"a second claim past MAX_CONCURRENT must not start another gateway call"
def test_a_finished_job_frees_the_slot(self, db, user):
jobs.enqueue(user["id"], "health", "a")