补充 DNS 验证等待测试
This commit is contained in:
parent
ddbf3b2d29
commit
519a538755
@ -55,10 +55,10 @@ func TestAdminDNSTestReturnsVerificationResult(t *testing.T) {
|
||||
t.Fatalf("DNS 在线测试失败:%d %s", response.Code, response.Body.String())
|
||||
}
|
||||
var payload struct {
|
||||
Verified bool `json:"verified"`
|
||||
CredentialRef string `json:"credential_ref"`
|
||||
VerificationToken string `json:"verification_token"`
|
||||
TestReport setup.DNSVerificationReport `json:"test_report"`
|
||||
Verified bool `json:"verified"`
|
||||
CredentialRef string `json:"credential_ref"`
|
||||
VerificationToken string `json:"verification_token"`
|
||||
TestReport setup.DNSVerificationReport `json:"test_report"`
|
||||
VerificationTimeoutSecs int `json:"verification_timeout_secs"`
|
||||
}
|
||||
if err := json.Unmarshal(response.Body.Bytes(), &payload); err != nil {
|
||||
@ -80,4 +80,7 @@ func TestAdminDNSTestRejectsUnavailableService(t *testing.T) {
|
||||
if response.Code != http.StatusServiceUnavailable {
|
||||
t.Fatalf("DNS 服务未初始化时应返回 503,实际为 %d:%s", response.Code, response.Body.String())
|
||||
}
|
||||
if !strings.Contains(response.Body.String(), `"verification_timeout_secs":930`) {
|
||||
t.Fatalf("DNS 服务未初始化响应应包含当前最大等待时间:%s", response.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,12 +159,12 @@ func (s *Server) testAdminDNS(c *gin.Context) {
|
||||
func (s *Server) testDNS(c *gin.Context) {
|
||||
verificationTimeoutSecs := int(s.dnsVerificationTimeout().Seconds())
|
||||
if s.setupDNS == nil {
|
||||
s.writeError(c, http.StatusServiceUnavailable, "DNS 在线验证服务尚未初始化", nil)
|
||||
s.writeDNSTestError(c, http.StatusServiceUnavailable, "DNS 在线验证服务尚未初始化", nil, verificationTimeoutSecs)
|
||||
return
|
||||
}
|
||||
var request dnsTestRequest
|
||||
if err := bindJSON(c, &request, setupRequestLimit); err != nil {
|
||||
s.writeError(c, http.StatusBadRequest, "DNS 测试请求参数无效", err)
|
||||
s.writeDNSTestError(c, http.StatusBadRequest, "DNS 测试请求参数无效", err, verificationTimeoutSecs)
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), s.dnsVerificationTimeout())
|
||||
@ -206,6 +206,16 @@ func (s *Server) testDNS(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) writeDNSTestError(c *gin.Context, status int, message string, err error, verificationTimeoutSecs int) {
|
||||
if s.logger != nil {
|
||||
s.logger.Error(message, "error", err)
|
||||
}
|
||||
c.JSON(status, gin.H{
|
||||
"error": message,
|
||||
"verification_timeout_secs": verificationTimeoutSecs,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) dnsVerificationTimeout() time.Duration {
|
||||
s.configMu.RLock()
|
||||
cfg := s.config
|
||||
|
||||
@ -527,6 +527,9 @@ func TestSetupDNSTestRejectsRemoteAndOversizedRequests(t *testing.T) {
|
||||
if oversizedResponse.Code != http.StatusBadRequest {
|
||||
t.Fatalf("超大 DNS 测试请求应被拒绝:%d %s", oversizedResponse.Code, oversizedResponse.Body.String())
|
||||
}
|
||||
if !strings.Contains(oversizedResponse.Body.String(), `"verification_timeout_secs":930`) {
|
||||
t.Fatalf("DNS 参数错误响应应包含当前最大等待时间:%s", oversizedResponse.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDNSTestReturnsNotImplementedForUnavailableProvider(t *testing.T) {
|
||||
@ -544,6 +547,9 @@ func TestSetupDNSTestReturnsNotImplementedForUnavailableProvider(t *testing.T) {
|
||||
if strings.Contains(response.Body.String(), "access_key_secret") || strings.Contains(response.Body.String(), "secret") {
|
||||
t.Fatal("未开放 Provider 响应不应包含提交的凭据")
|
||||
}
|
||||
if !strings.Contains(response.Body.String(), `"verification_timeout_secs":930`) {
|
||||
t.Fatalf("未开放 Provider 响应应包含当前最大等待时间:%s", response.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupValidationFailureDoesNotConsumeDNSToken(t *testing.T) {
|
||||
|
||||
@ -30,6 +30,7 @@ export function verifiedResponse(token = "test-token") {
|
||||
export async function mountComposable(t, factory, fetcher) {
|
||||
const intervals = new Map();
|
||||
let nextIntervalID = 1;
|
||||
let currentTime = Date.now();
|
||||
const originals = new Map();
|
||||
for (const [key, value] of Object.entries({
|
||||
window: {
|
||||
@ -49,6 +50,7 @@ export async function mountComposable(t, factory, fetcher) {
|
||||
originals.set(key, Object.getOwnPropertyDescriptor(globalThis, key));
|
||||
Object.defineProperty(globalThis, key, { configurable: true, writable: true, value });
|
||||
}
|
||||
t.mock.method(Date, "now", () => currentTime);
|
||||
t.mock.method(globalThis, "fetch", fetcher);
|
||||
const renderer = createRenderer({
|
||||
createComment: () => ({}),
|
||||
@ -85,5 +87,8 @@ export async function mountComposable(t, factory, fetcher) {
|
||||
runIntervals() {
|
||||
for (const callback of [...intervals.values()]) callback();
|
||||
},
|
||||
advanceTime(milliseconds) {
|
||||
currentTime += milliseconds;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -5,15 +5,16 @@ import { useDashboard } from "./Dashboard.js";
|
||||
import { aliyunProvider, deferred, mountComposable, verifiedResponse } from "../../test-support/mount.js";
|
||||
|
||||
async function setup(t, request) {
|
||||
const { state } = await mountComposable(t, useDashboard, async (path) => {
|
||||
const mounted = await mountComposable(t, useDashboard, async (path) => {
|
||||
if (path === "/api/v1/dns/test") return request();
|
||||
if (path.endsWith("/dns/providers")) return Response.json({ items: [aliyunProvider] });
|
||||
if (path.endsWith("/settings")) return Response.json({ dns: { provider: "aliyun", credential_configured: true, verification_state: "verified" } });
|
||||
return Response.json({ items: [] });
|
||||
});
|
||||
const { state } = mounted;
|
||||
state.openSettings();
|
||||
await nextTick();
|
||||
return state;
|
||||
return mounted;
|
||||
}
|
||||
|
||||
async function fill(state) {
|
||||
@ -23,7 +24,7 @@ async function fill(state) {
|
||||
}
|
||||
|
||||
test("已保存凭据不变可保存,输入新凭据必须重新验证", async (t) => {
|
||||
const state = await setup(t, () => verifiedResponse());
|
||||
const { state } = await setup(t, () => verifiedResponse());
|
||||
assert.equal(state.dnsReadyToSave.value, true);
|
||||
await fill(state);
|
||||
assert.equal(state.dnsReadyToSave.value, false);
|
||||
@ -35,7 +36,7 @@ test("已保存凭据不变可保存,输入新凭据必须重新验证", async
|
||||
|
||||
test("管理设置不接受旧输入的在途 DNS 响应", async (t) => {
|
||||
const pending = deferred();
|
||||
const state = await setup(t, () => pending.promise);
|
||||
const { state } = await setup(t, () => pending.promise);
|
||||
await fill(state);
|
||||
const task = state.testDNS();
|
||||
state.settingsForm.value.dns.test_domain = "other.test";
|
||||
@ -47,14 +48,14 @@ test("管理设置不接受旧输入的在途 DNS 响应", async (t) => {
|
||||
});
|
||||
|
||||
test("管理设置拒绝过期验证令牌", async (t) => {
|
||||
const state = await setup(t, () => Response.json({ credential_ref: "secret://test/only", verification_token: "expired", expires_at: "2000-01-01T00:00:00Z" }));
|
||||
const { state } = await setup(t, () => Response.json({ credential_ref: "secret://test/only", verification_token: "expired", expires_at: "2000-01-01T00:00:00Z" }));
|
||||
await fill(state);
|
||||
await state.testDNS();
|
||||
assert.equal(state.dnsReadyToSave.value, false);
|
||||
});
|
||||
|
||||
test("管理设置变更托管区不能复用验证令牌", async (t) => {
|
||||
const state = await setup(t, () => verifiedResponse());
|
||||
const { state } = await setup(t, () => verifiedResponse());
|
||||
await fill(state);
|
||||
state.settingsForm.value.dns.credentials.managed_zones = "example.test";
|
||||
state.settingsForm.value.dns.credentials.allowed_domains = "example.test";
|
||||
@ -67,7 +68,7 @@ test("管理设置变更托管区不能复用验证令牌", async (t) => {
|
||||
|
||||
test("关闭设置后不显示旧 DNS 请求的成功提示", async (t) => {
|
||||
const pending = deferred();
|
||||
const state = await setup(t, () => pending.promise);
|
||||
const { state } = await setup(t, () => pending.promise);
|
||||
await fill(state);
|
||||
const task = state.testDNS();
|
||||
state.closeSettings();
|
||||
@ -81,7 +82,7 @@ test("管理设置新请求成功后旧失败不能删除令牌", async (t) => {
|
||||
const first = deferred();
|
||||
const second = deferred();
|
||||
let count = 0;
|
||||
const state = await setup(t, () => (++count === 1 ? first.promise : second.promise));
|
||||
const { state } = await setup(t, () => (++count === 1 ? first.promise : second.promise));
|
||||
await fill(state);
|
||||
const oldTask = state.testDNS();
|
||||
const newTask = state.testDNS();
|
||||
@ -93,3 +94,52 @@ test("管理设置新请求成功后旧失败不能删除令牌", async (t) => {
|
||||
assert.equal(state.dnsTestError.value, "");
|
||||
assert.equal(state.dnsReadyToSave.value, true);
|
||||
});
|
||||
|
||||
test("管理设置 DNS 验证显示等待进度,并在结束后停止计时", async (t) => {
|
||||
const pending = deferred();
|
||||
const { state, runIntervals, advanceTime } = await setup(t, () => pending.promise);
|
||||
await fill(state);
|
||||
const task = state.testDNS();
|
||||
|
||||
assert.equal(state.dnsTesting.value, true);
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 930);
|
||||
advanceTime(3200);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 3);
|
||||
|
||||
pending.resolve(Response.json({
|
||||
verified: true,
|
||||
credential_ref: "secret://test/only",
|
||||
verification_token: "test-token",
|
||||
expires_at: new Date(Date.now() + 60000).toISOString(),
|
||||
verification_timeout_secs: 31,
|
||||
}));
|
||||
await task;
|
||||
assert.equal(state.dnsTesting.value, false);
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 31);
|
||||
advanceTime(1000);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
});
|
||||
|
||||
test("关闭管理设置会停止 DNS 等待计时器", async (t) => {
|
||||
const pending = deferred();
|
||||
const { state, runIntervals, advanceTime } = await setup(t, () => pending.promise);
|
||||
await fill(state);
|
||||
const task = state.testDNS();
|
||||
advanceTime(1000);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 1);
|
||||
|
||||
state.closeSettings();
|
||||
assert.equal(state.dnsTesting.value, false);
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
advanceTime(1000);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
|
||||
pending.resolve(verifiedResponse());
|
||||
await task;
|
||||
});
|
||||
|
||||
@ -18,14 +18,45 @@ test("已有初始化服务商选择保持兼容", async (t) => {
|
||||
});
|
||||
|
||||
async function setup(t, request) {
|
||||
const { state, unmount } = await mountComposable(t, () => useSetupWizard({ status: { dns_provider: "aliyun" } }, () => {}),
|
||||
const mounted = await mountComposable(t, () => useSetupWizard({ status: { dns_provider: "aliyun" } }, () => {}),
|
||||
async (path) => path.endsWith("/providers") ? Response.json({ items: [aliyunProvider] }) : request());
|
||||
const { state } = mounted;
|
||||
state.dnsCredentials.value = { access_key_id: "fake-key" };
|
||||
state.dnsTestDomain.value = "example.test";
|
||||
await nextTick();
|
||||
return { state, unmount };
|
||||
return mounted;
|
||||
}
|
||||
|
||||
test("初始化 DNS 验证显示等待进度,并采用服务端返回的最长等待时间", async (t) => {
|
||||
const pending = deferred();
|
||||
const { state, runIntervals, advanceTime } = await setup(t, () => pending.promise);
|
||||
const task = state.testDNS();
|
||||
|
||||
assert.equal(state.dnsTesting.value, true);
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 930);
|
||||
|
||||
advanceTime(2500);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 2);
|
||||
|
||||
pending.resolve(Response.json({
|
||||
verified: true,
|
||||
credential_ref: "secret://test/only",
|
||||
verification_token: "test-token",
|
||||
expires_at: new Date(Date.now() + 60000).toISOString(),
|
||||
verification_timeout_secs: 31,
|
||||
}));
|
||||
await task;
|
||||
assert.equal(state.dnsTesting.value, false);
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
assert.equal(state.dnsVerificationTimeoutSeconds.value, 31);
|
||||
|
||||
advanceTime(1000);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
});
|
||||
|
||||
test("初始化不接受输入修改前的在途 DNS 响应", async (t) => {
|
||||
const pending = deferred();
|
||||
const { state } = await setup(t, () => pending.promise);
|
||||
@ -61,6 +92,25 @@ test("初始化当前 DNS 成功有效,修改域名立即失效", async (t) =>
|
||||
assert.equal(state.dnsVerificationValid.value, false);
|
||||
});
|
||||
|
||||
test("初始化修改 DNS 输入后会停止等待计时器", async (t) => {
|
||||
const pending = deferred();
|
||||
const { state, runIntervals, advanceTime } = await setup(t, () => pending.promise);
|
||||
const task = state.testDNS();
|
||||
advanceTime(1000);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 1);
|
||||
|
||||
state.dnsTestDomain.value = "other.test";
|
||||
assert.equal(state.dnsTesting.value, false);
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
advanceTime(1000);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
|
||||
pending.resolve(verifiedResponse());
|
||||
await task;
|
||||
});
|
||||
|
||||
test("初始化修改显式授权范围立即作废旧验证", async (t) => {
|
||||
const { state } = await setup(t, () => verifiedResponse());
|
||||
state.dnsCredentials.value.managed_zones = "example.test";
|
||||
@ -72,11 +122,18 @@ test("初始化修改显式授权范围立即作废旧验证", async (t) => {
|
||||
assert.equal(state.dnsVerification.value, null);
|
||||
});
|
||||
|
||||
test("初始化卸载后不恢复旧 DNS 验证结果", async (t) => {
|
||||
test("初始化卸载后不恢复旧 DNS 验证结果或继续等待计时", async (t) => {
|
||||
const pending = deferred();
|
||||
const { state, unmount } = await setup(t, () => pending.promise);
|
||||
const { state, unmount, runIntervals, advanceTime } = await setup(t, () => pending.promise);
|
||||
const task = state.testDNS();
|
||||
advanceTime(1000);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 1);
|
||||
unmount();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
advanceTime(1000);
|
||||
runIntervals();
|
||||
assert.equal(state.dnsVerificationElapsedSeconds.value, 0);
|
||||
pending.resolve(verifiedResponse());
|
||||
await task;
|
||||
assert.equal(state.dnsVerification.value, null);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user