package com.nanri.aiimage.config; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * task-171:图片下载客户端配置接入契约(plan 10,诚实模式)。 * * 现网真实基线(已核实,对应 task-166 契约表"图片下载"行): * - SimilarAsinImageEmbedder 用自建 OkHttpClient(非共享 JDK 池):connect/read/write = * aiimage.similar-asin.image-download-timeout-seconds(默认 5s),call = 2×download(10s); * - 整批预取预算 aiimage.similar-asin.image-prefetch-timeout-seconds(默认 1800s); * - 无显式次数重试(依赖 OkHttp retryOnConnectionFailure + 上游批次重提),图片缓存幂等。 * * resolver 语义:模块级现有配置优先(保证接入切点行为不变),命名空间 aiimage.http-client.* * 兜底;不改动 SimilarAsinImageEmbedder 调用点,仅提供稳定访问面。 */ class ImageDownloadHttpConfigResolverTest { private HttpClientProperties http; private SimilarAsinProperties similarAsin; private ImageDownloadHttpConfigResolver resolver; @BeforeEach void setUp() { http = new HttpClientProperties(); similarAsin = new SimilarAsinProperties(); resolver = new ImageDownloadHttpConfigResolver(http, similarAsin); } @Test void defaultsMatchCurrentEmbedder() { // 模块默认 imageDownloadTimeoutSeconds=5 → OkHttp connect/read 5s、call 10s assertEquals(5_000L, resolver.readTimeoutMillis()); assertEquals(5_000L, resolver.connectTimeoutMillis()); assertEquals(10_000L, resolver.callTimeoutMillis()); } @Test void prefetchBudgetFromModule() { // 模块默认 imagePrefetchTimeoutSeconds=1800 → 1800s assertEquals(1_800_000L, resolver.prefetchTimeoutMillis()); } @Test void moduleWinsOverNamespace() { // 模块级现有配置优先:命名空间 read 调大不影响图片下载 read(行为不变) http.setReadTimeoutMillis(120_000); assertEquals(5_000L, resolver.readTimeoutMillis()); } @Test void downloadOverrideTakesEffect() { similarAsin.setImageDownloadTimeoutSeconds(8); assertEquals(8_000L, resolver.readTimeoutMillis()); assertEquals(8_000L, resolver.connectTimeoutMillis()); assertEquals(16_000L, resolver.callTimeoutMillis(), "call = 2×download"); } @Test void fallsBackToNamespaceWhenModuleUnset() { // 模块未配置(<=0) → 命名空间(默认 connect 10s/read 60s/call 90s) similarAsin.setImageDownloadTimeoutSeconds(0); similarAsin.setImagePrefetchTimeoutSeconds(0); assertEquals(10_000L, resolver.connectTimeoutMillis()); assertEquals(60_000L, resolver.readTimeoutMillis()); assertEquals(90_000L, resolver.callTimeoutMillis()); assertEquals(60_000L, resolver.prefetchTimeoutMillis()); } @Test void retryFromNamespaceClamped() { // 无显式次数重试是现状;次数来自命名空间,供未来接入切点 assertEquals(3, resolver.maxRetries()); http.setMaxRetries(99); assertEquals(10, resolver.maxRetries(), "命名空间重试钳制到 10"); assertEquals(500L, resolver.baseRetryDelayMillis()); } @Test void invalidNamespaceTimeoutsClamped() { similarAsin.setImageDownloadTimeoutSeconds(0); similarAsin.setImagePrefetchTimeoutSeconds(0); http.setConnectTimeoutMillis(-1); assertEquals(1_000L, resolver.connectTimeoutMillis()); http.setReadTimeoutMillis(999_999_999L); assertEquals(3_600_000L, resolver.readTimeoutMillis()); } @Test void resolverWiredAsStableAccessSurface() { assertTrue(resolver.connectTimeoutMillis() > 0); assertTrue(resolver.readTimeoutMillis() > 0); assertTrue(resolver.callTimeoutMillis() > 0); assertTrue(resolver.maxRetries() >= 0); } }