|
1 | | -import { existsSync, readFileSync, writeFileSync } from "fs"; |
| 1 | +import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs"; |
2 | 2 | import http from "node:http"; |
3 | 3 | import type { AddressInfo } from "node:net"; |
| 4 | +import { tmpdir } from "os"; |
4 | 5 | import { join } from "path"; |
5 | 6 | import { describe, expect, test } from "vite-plus/test"; |
6 | 7 | import { |
7 | 8 | isDashScopeE2EReady, |
| 9 | + isOpenApiE2EReady, |
8 | 10 | makeE2eOutputDir, |
9 | 11 | parseStdoutJson, |
10 | 12 | runCommandE2e, |
@@ -47,9 +49,7 @@ async function startValidationServer(statusCode = 200): Promise<ValidationServer |
47 | 49 | }; |
48 | 50 | } |
49 | 51 |
|
50 | | -/** |
51 | | - * Auth 相关 E2E:只验证 CLI 进程能正常解析参数并退出。 |
52 | | - */ |
| 52 | +/** Auth E2E:本地参数/持久化契约默认执行;真实鉴权请求按对应 readiness gate 执行。 */ |
53 | 53 |
|
54 | 54 | describe("e2e: auth", () => { |
55 | 55 | test("auth login --help 正常退出", async () => { |
@@ -116,6 +116,35 @@ describe("e2e: auth", () => { |
116 | 116 | expect(stderr).toMatch(/Provide --access-key-id and --access-key-secret with --open-api/); |
117 | 117 | }); |
118 | 118 |
|
| 119 | + test("auth login --open-api --dry-run 使用 placeholder 时不请求服务端、不写配置", async () => { |
| 120 | + const configDir = mkdtempSync(join(tmpdir(), "bl-auth-openapi-dry-run-")); |
| 121 | + try { |
| 122 | + const { stdout, stderr, exitCode } = await runCommandE2e( |
| 123 | + AUTH_ROUTES, |
| 124 | + [ |
| 125 | + "auth", |
| 126 | + "login", |
| 127 | + "--open-api", |
| 128 | + "--access-key-id", |
| 129 | + "LTAI-e2e-placeholder", |
| 130 | + "--access-key-secret", |
| 131 | + "secret-e2e-placeholder", |
| 132 | + "--dry-run", |
| 133 | + ], |
| 134 | + { |
| 135 | + BAILIAN_CONFIG_DIR: configDir, |
| 136 | + ALIBABA_CLOUD_ACCESS_KEY_ID: "", |
| 137 | + ALIBABA_CLOUD_ACCESS_KEY_SECRET: "", |
| 138 | + }, |
| 139 | + ); |
| 140 | + expect(exitCode, stderr).toBe(0); |
| 141 | + expect(stdout).toContain("Would save OpenAPI AK/SK credentials"); |
| 142 | + expect(existsSync(join(configDir, "config.json"))).toBe(false); |
| 143 | + } finally { |
| 144 | + rmSync(configDir, { recursive: true, force: true }); |
| 145 | + } |
| 146 | + }); |
| 147 | + |
119 | 148 | test("auth logout --help 正常退出", async () => { |
120 | 149 | const { stderr, exitCode } = await runCommandE2e(AUTH_ROUTES, ["auth", "logout", "--help"]); |
121 | 150 | expect(exitCode, stderr).toBe(0); |
@@ -458,57 +487,73 @@ describe("e2e: auth", () => { |
458 | 487 | expect(denied.stderr).toMatch(/Unknown flag.*--access-key-id/); |
459 | 488 | }); |
460 | 489 |
|
461 | | - test("auth login --open-api 持久化 OpenAPI AK/SK 并支持单独 logout", async () => { |
462 | | - const configDir = makeE2eOutputDir("auth-openapi-login"); |
463 | | - const env = { |
464 | | - BAILIAN_CONFIG_DIR: configDir, |
465 | | - ALIBABA_CLOUD_ACCESS_KEY_ID: "", |
466 | | - ALIBABA_CLOUD_ACCESS_KEY_SECRET: "", |
467 | | - }; |
468 | | - |
469 | | - const login = await runCommandE2e( |
470 | | - AUTH_ROUTES, |
471 | | - [ |
472 | | - "auth", |
473 | | - "login", |
474 | | - "--open-api", |
475 | | - "--access-key-id", |
476 | | - "LTAI-e2e-login-placeholder", |
477 | | - "--access-key-secret", |
478 | | - "secret-e2e-login-placeholder", |
479 | | - ], |
480 | | - env, |
481 | | - ); |
482 | | - expect(login.exitCode, login.stderr).toBe(0); |
483 | | - expect(login.stderr).toMatch(/OpenAPI credentials saved/); |
484 | | - |
485 | | - const config = JSON.parse(readFileSync(join(configDir, "config.json"), "utf8")) as Record< |
486 | | - string, |
487 | | - unknown |
488 | | - >; |
489 | | - expect(config.access_key_id).toBe("LTAI-e2e-login-placeholder"); |
490 | | - expect(config.access_key_secret).toBe("secret-e2e-login-placeholder"); |
491 | | - expect(config.openapi_access_key_id).toBeUndefined(); |
492 | | - expect(config.openapi_access_key_secret).toBeUndefined(); |
493 | | - |
494 | | - const status = await runCommandE2e(AUTH_ROUTES, ["auth", "status", "--output", "json"], env); |
495 | | - expect(status.exitCode, status.stderr).toBe(0); |
496 | | - const data = parseStdoutJson<{ |
497 | | - authenticated?: boolean; |
498 | | - openapi?: { source?: string; access_key_id?: string; access_key_secret?: string }; |
499 | | - }>(status.stdout); |
500 | | - expect(data.authenticated).toBe(true); |
501 | | - expect(data.openapi?.source).toBe("config"); |
502 | | - expect(data.openapi?.access_key_id).not.toBe("LTAI-e2e-login-placeholder"); |
503 | | - expect(data.openapi?.access_key_secret).not.toBe("secret-e2e-login-placeholder"); |
504 | | - |
505 | | - const logout = await runCommandE2e(AUTH_ROUTES, ["auth", "logout", "--open-api"], env); |
506 | | - expect(logout.exitCode, logout.stderr).toBe(0); |
507 | | - expect(logout.stderr).toMatch(/Cleared access_key_id/); |
508 | | - |
509 | | - const after = await runCommandE2e(AUTH_ROUTES, ["auth", "status", "--output", "json"], env); |
510 | | - expect(after.exitCode, after.stderr).toBe(0); |
511 | | - const afterData = parseStdoutJson<{ authenticated?: boolean; openapi?: unknown }>(after.stdout); |
512 | | - expect(afterData.openapi).toBeUndefined(); |
513 | | - }); |
| 490 | + test.skipIf(!isOpenApiE2EReady())( |
| 491 | + "auth login --open-api 使用环境中的真实 AK/SK,持久化后支持单独 logout", |
| 492 | + async () => { |
| 493 | + const accessKeyId = process.env.ALIBABA_CLOUD_ACCESS_KEY_ID!.trim(); |
| 494 | + const accessKeySecret = process.env.ALIBABA_CLOUD_ACCESS_KEY_SECRET!.trim(); |
| 495 | + const configDir = mkdtempSync(join(tmpdir(), "bl-auth-openapi-login-")); |
| 496 | + const env = { |
| 497 | + BAILIAN_CONFIG_DIR: configDir, |
| 498 | + ALIBABA_CLOUD_ACCESS_KEY_ID: "", |
| 499 | + ALIBABA_CLOUD_ACCESS_KEY_SECRET: "", |
| 500 | + }; |
| 501 | + |
| 502 | + try { |
| 503 | + const login = await runCommandE2e( |
| 504 | + AUTH_ROUTES, |
| 505 | + [ |
| 506 | + "auth", |
| 507 | + "login", |
| 508 | + "--open-api", |
| 509 | + "--access-key-id", |
| 510 | + accessKeyId, |
| 511 | + "--access-key-secret", |
| 512 | + accessKeySecret, |
| 513 | + ], |
| 514 | + env, |
| 515 | + ); |
| 516 | + expect(login.exitCode, login.stderr).toBe(0); |
| 517 | + expect(login.stderr).toMatch(/OpenAPI credentials saved/); |
| 518 | + |
| 519 | + const config = JSON.parse(readFileSync(join(configDir, "config.json"), "utf8")) as Record< |
| 520 | + string, |
| 521 | + unknown |
| 522 | + >; |
| 523 | + // 只断言布尔结果,避免失败 diff 把真实凭证打印到测试日志。 |
| 524 | + expect(config.access_key_id === accessKeyId).toBe(true); |
| 525 | + expect(config.access_key_secret === accessKeySecret).toBe(true); |
| 526 | + expect(config.openapi_access_key_id).toBeUndefined(); |
| 527 | + expect(config.openapi_access_key_secret).toBeUndefined(); |
| 528 | + |
| 529 | + const status = await runCommandE2e( |
| 530 | + AUTH_ROUTES, |
| 531 | + ["auth", "status", "--output", "json"], |
| 532 | + env, |
| 533 | + ); |
| 534 | + expect(status.exitCode, status.stderr).toBe(0); |
| 535 | + const data = parseStdoutJson<{ |
| 536 | + authenticated?: boolean; |
| 537 | + openapi?: { source?: string; access_key_id?: string; access_key_secret?: string }; |
| 538 | + }>(status.stdout); |
| 539 | + expect(data.authenticated).toBe(true); |
| 540 | + expect(data.openapi?.source).toBe("config"); |
| 541 | + expect(data.openapi?.access_key_id === accessKeyId).toBe(false); |
| 542 | + expect(data.openapi?.access_key_secret === accessKeySecret).toBe(false); |
| 543 | + |
| 544 | + const logout = await runCommandE2e(AUTH_ROUTES, ["auth", "logout", "--open-api"], env); |
| 545 | + expect(logout.exitCode, logout.stderr).toBe(0); |
| 546 | + expect(logout.stderr).toMatch(/Cleared access_key_id/); |
| 547 | + |
| 548 | + const after = await runCommandE2e(AUTH_ROUTES, ["auth", "status", "--output", "json"], env); |
| 549 | + expect(after.exitCode, after.stderr).toBe(0); |
| 550 | + const afterData = parseStdoutJson<{ authenticated?: boolean; openapi?: unknown }>( |
| 551 | + after.stdout, |
| 552 | + ); |
| 553 | + expect(afterData.openapi).toBeUndefined(); |
| 554 | + } finally { |
| 555 | + rmSync(configDir, { recursive: true, force: true }); |
| 556 | + } |
| 557 | + }, |
| 558 | + ); |
514 | 559 | }); |
0 commit comments