fix(ui): 保留业务HTTP配置超时
build / build (push) Successful in 1m33s

F13 动态客户端原先把 TEST_WEKNORA_HTTP_TIMEOUT(默认 60s)强制截成 10s,
与 readiness 探针上限混用,导致上传/轮询等业务请求 10s 即超时。

现在显式正超时原样传入;未配置/非正值回落到 appconfig 的 60s 默认。
readiness 探测自己的 10s 上限不变。

新增本地 httptest:60s 配置下 11s 响应成功,显式 50ms 短超时仍生效;
端点/凭据/绑定动态解析回归保持通过。make verify 17 项通过。
This commit is contained in:
姚章浩
2026-11-05 12:03:42 +08:00
parent 5bf4aae30e
commit 7675e2cd88
3 changed files with 72 additions and 2 deletions
+9
View File
@@ -1,5 +1,14 @@
# Changelog
## 开发快照 0.1.358(业务 HTTP 超时不再被探针上限截断)
### Fixed
- **F13 超时回归**:`weknoraaccess.Client` 不再把显式配置的
`TEST_WEKNORA_HTTP_TIMEOUT`(默认 60s)强制截成 10s。只有未配置/非正值才回落到
与原 appconfig 一致的 60s;readiness 探测自身的 10s 上限保持独立。11s 本地响应在
60s 配置下可完成,显式 50ms 短超时仍生效。
## 开发快照 0.1.357(取件后自动解析、定位一次性请求、worker 有效配置)
### Fixed
+6 -2
View File
@@ -11,6 +11,10 @@ import (
var ErrNotConfigured = errors.New("WeKnora 有效端点/凭据未配置")
// defaultBusinessTimeout matches appconfig's TEST_WEKNORA_HTTP_TIMEOUT default.
// The readiness probe has its own 10s cap; it must not bound upload/poll calls.
const defaultBusinessTimeout = 60 * time.Second
// Client resolves the effective WeKnora target on every call. It deliberately
// owns no cached *weknora.Client: endpoint, key and binding are console-managed.
type Client struct {
@@ -28,8 +32,8 @@ func (client *Client) with(ctx context.Context, call func(*weknora.Client) error
return ErrNotConfigured
}
timeout := client.Timeout
if timeout <= 0 || timeout > 10*time.Second {
timeout = 10 * time.Second
if timeout <= 0 {
timeout = defaultBusinessTimeout
}
remote, err := weknora.NewClient(weknora.ClientConfig{
BaseURL: target.BaseURL,
+57
View File
@@ -51,3 +51,60 @@ func TestDynamicClientReResolvesEndpointKeyAndBinding(t *testing.T) {
t.Fatalf("rotated endpoint/key = %#v, %v", knowledge, err)
}
}
// F13 timeout regression: the configured business timeout (default 60s) must
// reach upload/poll clients unchanged; only a missing/zero value falls back.
// The readiness probe's 10s cap must not leak into business HTTP.
func TestConfiguredBusinessTimeoutIsNotCappedToProbeTimeout(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
select {
case <-time.After(11 * time.Second):
_, _ = w.Write([]byte(`{"success":true,"data":{"id":"slow-but-valid"}}`))
case <-r.Context().Done():
}
}))
defer server.Close()
client := &Client{
Resolver: readiness.Resolver{
Endpoint: func(context.Context) string { return server.URL },
ReadKey: func() (string, error) { return "isolated-test-key", nil },
DefaultKB: "isolated-test-kb",
},
Timeout: 60 * time.Second,
}
started := time.Now()
item, err := client.GetKnowledge(context.Background(), "slow-but-valid")
if err != nil {
t.Fatalf("60s business timeout rejected 11s response after %s: %v", time.Since(started), err)
}
if item.ID != "slow-but-valid" {
t.Fatalf("wrong response identity: %q", item.ID)
}
}
// An explicit short timeout is still honoured for callers that need it.
func TestExplicitShortBusinessTimeoutStillApplies(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
select {
case <-time.After(300 * time.Millisecond):
_, _ = w.Write([]byte(`{"success":true,"data":{"id":"too-late"}}`))
case <-r.Context().Done():
}
}))
defer server.Close()
client := &Client{
Resolver: readiness.Resolver{
Endpoint: func(context.Context) string { return server.URL },
ReadKey: func() (string, error) { return "isolated-test-key", nil },
DefaultKB: "isolated-test-kb",
},
Timeout: 50 * time.Millisecond,
}
started := time.Now()
if _, err := client.GetKnowledge(context.Background(), "too-late"); err == nil {
t.Fatal("explicit 50ms timeout did not bound the call")
}
if elapsed := time.Since(started); elapsed > 2*time.Second {
t.Fatalf("explicit short timeout took %s", elapsed)
}
}