Explorar el Código

test(api-test): 配置按 server 分组,--server 选中对应账号/种子数据

- config.local.php 顶层 servers 按 alias 分组,run.php 解析出实际 server 后选对应块
- --server=alias 现在连同账号/密码/token/种子数据一起切换,不再只换 URL
- 完整 URL / API_TEST_BASE 反查 alias;命令行 > 环境变量 > servers[alias] > fixtures.php 默认
visuddhinanda hace 2 días
padre
commit
e38893f3be
Se han modificado 3 ficheros con 113 adiciones y 39 borrados
  1. 73 19
      api-test/config.local.example.php
  2. 16 17
      api-test/config.php
  3. 24 3
      api-test/run.php

+ 73 - 19
api-test/config.local.example.php

@@ -5,27 +5,81 @@
  *
  *   cp config.local.example.php config.local.php
  *
- * 优先级:命令行参数 > 环境变量(API_TEST_*)> 本文件 > config.php 的默认值。
+ * 切 server 两种方式(账号 / 种子数据都会跟着走):
+ *   1. 改 `$server` 默认值;
+ *   2. `php run.php --server=<alias>` 临时切,alias 见 config.php 的 servers 列表。
+ * 每个 server 一套账号 + 种子数据,集中列在 `$servers`,互不干扰。
+ * 优先级:命令行参数 > 环境变量(API_TEST_*)> 本文件 > config.php 默认值。
  * 不需要的键删掉或留 null 即可。
  */
 
-return [
-    // 默认服务器 alias(见 config.php 的 servers),或完整 base URL
-    'default_server' => 'local',
-
-    // 测试账号:调 POST /v3/sessions 换 token。也可以直接给 token(优先)
-    'username' => 'test161',
-    'password' => null,
-    'token' => null,
-
-    // 覆盖 fixtures.php 里的种子数据(换环境时用),只写要改的键
-    'fixtures' => [
-        // 讨论写用例挂的句子:必须在测试账号自己的 channel 里、由测试账号编辑过
-        // 'discussion_sentence' => 'ee5259be-5d1e-4408-a2a6-566a5773eef1',
-        // 'discussion_channel' => '1721b7fa-e471-46fd-b860-64b9e79d370e',
-        // agent 代写用例:归测试账号所有的 AI 模型 uid;不给则 SKIP
-        // 'agent_model' => null,
-        // email-certifications「已注册邮箱」用例;不给则 SKIP
-        // 'registered_email' => 'someone@example.com',
+$server = 'local';   // local | staging | next | prod | next-cn | prod-cn
+
+$servers = [
+    'local' => [
+        'username' => 'test',
+        'password' => null,
+        'token' => null,                       // 填了就直接用,跳过登录
+        'fixtures' => [
+            // 讨论写用例挂的句子:必须在测试账号自己的 channel 里、由测试账号编辑过
+            // 'discussion_sentence' => 'ee5259be-5d1e-4408-a2a6-566a5773eef1',
+            // discussion_sentence 所在的 channel(agent 授权的范围)
+            // 'discussion_channel' => '1721b7fa-e471-46fd-b860-64b9e79d370e',
+            // agent 代写用例:归测试账号所有的 AI 模型 uid;不给则 SKIP
+            // 'agent_model' => null,
+            // email-certifications「已注册邮箱」用例;不给则 SKIP
+            // 'registered_email' => 'someone@example.com',
+        ],
+    ],
+
+    'staging' => [
+        'username' => null,
+        'password' => null,
+        'token' => null,
+        'fixtures' => [
+            // 'discussion_sentence' => '',
+            // 'discussion_channel' => '',
+            // 'agent_model' => null,
+            // 'registered_email' => null,
+        ],
+    ],
+
+    'next' => [
+        'username' => null,
+        'password' => null,
+        'token' => null,
+        'fixtures' => [],
+    ],
+
+    'prod' => [
+        'username' => null,
+        'password' => null,
+        'token' => null,
+        'fixtures' => [],
     ],
+
+    'next-cn' => [
+        'username' => null,
+        'password' => null,
+        'token' => null,
+        'fixtures' => [],
+    ],
+
+    'prod-cn' => [
+        'username' => null,
+        'password' => null,
+        'token' => null,
+        'fixtures' => [],
+    ],
+];
+
+if (! isset($servers[$server])) {
+    throw new RuntimeException(
+        'config.local.php: 未知 server "' . $server . '",可选:' . implode(' / ', array_keys($servers))
+    );
+}
+
+return [
+    'default_server' => $server,
+    'servers' => $servers,
 ];

+ 16 - 17
api-test/config.php

@@ -6,24 +6,23 @@
  * 服务器切换的优先级(高 → 低):
  *   1. 命令行 `php run.php --server=<alias|完整 base URL>`
  *   2. 环境变量 `API_TEST_BASE`(完整 base URL,覆盖 alias)
- *   3. 本文件的 `default_server`
+ *   3. config.local.php 的 `default_server`
  *
  * alias 对齐 openapi/public/assets/protocol/main.yaml 里的 servers。
  * 第一个 `/api` 是同源地址,CLI 客户端用不到,故略。
  *
  * 本机的账号、默认服务器、种子数据覆盖写在 config.local.php(模板
- * config.local.example.php,已 gitignore)。
+ * config.local.example.php,已 gitignore)。config.local.php 两种写法都认:
+ *   - 新分组:顶层 `servers` 按 alias 分组,run.php 按实际 server 选对应那套(推荐)
+ *   - 旧扁平:顶层直接写 username / token / fixtures(一套,切环境要手动改)
  *
- * token(需登录的用例用)优先级:
+ * token(需登录的用例用)优先级(在 run.php 里按序套用):
  *   1. `php run.php --token=...`
  *   2. 环境变量 `API_TEST_TOKEN`
- *   3. config.local.php 的 `token`
- *   4. 都没有时,用下面的 username/password 调 `POST /v3/sessions` 现换一个 token
+ *   3. 实际 server 对应的 `servers[alias].token`
+ *   4. 都没有时,用 username/password 调 `POST /v3/sessions` 现换一个 token
  *
- * username/password(登录换 token 用)优先级:
- *   1. `php run.php --username=... --password=...`
- *   2. 环境变量 `API_TEST_USERNAME` / `API_TEST_PASSWORD`
- *   3. config.local.php
+ * username/password 同理:--username/--password > API_TEST_USERNAME/PASSWORD > servers[alias]
  *
  * 写操作(POST/DELETE)在**所有服务器上都真实执行**,包括 prod——会产生垃圾数据,
  * 由测试账号定期清理。
@@ -43,15 +42,15 @@ return [
     ],
     'default_server' => $local['default_server'] ?? 'local',
 
-    // bearer token;没有则 /v3/me/* 的“带 token”用例会 FAIL(不跳过)。
-    'token' => getenv('API_TEST_TOKEN') ?: ($local['token'] ?? null),
-
-    // 登录凭据:sign-in 用 username(也可以是 email)+ password。
-    'username' => getenv('API_TEST_USERNAME') ?: ($local['username'] ?? null),
-    'password' => getenv('API_TEST_PASSWORD') ?: ($local['password'] ?? null),
-
-    // 覆盖 fixtures.php 的种子数据
+    // 旧扁平结构兜底(新分组的 config.local.php 顶层没有这些键,留空即可)
+    'token' => $local['token'] ?? null,
+    'username' => $local['username'] ?? null,
+    'password' => $local['password'] ?? null,
     'fixtures' => $local['fixtures'] ?? [],
 
+    // 新分组结构:per-server 配置块(username/password/token/fixtures),
+    // run.php 解析出实际 server 后从这里选对应那套。
+    'local_servers' => $local['servers'] ?? null,
+
     'timeout' => 20,
 ];

+ 24 - 3
api-test/run.php

@@ -43,25 +43,46 @@ $servers = $config['servers'];
 $serverArg = is_string($opts['server'] ?? null) ? $opts['server'] : null;
 $envBase = getenv('API_TEST_BASE') ?: null;
 
+// 除了 base_url,还要记下对应的 alias($serverKey),好去 config.local.php 的
+// servers[alias] 里选账号 / token / 种子数据。完整 URL 会反查 alias。
+$serverKey = null;
 if ($envBase !== null) {
     $config['base_url'] = rtrim($envBase, '/');
+    $serverKey = array_search($config['base_url'], $servers, true) ?: null;
 } elseif ($serverArg !== null && isset($servers[$serverArg])) {
     $config['base_url'] = $servers[$serverArg];
+    $serverKey = $serverArg;
 } elseif ($serverArg !== null && preg_match('#^https?://#', $serverArg)) {
     $config['base_url'] = rtrim($serverArg, '/');
+    $serverKey = array_search($config['base_url'], $servers, true) ?: null;
 } elseif (isset($servers[$config['default_server']])) {
     $config['base_url'] = $servers[$config['default_server']];
+    $serverKey = $config['default_server'];
 } else {
     // config.local.php 的 default_server 也可以直接写完整 base URL
     $config['base_url'] = rtrim($config['default_server'], '/');
+    $serverKey = array_search($config['base_url'], $servers, true) ?: null;
 }
 
-// token 覆盖
+// 按实际 server 选 config.local.php 的 per-server 块(账号 / token / 种子数据跟着 server 走)
+$localServers = $config['local_servers'] ?? null;
+if (is_array($localServers) && $serverKey !== null && isset($localServers[$serverKey])) {
+    $block = $localServers[$serverKey];
+    $config['token'] = $block['token'] ?? $config['token'];
+    $config['username'] = $block['username'] ?? $config['username'];
+    $config['password'] = $block['password'] ?? $config['password'];
+    $config['fixtures'] = $block['fixtures'] ?? $config['fixtures'];
+}
+
+// 环境变量覆盖(高于 config.local.php,低于命令行)
+$config['token'] = getenv('API_TEST_TOKEN') ?: $config['token'];
+$config['username'] = getenv('API_TEST_USERNAME') ?: $config['username'];
+$config['password'] = getenv('API_TEST_PASSWORD') ?: $config['password'];
+
+// 命令行覆盖(最高优先级)
 if (is_string($opts['token'] ?? null) && $opts['token'] !== '') {
     $config['token'] = $opts['token'];
 }
-
-// username/password 覆盖
 if (is_string($opts['username'] ?? null) && $opts['username'] !== '') {
     $config['username'] = $opts['username'];
 }